diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index e5520236d..458f85029 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -649,7 +649,6 @@ __all__ = [ 'FontEngine', 'FontSet', 'Geometry2d', - 'GlyphSymbolizer', 'Image', 'ImageView', 'Grid', diff --git a/bindings/python/mapnik_glyph_symbolizer.cpp b/bindings/python/mapnik_glyph_symbolizer.cpp deleted file mode 100644 index 70f01ac73..000000000 --- a/bindings/python/mapnik_glyph_symbolizer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include - -#include -#include "mapnik_enumeration.hpp" -#include - -using mapnik::glyph_symbolizer; -using mapnik::position; -using mapnik::enumeration_; -using mapnik::angle_mode_e; -using mapnik::AZIMUTH; -using mapnik::TRIGONOMETRIC; -using namespace boost::python; - -namespace { -using namespace boost::python; - -tuple get_displacement(const glyph_symbolizer& s) -{ - boost::tuple pos = s.get_displacement(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); -} - -void set_displacement(glyph_symbolizer & s, boost::python::tuple arg) -{ - s.set_displacement(extract(arg[0]),extract(arg[1])); -} - -} - -void export_glyph_symbolizer() -{ - enumeration_("angle_mode") - .value("AZIMUTH", AZIMUTH) - .value("TRIGONOMETRIC", TRIGONOMETRIC) - ; - - class_("GlyphSymbolizer", - init()) - .add_property("face_name", - make_function(&glyph_symbolizer::get_face_name, - return_value_policy()), - &glyph_symbolizer::set_face_name, - "Get/Set the name of the font face (eg:\"DejaVu Sans " - "Book\") which contains the glyph" - ) - .add_property("char", - &glyph_symbolizer::get_char, - &glyph_symbolizer::set_char, - "Get/Set the char expression. The char is the unicode " - "character indexing the glyph in the font referred by " - "face_name." - ) - .add_property("allow_overlap", - &glyph_symbolizer::get_allow_overlap, - &glyph_symbolizer::set_allow_overlap, - "Get/Set the flag which controls if glyphs should " - "overlap any symbols previously rendered" - ) - .add_property("avoid_edges", - &glyph_symbolizer::get_avoid_edges, - &glyph_symbolizer::set_avoid_edges, - "Get/Set the flag which controls if glyphs should be " - "partially drawn beside the edge of a tile." - ) - .add_property("displacement", - &get_displacement, - &set_displacement) - - .add_property("halo_fill", - make_function(&glyph_symbolizer::get_halo_fill, - return_value_policy()), - &glyph_symbolizer::set_halo_fill) - - .add_property("halo_radius", - &glyph_symbolizer::get_halo_radius, - &glyph_symbolizer::set_halo_radius) - - .add_property("size", - &glyph_symbolizer::get_size, - &glyph_symbolizer::set_size, - "Get/Set the size expression used to size the glyph." - ) - - .add_property("angle", - &glyph_symbolizer::get_angle, - &glyph_symbolizer::set_angle, - "Get/Set the angle expression used to rotate the glyph " - "along its center." - ) - .add_property("angle_mode", - &glyph_symbolizer::get_angle_mode, - &glyph_symbolizer::set_angle_mode, - "Get/Set the angle_mode property. This controls how the " - "angle is interpreted. Valid values are AZIMUTH and " - "TRIGONOMETRIC." - ) - .add_property("value", - &glyph_symbolizer::get_value, - &glyph_symbolizer::set_value, - "Get/set the value expression which will be used to " - "retrieve a a value for the colorizer to use to choose " - "a color." - ) - .add_property("color", - &glyph_symbolizer::get_color, - &glyph_symbolizer::set_color, - "Get/Set the color expression used to color the glyph. " - "(See also the 'colorizer' attribute)" - - ) - .add_property("colorizer", - &glyph_symbolizer::get_colorizer, - &glyph_symbolizer::set_colorizer, - "Get/Set the RasterColorizer used to color the glyph " - "depending on the 'value' expression (which must be " - "defined).\n" - "Only needed if no explicit color is provided" - ) - ; -} diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index af4d0c836..1f26e9a0b 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -60,13 +60,13 @@ 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(); void export_proj_transform(); void export_view_transform(); void export_raster_colorizer(); -void export_glyph_symbolizer(); void export_inmem_metawriter(); void export_label_collision_detector(); @@ -84,6 +84,7 @@ void export_label_collision_detector(); #include #include #include +#include #include "python_grid_utils.hpp" #include "mapnik_value_converter.hpp" #include "python_optional.hpp" @@ -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(); @@ -434,7 +436,6 @@ BOOST_PYTHON_MODULE(_mapnik) export_coord(); export_map(); export_raster_colorizer(); - export_glyph_symbolizer(); export_inmem_metawriter(); export_label_collision_detector(); diff --git a/bindings/python/mapnik_rule.cpp b/bindings/python/mapnik_rule.cpp index b6f3e4bc8..62de4e5f7 100644 --- a/bindings/python/mapnik_rule.cpp +++ b/bindings/python/mapnik_rule.cpp @@ -44,7 +44,6 @@ using mapnik::shield_symbolizer; using mapnik::text_symbolizer; using mapnik::building_symbolizer; using mapnik::markers_symbolizer; -using mapnik::glyph_symbolizer; using mapnik::symbolizer; using mapnik::to_expression_string; @@ -162,7 +161,6 @@ void export_rule() implicitly_convertible(); implicitly_convertible(); implicitly_convertible(); - implicitly_convertible(); implicitly_convertible(); class_("Symbolizers",init<>("TODO")) diff --git a/bindings/python/mapnik_shield_symbolizer.cpp b/bindings/python/mapnik_shield_symbolizer.cpp index 4bfe0fe61..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) @@ -64,17 +65,6 @@ void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg) t.set_displacement(extract(arg[0]),extract(arg[1])); } -tuple get_anchor(const shield_symbolizer& t) -{ - boost::tuple pos = t.get_anchor(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); -} - -void set_anchor(shield_symbolizer & t, boost::python::tuple arg) -{ - t.set_anchor(extract(arg[0]),extract(arg[1])); -} - const std::string get_filename(shield_symbolizer const& t) { return path_processor_type::to_string(*t.get_filename()); @@ -137,9 +127,6 @@ void export_shield_symbolizer() path_expression_ptr>("TODO") ) //.def_pickle(shield_symbolizer_pickle_suite()) - .add_property("anchor", - &get_anchor, - &set_anchor) .add_property("allow_overlap", &shield_symbolizer::get_allow_overlap, &shield_symbolizer::set_allow_overlap, @@ -237,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_symbolizer.cpp b/bindings/python/mapnik_symbolizer.cpp index bd9f1c185..a4c4c3512 100644 --- a/bindings/python/mapnik_symbolizer.cpp +++ b/bindings/python/mapnik_symbolizer.cpp @@ -39,7 +39,6 @@ using mapnik::shield_symbolizer; using mapnik::text_symbolizer; using mapnik::building_symbolizer; using mapnik::markers_symbolizer; -using mapnik::glyph_symbolizer; struct get_symbolizer_type : public boost::static_visitor { @@ -95,12 +94,6 @@ public: { return "markers"; } - - std::string operator () ( const glyph_symbolizer & /*sym*/ ) - { - return "glyph"; - } - }; std::string get_symbol_type(const symbolizer& symbol) @@ -160,11 +153,6 @@ const markers_symbolizer& markers_( const symbolizer& symbol ) return boost::get(symbol); } -const glyph_symbolizer& glyph_( const symbolizer& symbol ) -{ - return boost::get(symbol); -} - void export_symbolizer() { using namespace boost::python; @@ -202,10 +190,6 @@ void export_symbolizer() .def("markers",markers_, return_value_policy()) - - .def("glyph",glyph_, - return_value_policy()) - ; } 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 00c8d8e5d..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) @@ -48,17 +48,6 @@ void set_text_displacement(text_symbolizer & t, boost::python::tuple arg) t.set_displacement(extract(arg[0]),extract(arg[1])); } -tuple get_anchor(const text_symbolizer& t) -{ - position pos = t.get_anchor(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); -} - -void set_anchor(text_symbolizer & t, boost::python::tuple arg) -{ - t.set_anchor(extract(arg[0]),extract(arg[1])); -} - } struct text_symbolizer_pickle_suite : boost::python::pickle_suite @@ -76,7 +65,6 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite getstate(const text_symbolizer& t) { boost::python::tuple disp = get_text_displacement(t); - boost::python::tuple anchor = get_anchor(t); // so we do not exceed max args accepted by make_tuple, // lets put the increasing list of parameters in a list @@ -95,7 +83,7 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite return boost::python::make_tuple(disp,t.get_label_placement(), t.get_vertical_alignment(),t.get_halo_radius(),t.get_halo_fill(),t.get_text_ratio(), t.get_wrap_width(),t.get_label_spacing(),t.get_minimum_distance(),t.get_allow_overlap(), - anchor,t.get_force_odd_labels(),t.get_max_char_angle_delta(),extras + t.get_force_odd_labels(),t.get_max_char_angle_delta(),extras ); } @@ -136,15 +124,10 @@ struct text_symbolizer_pickle_suite : boost::python::pickle_suite t.set_allow_overlap(extract(state[9])); - tuple anch = extract(state[10]); - double x = extract(anch[0]); - double y = extract(anch[1]); - t.set_anchor(x,y); + t.set_force_odd_labels(extract(state[10])); - t.set_force_odd_labels(extract(state[11])); - - t.set_max_char_angle_delta(extract(state[12])); - list extras = extract(state[13]); + t.set_max_char_angle_delta(extract(state[11])); + list extras = extract(state[12]); t.set_wrap_char_from_string(extract(extras[0])); t.set_line_spacing(extract(extras[1])); t.set_character_spacing(extract(extras[2])); @@ -180,6 +163,7 @@ void export_text_symbolizer() .value("LEFT",H_LEFT) .value("MIDDLE",H_MIDDLE) .value("RIGHT",H_RIGHT) + .value("AUTO",H_AUTO) ; enumeration_("justify_alignment") @@ -211,9 +195,6 @@ void export_text_symbolizer() */ //.def_pickle(text_symbolizer_pickle_suite()) - .add_property("anchor", - &get_anchor, - &set_anchor) .add_property("allow_overlap", &text_symbolizer::get_allow_overlap, &text_symbolizer::set_allow_overlap, diff --git a/docs/textrendering.gv b/docs/textrendering.gv new file mode 100644 index 000000000..26bf9fb1f --- /dev/null +++ b/docs/textrendering.gv @@ -0,0 +1,40 @@ +/* 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] */ + + Renderer [color=red] + rankdir="TD"; + text_placements[shape=box] + text_placement_info[shape=box] + 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] + text_placement_info -> text_symbolizer_properties [label="properties"] + text_placement_info -> text_path [label="placements", style=dashed] + 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()", style=dashed] + text_path -> Renderer [color=red, label="used by"] + 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/agg_renderer.hpp b/include/mapnik/agg_renderer.hpp index 1a74dd461..9fbe0e80c 100644 --- a/include/mapnik/agg_renderer.hpp +++ b/include/mapnik/agg_renderer.hpp @@ -101,10 +101,7 @@ public: proj_transform const& prj_trans); void process(markers_symbolizer const& sym, Feature const& feature, - proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); + proj_transform const& prj_trans); inline bool process(rule::symbolizers const& /*syms*/, Feature const& /*feature*/, proj_transform const& /*prj_trans*/) diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index e256a6c9e..8efd91911 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -24,16 +24,11 @@ #define MAPNIK_ATTRIBUTE_COLLECTOR_HPP // mapnik -#include #include -#include -#include - // boost #include #include #include - // stl #include #include @@ -93,18 +88,12 @@ struct symbolizer_attributes : public boost::static_visitor<> void operator () (text_symbolizer const& sym) { - expression_ptr const& name_expr = sym.get_name(); - if (name_expr) + std::set::const_iterator it; + std::set expressions = sym.get_placement_options()->get_all_expressions(); + expression_attributes f_attr(names_); + for (it=expressions.begin(); it != expressions.end(); it++) { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*name_expr); - } - - expression_ptr const& orientation_expr = sym.get_orientation(); - if (orientation_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*orientation_expr); + if (*it) boost::apply_visitor(f_attr, **it); } collect_metawriter(sym); } @@ -152,11 +141,12 @@ struct symbolizer_attributes : public boost::static_visitor<> void operator () (shield_symbolizer const& sym) { - expression_ptr const& name_expr = sym.get_name(); - if (name_expr) + std::set::const_iterator it; + std::set expressions = sym.get_placement_options()->get_all_expressions(); + expression_attributes f_attr(names_); + for (it=expressions.begin(); it != expressions.end(); it++) { - expression_attributes name_attr(names_); - boost::apply_visitor(name_attr,*name_expr); + if (*it) boost::apply_visitor(f_attr, **it); } path_expression_ptr const& filename_expr = sym.get_filename(); @@ -167,45 +157,6 @@ struct symbolizer_attributes : public boost::static_visitor<> collect_metawriter(sym); } - void operator () (glyph_symbolizer const& sym) - { - expression_ptr const& char_expr = sym.get_char(); - if (char_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*char_expr); - } - - expression_ptr const& angle_expr = sym.get_angle(); - if (angle_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*angle_expr); - } - - expression_ptr const& value_expr = sym.get_value(); - if (value_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*value_expr); - } - - expression_ptr const& size_expr = sym.get_size(); - if (size_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*size_expr); - } - - expression_ptr const& color_expr = sym.get_color(); - if (color_expr) - { - expression_attributes f_attr(names_); - boost::apply_visitor(f_attr,*color_expr); - } - collect_metawriter(sym); - } - void operator () (markers_symbolizer const& sym) { collect_metawriter(sym); diff --git a/include/mapnik/cairo_renderer.hpp b/include/mapnik/cairo_renderer.hpp index 821ed1334..ad2e0da90 100644 --- a/include/mapnik/cairo_renderer.hpp +++ b/include/mapnik/cairo_renderer.hpp @@ -111,9 +111,6 @@ public: void process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); inline bool process(rule::symbolizers const& /*syms*/, Feature const& /*feature*/, proj_transform const& /*prj_trans*/) diff --git a/src/grid/process_glyph_symbolizer.cpp b/include/mapnik/char_info.hpp similarity index 62% rename from src/grid/process_glyph_symbolizer.cpp rename to include/mapnik/char_info.hpp index b21453a7a..a1417735f 100644 --- a/src/grid/process_glyph_symbolizer.cpp +++ b/include/mapnik/char_info.hpp @@ -19,23 +19,34 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -//$Id$ -// mapnik -#include -#include +#ifndef CHAR_INFO_HPP +#define CHAR_INFO_HPP + +#include namespace mapnik { +struct char_properties; -template -void grid_renderer::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) -{ - std::clog << "grid_renderer does not yet support glyph_symbolizer\n"; -} +class char_info { +public: + char_info(unsigned c_, double width_, double ymax_, double ymin_, double line_height_) + : c(c_), width(width_), line_height(line_height_), ymin(ymin_), ymax(ymax_) + { + } + char_info() + : c(0), width(0), line_height(0), ymin(0), ymax(0) + { + } -template void grid_renderer::process(glyph_symbolizer const&, - Feature const&, - proj_transform const&); + unsigned c; + double width; + double line_height; + double ymin; + double ymax; + double avg_height; + char_properties *format; + double height() const { return ymax-ymin; } +}; } +#endif diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp index e72dc4538..5ded5076d 100644 --- a/include/mapnik/font_engine_freetype.hpp +++ b/include/mapnik/font_engine_freetype.hpp @@ -145,13 +145,6 @@ private: class MAPNIK_DECL font_face_set : private boost::noncopyable { public: - class dimension_t { - public: - dimension_t(unsigned width_, int ymax_, int ymin_) : width(width_), height(ymax_-ymin_), ymin(ymin_) {} - unsigned width, height; - int ymin; - }; - font_face_set(void) : faces_() {} @@ -179,9 +172,9 @@ public: return boost::make_shared(*faces_.begin(), 0); } - dimension_t character_dimensions(const unsigned c); + char_info character_dimensions(const unsigned c); - void get_string_info(string_info & info); + void get_string_info(string_info & info, UnicodeString const& ustr, char_properties *format); void set_pixel_sizes(unsigned size) { @@ -200,7 +193,7 @@ public: } private: std::vector faces_; - std::map dimension_cache_; + std::map dimension_cache_; }; // FT_Stroker wrapper @@ -313,6 +306,18 @@ public: return face_set; } + face_set_ptr get_face_set(std::string const& name, font_set const& fset) + { + if (fset.size() > 0) + { + return get_face_set(fset); + } + else + { + return get_face_set(name); + } + } + stroker_ptr get_stroker() { return stroker_; @@ -330,247 +335,21 @@ struct text_renderer : private boost::noncopyable struct glyph_t : boost::noncopyable { FT_Glyph image; - glyph_t(FT_Glyph image_) : image(image_) {} + char_properties *properties; + glyph_t(FT_Glyph image_, char_properties *properties_) : image(image_), properties(properties_) {} ~glyph_t () { FT_Done_Glyph(image);} }; typedef boost::ptr_vector glyphs_t; typedef T pixmap_type; - text_renderer (pixmap_type & pixmap, face_set_ptr faces, stroker & s) - : pixmap_(pixmap), - faces_(faces), - stroker_(s), - fill_(0,0,0), - halo_fill_(255,255,255), - halo_radius_(0.0), - opacity_(1.0) {} + text_renderer (pixmap_type & pixmap, face_manager &font_manager_, stroker & s); + box2d prepare_glyphs(text_path *path); + void render(double x0, double y0); + void render_id(int feature_id,double x0, double y0, double min_radius=1.0); - - void set_pixel_size(unsigned size) - { - faces_->set_pixel_sizes(size); - } - - void set_character_size(float size) - { - faces_->set_character_sizes(size); - } - - void set_fill(mapnik::color const& fill) - { - fill_=fill; - } - - void set_halo_fill(mapnik::color const& halo) - { - halo_fill_=halo; - } - - void set_halo_radius( double radius=1.0) - { - halo_radius_=radius; - } - - void set_opacity( double opacity=1.0) - { - opacity_=opacity; - } - - box2d prepare_glyphs(text_path *path) - { - //clear glyphs - glyphs_.clear(); - - FT_Matrix matrix; - FT_Vector pen; - FT_Error error; - - FT_BBox bbox; - bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we - bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later - - for (int i = 0; i < path->num_nodes(); i++) - { - int c; - double x, y, angle; - - path->vertex(&c, &x, &y, &angle); - -#ifdef MAPNIK_DEBUG - // TODO Enable when we have support for setting verbosity - //std::clog << "prepare_glyphs: " << c << "," << x << - // "," << y << "," << angle << std::endl; -#endif - - FT_BBox glyph_bbox; - FT_Glyph image; - - pen.x = int(x * 64); - pen.y = int(y * 64); - - glyph_ptr glyph = faces_->get_glyph(unsigned(c)); - FT_Face face = glyph->get_face()->get_face(); - - matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); - matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); - matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); - matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); - - FT_Set_Transform(face, &matrix, &pen); - - error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING); - if ( error ) - continue; - - error = FT_Get_Glyph(face->glyph, &image); - if ( error ) - continue; - - FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); - if (glyph_bbox.xMin < bbox.xMin) - bbox.xMin = glyph_bbox.xMin; - if (glyph_bbox.yMin < bbox.yMin) - bbox.yMin = glyph_bbox.yMin; - if (glyph_bbox.xMax > bbox.xMax) - bbox.xMax = glyph_bbox.xMax; - if (glyph_bbox.yMax > bbox.yMax) - bbox.yMax = glyph_bbox.yMax; - - // Check if we properly grew the bbox - if ( bbox.xMin > bbox.xMax ) - { - bbox.xMin = 0; - bbox.yMin = 0; - bbox.xMax = 0; - bbox.yMax = 0; - } - - // take ownership of the glyph - glyphs_.push_back(new glyph_t(image)); - } - - return box2d(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); - } - - void render(double x0, double y0) - { - FT_Error error; - FT_Vector start; - unsigned height = pixmap_.height(); - - start.x = static_cast(x0 * (1 << 6)); - start.y = static_cast((height - y0) * (1 << 6)); - - // now render transformed glyphs - typename glyphs_t::iterator pos; - - //make sure we've got reasonable values. - if (halo_radius_ > 0.0 && halo_radius_ < 1024.0) - { - stroker_.init(halo_radius_); - for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) - { - FT_Glyph g; - error = FT_Glyph_Copy(pos->image, &g); - if (!error) - { - FT_Glyph_Transform(g,0,&start); - FT_Glyph_Stroke(&g,stroker_.get(),1); - error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); - if ( ! error ) - { - - FT_BitmapGlyph bit = (FT_BitmapGlyph)g; - render_bitmap(&bit->bitmap, halo_fill_.rgba(), - bit->left, - height - bit->top); - } - } - FT_Done_Glyph(g); - } - } - //render actual text - for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) - { - - FT_Glyph_Transform(pos->image,0,&start); - - error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1); - if ( ! error ) - { - - FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image; - render_bitmap(&bit->bitmap, fill_.rgba(), - bit->left, - height - bit->top); - } - } - } - - void render_id(int feature_id,double x0, double y0, double min_radius=1.0) - { - FT_Error error; - FT_Vector start; - unsigned height = pixmap_.height(); - - start.x = static_cast(x0 * (1 << 6)); - start.y = static_cast((height - y0) * (1 << 6)); - - // now render transformed glyphs - typename glyphs_t::iterator pos; - - stroker_.init(std::max(halo_radius_,min_radius)); - for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) - { - FT_Glyph g; - error = FT_Glyph_Copy(pos->image, &g); - if (!error) - { - FT_Glyph_Transform(g,0,&start); - FT_Glyph_Stroke(&g,stroker_.get(),1); - error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); - //error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_MONO,0,1); - if ( ! error ) - { - - FT_BitmapGlyph bit = (FT_BitmapGlyph)g; - render_bitmap_id(&bit->bitmap, feature_id, - bit->left, - height - bit->top); - } - } - FT_Done_Glyph(g); - } - } - private: - - // unused currently, stroker is the new method for drawing halos - /* - void render_halo(FT_Bitmap *bitmap,unsigned rgba,int x,int y,int radius) - { - int x_max=x+bitmap->width; - int y_max=y+bitmap->rows; - int i,p,j,q; - - for (i=x,p=0;ibuffer[q*bitmap->width+p]; - if (gray) - { - for (int n=-halo_radius_; n <=halo_radius_; ++n) - for (int m=-halo_radius_;m <= halo_radius_; ++m) - pixmap_.blendPixel2(i+m,j+n,rgba,gray,opacity_); - } - } - } - } - */ - - void render_bitmap(FT_Bitmap *bitmap,unsigned rgba,int x,int y) + void render_bitmap(FT_Bitmap *bitmap, unsigned rgba, int x, int y, double opacity) { int x_max=x+bitmap->width; int y_max=y+bitmap->rows; @@ -583,7 +362,7 @@ private: int gray=bitmap->buffer[q*bitmap->width+p]; if (gray) { - pixmap_.blendPixel2(i,j,rgba,gray,opacity_); + pixmap_.blendPixel2(i, j, rgba, gray, opacity); } } } @@ -610,14 +389,11 @@ private: } pixmap_type & pixmap_; - face_set_ptr faces_; + face_manager &font_manager_; stroker & stroker_; - color fill_; - color halo_fill_; - double halo_radius_; glyphs_t glyphs_; - double opacity_; }; +typedef face_manager face_manager_freetype; } #endif // MAPNIK_FONT_ENGINE_FREETYPE_HPP diff --git a/include/mapnik/glyph_symbolizer.hpp b/include/mapnik/glyph_symbolizer.hpp deleted file mode 100644 index 25daf62b8..000000000 --- a/include/mapnik/glyph_symbolizer.hpp +++ /dev/null @@ -1,215 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#ifndef MAPNIK_GLYPH_SYMBOLIZER_HPP -#define MAPNIK_GLYPH_SYMBOLIZER_HPP - -// mapnik -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// boost -#include - -namespace mapnik -{ - -typedef boost::tuple position; - -enum angle_mode_enum { - AZIMUTH, - TRIGONOMETRIC, - angle_mode_enum_MAX -}; - -DEFINE_ENUM(angle_mode_e, angle_mode_enum); - -struct MAPNIK_DECL glyph_symbolizer : public symbolizer_base -{ - glyph_symbolizer(std::string face_name, expression_ptr c) - : symbolizer_base(), - face_name_(face_name), - char_(c), - angle_(), - value_(), - size_(), - color_(), - colorizer_(), - allow_overlap_(false), - avoid_edges_(false), - displacement_(0.0, 0.0), - halo_fill_(color(255,255,255)), - halo_radius_(0), - angle_mode_(TRIGONOMETRIC) {} - - std::string const& get_face_name() const - { - return face_name_; - } - void set_face_name(std::string face_name) - { - face_name_ = face_name; - } - - expression_ptr get_char() const - { - return char_; - } - void set_char(expression_ptr c) - { - char_ = c; - } - - bool get_allow_overlap() const - { - return allow_overlap_; - } - void set_allow_overlap(bool allow_overlap) - { - allow_overlap_ = allow_overlap; - } - - bool get_avoid_edges() const - { - return avoid_edges_; - } - void set_avoid_edges(bool avoid_edges) - { - avoid_edges_ = avoid_edges; - } - - void set_displacement(double x, double y) - { - displacement_ = boost::make_tuple(x,y); - } - position const& get_displacement() const - { - return displacement_; - } - - void set_halo_fill(color const& fill) - { - halo_fill_ = fill; - } - color const& get_halo_fill() const - { - return halo_fill_; - } - - void set_halo_radius(unsigned radius) - { - halo_radius_ = radius; - } - - unsigned get_halo_radius() const - { - return halo_radius_; - } - - expression_ptr get_angle() const - { - return angle_; - } - void set_angle(expression_ptr angle) - { - angle_ = angle; - } - - expression_ptr get_value() const - { - return value_; - } - void set_value(expression_ptr value) - { - value_ = value; - } - - expression_ptr get_size() const - { - return size_; - } - void set_size(expression_ptr size) - { - size_ = size; - } - - expression_ptr get_color() const - { - return color_; - } - void set_color(expression_ptr color) - { - color_ = color; - } - - raster_colorizer_ptr get_colorizer() const - { - return colorizer_; - } - void set_colorizer(raster_colorizer_ptr const& colorizer) - { - colorizer_ = colorizer; - } - void set_angle_mode(angle_mode_e angle_mode) - { - angle_mode_ = angle_mode; - } - angle_mode_e get_angle_mode() const - { - return angle_mode_; - } - - - text_path_ptr get_text_path(face_set_ptr const& faces, - Feature const& feature) const; - UnicodeString eval_char(Feature const& feature) const; - double eval_angle(Feature const& feature) const; - unsigned eval_size(Feature const& feature) const; - color eval_color(Feature const& feature) const; - - -private: - std::string face_name_; - expression_ptr char_; - expression_ptr angle_; - expression_ptr value_; - expression_ptr size_; - expression_ptr color_; - raster_colorizer_ptr colorizer_; - bool allow_overlap_; - bool avoid_edges_; - position displacement_; - color halo_fill_; - unsigned halo_radius_; - angle_mode_e angle_mode_; -}; - -} // end mapnik namespace - -#endif // MAPNIK_GLYPH_SYMBOLIZER_HPP diff --git a/include/mapnik/grid/grid.hpp b/include/mapnik/grid/grid.hpp index 801422bfd..37c7172af 100644 --- a/include/mapnik/grid/grid.hpp +++ b/include/mapnik/grid/grid.hpp @@ -24,7 +24,6 @@ #define MAPNIK_GRID_HPP // mapnik -#include #include #include #include diff --git a/include/mapnik/grid/grid_renderer.hpp b/include/mapnik/grid/grid_renderer.hpp index 54b0bae29..4517e4845 100644 --- a/include/mapnik/grid/grid_renderer.hpp +++ b/include/mapnik/grid/grid_renderer.hpp @@ -98,9 +98,6 @@ public: void process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); inline bool process(rule::symbolizers const& /*syms*/, Feature const& /*feature*/, proj_transform const& /*prj_trans*/) diff --git a/include/mapnik/jpeg_io.hpp b/include/mapnik/jpeg_io.hpp index f5d96bc71..0f681d017 100644 --- a/include/mapnik/jpeg_io.hpp +++ b/include/mapnik/jpeg_io.hpp @@ -27,8 +27,11 @@ #include +#include + extern "C" { +#include #include } diff --git a/include/mapnik/label_placement.hpp b/include/mapnik/label_placement.hpp deleted file mode 100644 index a667516a9..000000000 --- a/include/mapnik/label_placement.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ - -#ifndef MAPNIK_LABEL_PLACEMENT_HPP -#define MAPNIK_LABEL_PLACEMENT_HPP - -namespace mapnik -{ -struct point_ -{ - double x; - double y; - point_() - : x(0),y(0) {} - point_(double x_,double y_) - : x(x_),y(y_) {} -}; - -class label_placement -{ -private: - point_ anchor_; - point_ displacement_; - double rotation_; -public: - label_placement() - : anchor_(), - displacement_(), - rotation_(0.0) {} - -}; -} - -#endif // MAPNIK_LABEL_PLACEMENT_HPP diff --git a/include/mapnik/metawriter.hpp b/include/mapnik/metawriter.hpp index fa324cecf..7bd2fc394 100644 --- a/include/mapnik/metawriter.hpp +++ b/include/mapnik/metawriter.hpp @@ -41,7 +41,7 @@ namespace mapnik { -struct placement; +class text_placement_info; /** Implementation of std::map that also returns const& for operator[]. */ class metawriter_property_map @@ -98,8 +98,8 @@ public: virtual void add_box(box2d const& box, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties)=0; - virtual void add_text(placement const& placement, - face_set_ptr face, + virtual void add_text(text_placement_info const& placement, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties)=0; diff --git a/include/mapnik/metawriter_inmem.hpp b/include/mapnik/metawriter_inmem.hpp index 5fb00d624..870bdcb2d 100644 --- a/include/mapnik/metawriter_inmem.hpp +++ b/include/mapnik/metawriter_inmem.hpp @@ -65,8 +65,8 @@ public: virtual void add_box(box2d const& box, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); - virtual void add_text(placement const& p, - face_set_ptr face, + virtual void add_text(text_placement_info const& p, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); diff --git a/include/mapnik/metawriter_json.hpp b/include/mapnik/metawriter_json.hpp index eb7ae8ca9..6c30c2355 100644 --- a/include/mapnik/metawriter_json.hpp +++ b/include/mapnik/metawriter_json.hpp @@ -45,8 +45,8 @@ public: virtual void add_box(box2d const& box, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); - virtual void add_text(placement const& p, - face_set_ptr face, + virtual void add_text(text_placement_info const& p, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties); diff --git a/include/mapnik/octree.hpp b/include/mapnik/octree.hpp index 755fbb3cc..bdba82890 100644 --- a/include/mapnik/octree.hpp +++ b/include/mapnik/octree.hpp @@ -34,6 +34,7 @@ #include #include #include +#include namespace mapnik { diff --git a/include/mapnik/placement_finder.hpp b/include/mapnik/placement_finder.hpp index bf8869ec4..f565a90f6 100644 --- a/include/mapnik/placement_finder.hpp +++ b/include/mapnik/placement_finder.hpp @@ -23,86 +23,31 @@ #ifndef MAPNIK_PLACEMENT_FINDER_HPP #define MAPNIK_PLACEMENT_FINDER_HPP -// mapnik -#include -#include -#include -#include #include -#include #include -// stl -#include - namespace mapnik { -typedef text_path placement_element; - -struct placement : boost::noncopyable -{ - placement(string_info & info_, shield_symbolizer const& sym, double scale_factor, unsigned w, unsigned h, bool has_dimensions_= false); - - placement(string_info & info_, text_symbolizer const& sym, double scale_factor); - - ~placement(); - - string_info & info; // should only be used for finding placement. doesn't necessarily match placements.vertex() values - - double scale_factor_; - label_placement_e label_placement; - - std::queue< box2d > envelopes; - - //output - boost::ptr_vector placements; - - int wrap_width; - bool wrap_before; // wraps text at wrap_char immediately before current word - unsigned char wrap_char; - int text_ratio; - - int label_spacing; // distance between repeated labels on a single geometry - unsigned label_position_tolerance; //distance the label can be moved on the line to fit, if 0 the default is used - bool force_odd_labels; //Always try render an odd amount of labels - - double max_char_angle_delta; - double minimum_distance; - double minimum_padding; - double minimum_path_length; - bool avoid_edges; - bool has_dimensions; - bool allow_overlap; - std::pair dimensions; - bool collect_extents; - box2d extents; - - // additional boxes attached to the text labels which must also be - // placed in order for the text placement to succeed. e.g: shields. - std::vector > additional_boxes; -}; - - template class placement_finder : boost::noncopyable { public: - placement_finder(DetectorT & detector); - placement_finder(DetectorT & detector, box2d const& extent); + 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 - void find_point_placement(placement & p, text_placement_info_ptr po, double pos_x, double pos_y, double angle=0.0, unsigned line_spacing=0, unsigned character_spacing=0); + /** 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(placement & p, text_placement_info_ptr po, T & path); + 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(placement & p, text_placement_info_ptr po, T & path); + void find_line_placements(T & path); - void update_detector(placement & p); + void update_detector(); void clear(); @@ -117,15 +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(placement & p, - 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(placement & p, 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 @@ -137,14 +81,26 @@ private: const double &x1, const double &y1, const double &x2, const double &y2, double &ix, double &iy); + void find_line_breaks(); + void init_string_size(); + void init_alignment(); + void adjust_position(text_path *current_placement, double label_x, double label_y); + ///General Internals - - - DetectorT & detector_; box2d const& dimensions_; + string_info &info_; + text_symbolizer_properties &p; + text_placement_info π + double string_width_; + double string_height_; + double first_line_space_; + vertical_alignment_e valign_; + horizontal_alignment_e halign_; + std::vector line_breaks_; + std::vector > line_sizes_; }; - } + #endif // MAPNIK_PLACEMENT_FINDER_HPP diff --git a/include/mapnik/ptree_helpers.hpp b/include/mapnik/ptree_helpers.hpp index c97ce4d55..c3790ebdf 100644 --- a/include/mapnik/ptree_helpers.hpp +++ b/include/mapnik/ptree_helpers.hpp @@ -258,7 +258,7 @@ T get(const boost::property_tree::ptree & node, const std::string & name, bool i } else { - str = node.get_optional(name ); + str = node.get_optional(name+"."); } if ( str ) { @@ -289,7 +289,7 @@ inline color get(boost::property_tree::ptree const& node, std::string const& nam } else { - str = node.get_optional(name ); + str = node.get_optional(name+"."); } if ( str ) @@ -322,7 +322,7 @@ T get(const boost::property_tree::ptree & node, const std::string & name, bool i } else { - str = node.get_optional(name); + str = node.get_optional(name+"."); } if ( ! str ) { @@ -348,7 +348,18 @@ T get_value(const boost::property_tree::ptree & node, const std::string & name) { try { - return node.get_value(); + /* NOTE: get_child works as long as there is only one child with that name. + If this function is used this used this condition must always be satisfied. + */ + return node.get_child("").get_value(); + } + catch (boost::property_tree::ptree_bad_path) + { + /* If the XML parser did not find any non-empty data element the is no + node. But we don't want to fail here but simply return a + default constructed value of the requested type. + */ + return T(); } catch (...) { @@ -369,7 +380,7 @@ boost::optional get_optional(const boost::property_tree::ptree & node, const } else { - str = node.get_optional(name); + str = node.get_optional(name+"."); } boost::optional result; @@ -403,7 +414,7 @@ inline boost::optional get_optional(const boost::property_tree::ptree & n } else { - str = node.get_optional(name); + str = node.get_optional(name+"."); } boost::optional result; diff --git a/include/mapnik/rule.hpp b/include/mapnik/rule.hpp index d3185bef9..9e811bf3f 100644 --- a/include/mapnik/rule.hpp +++ b/include/mapnik/rule.hpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -108,11 +107,6 @@ inline bool operator==(markers_symbolizer const& lhs, return (&lhs == &rhs); } -inline bool operator==(glyph_symbolizer const& lhs, - glyph_symbolizer const& rhs) -{ - return (&lhs == &rhs); -} typedef boost::variant symbolizer; + markers_symbolizer> symbolizer; diff --git a/include/mapnik/shield_symbolizer.hpp b/include/mapnik/shield_symbolizer.hpp index 847bff663..06b12fb2e 100644 --- a/include/mapnik/shield_symbolizer.hpp +++ b/include/mapnik/shield_symbolizer.hpp @@ -36,6 +36,8 @@ namespace mapnik struct MAPNIK_DECL shield_symbolizer : public text_symbolizer, public symbolizer_with_image { + shield_symbolizer(text_placements_ptr placements = text_placements_ptr( + boost::make_shared())); shield_symbolizer(expression_ptr name, std::string const& face_name, float size, @@ -48,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/svg_renderer.hpp b/include/mapnik/svg_renderer.hpp index 3459d0eb3..d4e5e1773 100644 --- a/include/mapnik/svg_renderer.hpp +++ b/include/mapnik/svg_renderer.hpp @@ -83,9 +83,6 @@ namespace mapnik void process(markers_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans); - void process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); /*! * @brief Overload that process the whole set of symbolizers of a rule. diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp index 546c4c1fe..159664617 100644 --- a/include/mapnik/symbolizer.hpp +++ b/include/mapnik/symbolizer.hpp @@ -96,10 +96,10 @@ public: void set_opacity(float opacity); float get_opacity() const; protected: - symbolizer_with_image(path_expression_ptr filename); + symbolizer_with_image(path_expression_ptr filename = path_expression_ptr()); symbolizer_with_image(symbolizer_with_image const& rhs); path_expression_ptr image_filename_; - float opacity_; + float image_opacity_; transform_type matrix_; }; } diff --git a/include/mapnik/symbolizer_helpers.hpp b/include/mapnik/symbolizer_helpers.hpp new file mode 100644 index 000000000..6c9dcdcb5 --- /dev/null +++ b/include/mapnik/symbolizer_helpers.hpp @@ -0,0 +1,169 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2012 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#ifndef SYMBOLIZER_HELPERS_HPP +#define SYMBOLIZER_HELPERS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace mapnik { + +/** Helper object that does all the TextSymbolizer placment finding + * work except actually rendering the object. */ +template +class text_symbolizer_helper +{ +public: + 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) + : 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(); + + //Input + text_symbolizer const& sym_; + Feature const& feature_; + proj_transform const& prj_trans_; + CoordTransform const &t_; + FaceManagerT &font_manager_; + DetectorT &detector_; + 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 +class shield_symbolizer_helper: public text_symbolizer_helper +{ +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) + { + init_marker(); + } + + 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_path.hpp b/include/mapnik/text_path.hpp index eeebd97c4..b7d7daaf1 100644 --- a/include/mapnik/text_path.hpp +++ b/include/mapnik/text_path.hpp @@ -23,6 +23,12 @@ #ifndef MAPNIK_TEXT_PATH_HPP #define MAPNIK_TEXT_PATH_HPP +// mapnik +#include + +//stl +#include + // boost #include #include @@ -32,41 +38,39 @@ namespace mapnik { -struct character_info -{ - int character; - double width, height; - - character_info() : character(0), width(0), height(0) {} - character_info(int c_, double width_, double height_) : character(c_), width(width_), height(height_) {} - ~character_info() {} - - character_info(const character_info &ci) - : character(ci.character), width(ci.width), height(ci.height) - { - } - -}; class string_info : private boost::noncopyable { protected: - typedef boost::ptr_vector characters_t; + typedef std::vector characters_t; characters_t characters_; - UnicodeString const& text_; - double width_; - double height_; + UnicodeString text_; bool is_rtl; public: string_info(UnicodeString const& text) - : text_(text), - width_(0), - height_(0), - is_rtl(false) {} - - void add_info(int c, double width, double height) + : characters_(), + text_(text), + is_rtl(false) { - characters_.push_back(new character_info(c, width, height)); + + } + + string_info() + : characters_(), + text_(), + is_rtl(false) + { + + } + + void add_info(char_info const& info) + { + characters_.push_back(info); + } + + void add_text(UnicodeString text) + { + text_ += text; } unsigned num_characters() const @@ -74,29 +78,25 @@ public: return characters_.size(); } - void set_rtl(bool value) {is_rtl = value;} - bool get_rtl() const {return is_rtl;} + void set_rtl(bool value) + { + is_rtl = value; + } + + bool get_rtl() const + { + return is_rtl; + } - character_info at(unsigned i) const + char_info const& at(unsigned i) const { return characters_[i]; } - character_info operator[](unsigned i) const + char_info const& operator[](unsigned i) const { return at(i); } - - void set_dimensions(double width, double height) - { - width_ = width; - height_ = height; - } - - std::pair get_dimensions() const - { - return std::pair(width_, height_); - } UnicodeString const& get_string() const { @@ -108,69 +108,84 @@ public: UChar break_char = '\n'; return (text_.indexOf(break_char) >= 0); } + + /** Resets object to initial state. */ + void clear(void) + { + text_ = ""; + characters_.clear(); + } }; -struct text_path : boost::noncopyable + +/** List of all characters and their positions and formats for a placement. */ +class text_path : boost::noncopyable { struct character_node { int c; double x, y, angle; + char_properties *format; - character_node(int c_, double x_, double y_, double angle_) - : c(c_), x(x_), y(y_), angle(angle_) {} + character_node(int c_, double x_, double y_, double angle_, char_properties *format_) + : c(c_), x(x_), y(y_), angle(angle_), format(format_) {} ~character_node() {} - void vertex(int *c_, double *x_, double *y_, double *angle_) + void vertex(int *c_, double *x_, double *y_, double *angle_, char_properties **format_) { *c_ = c; *x_ = x; *y_ = y; *angle_ = angle; + *format_ = format; } }; + int itr_; +public: typedef std::vector character_nodes_t; + character_nodes_t nodes_; double starting_x; double starting_y; - character_nodes_t nodes_; - int itr_; - std::pair string_dimensions; +// std::pair string_dimensions; text_path() - : starting_x(0), - starting_y(0), - itr_(0) {} - - //text_path(text_path const& other) : - // itr_(0), - // nodes_(other.nodes_), - // string_dimensions(other.string_dimensions) - //{} + : itr_(0), + starting_x(0), + starting_y(0) + + { + + } ~text_path() {} - void add_node(int c, double x, double y, double angle) + /** Adds a new char to the list. */ + void add_node(int c, double x, double y, double angle, char_properties *format) { - nodes_.push_back(character_node(c, x, y, angle)); + nodes_.push_back(character_node(c, x, y, angle, format)); } - void vertex(int *c, double *x, double *y, double *angle) + /** Return node. Always returns a new node. Has no way to report that there are no more nodes. */ + void vertex(int *c, double *x, double *y, double *angle, char_properties **format) { - nodes_[itr_++].vertex(c, x, y, angle); + nodes_[itr_++].vertex(c, x, y, angle, format); } + /** Start again at first node. */ void rewind() { itr_ = 0; } + /** Number of nodes. */ int num_nodes() const { return nodes_.size(); } + /** Delete all nodes. */ void clear() { nodes_.clear(); diff --git a/include/mapnik/text_placements.hpp b/include/mapnik/text_placements.hpp index 383bbcb33..195b2ac8d 100644 --- a/include/mapnik/text_placements.hpp +++ b/include/mapnik/text_placements.hpp @@ -24,159 +24,257 @@ #define MAPNIK_TEXT_PLACEMENTS_HPP // mapnik -#include -#include +#include +#include +#include +#include +#include // stl #include #include +#include +#include // boost #include #include #include +#include namespace mapnik { -typedef boost::tuple position; - -enum label_placement_enum { - POINT_PLACEMENT, - LINE_PLACEMENT, - VERTEX_PLACEMENT, - INTERIOR_PLACEMENT, - label_placement_enum_MAX -}; - -DEFINE_ENUM( label_placement_e, label_placement_enum ); - -enum vertical_alignment -{ - V_TOP = 0, - V_MIDDLE, - V_BOTTOM, - V_AUTO, - vertical_alignment_MAX -}; - -DEFINE_ENUM( vertical_alignment_e, vertical_alignment ); - -enum horizontal_alignment -{ - H_LEFT = 0, - H_MIDDLE, - H_RIGHT, - H_AUTO, - horizontal_alignment_MAX -}; - -DEFINE_ENUM( horizontal_alignment_e, horizontal_alignment ); - -enum justify_alignment -{ - J_LEFT = 0, - J_MIDDLE, - J_RIGHT, - justify_alignment_MAX -}; - -DEFINE_ENUM( justify_alignment_e, justify_alignment ); - -enum text_transform -{ - NONE = 0, - UPPERCASE, - LOWERCASE, - CAPITALIZE, - text_transform_MAX -}; - -DEFINE_ENUM( text_transform_e, text_transform ); - class text_placements; -class text_placement_info : boost::noncopyable -{ -public: - text_placement_info(text_placements const* parent); - /** Get next placement. - * This function is also called before the first placement is tried. */ - virtual bool next()=0; - /** Get next placement position. - * This function is also called before the first position is used. - * Each class has to return at least one position! - * If this functions returns false the placement data should be considered invalid! - */ - virtual bool next_position_only()=0; - virtual ~text_placement_info() {} +typedef std::pair position; +typedef std::pair dimension_type; - /* NOTE: Values are public and non-virtual to avoid any performance problems. */ - position displacement; +struct char_properties +{ + char_properties(); + /** Construct object from XML. */ + 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; + font_set fontset; float text_size; + double character_spacing; + double line_spacing; //Largest total height (fontsize+line_spacing) per line is chosen + double text_opacity; + bool wrap_before; + unsigned wrap_char; + text_transform_e text_transform; //Per expression + color fill; + color halo_fill; + double halo_radius; +}; + +/** Contains all text symbolizer properties which are not directly related to text formating. */ +struct text_symbolizer_properties +{ + text_symbolizer_properties(); + /** Load all values from XML ptree. */ + 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; + + /** 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) const; + /** Automatically create processing instructions for a single expression. */ + void set_old_style_expression(expression_ptr expr); + /** Sets new format tree. */ + void set_format_tree(formating::node_ptr tree); + /** Get format tree. */ + formating::node_ptr 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; + + //Per symbolizer options + expression_ptr orientation; + position displacement; + label_placement_e label_placement; horizontal_alignment_e halign; justify_alignment_e jalign; vertical_alignment_e valign; + /** distance between repeated labels on a single geometry */ + unsigned label_spacing; + /** distance the label can be moved on the line to fit, if 0 the default is used */ + unsigned label_position_tolerance; + bool avoid_edges; + double minimum_distance; + double minimum_padding; + double minimum_path_length; + double max_char_angle_delta; + /** Always try render an odd amount of labels */ + bool force_odd_labels; + bool allow_overlap; + unsigned text_ratio; + unsigned wrap_width; + /** Default values for char_properties. */ + char_properties default_format; +private: + formating::node_ptr tree_; +}; + +class processed_text : boost::noncopyable +{ +public: + class processed_expression + { + public: + processed_expression(char_properties const& properties, UnicodeString const& text) : + p(properties), str(text) {} + char_properties p; + UnicodeString str; + }; +public: + processed_text(face_manager & font_manager, double scale_factor); + void push_back(processed_expression const& exp); + unsigned size() const { return expr_list_.size(); } + unsigned empty() const { return expr_list_.empty(); } + void clear(); + typedef std::list expression_list; + expression_list::const_iterator begin() const; + expression_list::const_iterator end() const; + string_info &get_string_info(); +private: + expression_list expr_list_; + face_manager & font_manager_; + double scale_factor_; + string_info info_; +}; + +/** Generate a possible placement and store results of placement_finder. + * This placement has first to be tested by placement_finder to verify it + * can actually be used. + */ +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, + 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! + * If this functions returns false the placement data should be + * considered invalid! + */ + virtual bool next()=0; + virtual ~text_placement_info() {} + + /** Properties actually used by placement finder and renderer. Values in + * here are modified each time next() is called. */ + text_symbolizer_properties properties; + + /** Scale factor used by the renderer. */ + double scale_factor; + /* TODO: Don't know what this is used for. */ + bool has_dimensions; + /* TODO: Don't know what this is used for. */ + dimension_type dimensions; + /** Set scale factor. */ + void set_scale_factor(double factor) { scale_factor = factor; } + /** Get scale factor. */ + double get_scale_factor() { return scale_factor; } + /** Get label spacing taking the scale factor into account. */ + double get_actual_label_spacing() { return scale_factor * properties.label_spacing; } + /** Get minimum distance taking the scale factor into account. */ + double get_actual_minimum_distance() { return scale_factor * properties.minimum_distance; } + /** Get minimum padding taking the scale factor into account. */ + double get_actual_minimum_padding() { return scale_factor * properties.minimum_padding; } + + /** Collect a bounding box of all texts placed. */ + bool collect_extents; + //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; + /** Used to return all placements found. */ + boost::ptr_vector placements; }; typedef boost::shared_ptr text_placement_info_ptr; +/** This object handles the management of all TextSymbolizer properties. It can + * be used as a base class for own objects which implement new processing + * semantics. Basically this class just makes sure a pointer of the right + * class is returned by the get_placement_info call. + */ class text_placements { public: - text_placements() : - text_size_(10), halign_(H_MIDDLE), jalign_(J_MIDDLE), valign_(V_MIDDLE) {} - virtual text_placement_info_ptr get_placement_info() const =0; - - virtual void set_default_text_size(float size) { text_size_ = size; } - float get_default_text_size() const { return text_size_; } - - virtual void set_default_displacement(position const& displacement) { displacement_ = displacement;} - position const& get_default_displacement() { return displacement_; } - - virtual void set_default_halign(horizontal_alignment_e const& align) { halign_ = align;} - horizontal_alignment_e const& get_default_halign() { return halign_; } - - virtual void set_default_jalign(justify_alignment_e const& align) { jalign_ = align;} - justify_alignment_e const& get_default_jalign() { return jalign_; } - - virtual void set_default_valign(vertical_alignment_e const& align) { valign_ = align;} - vertical_alignment_e const& get_default_valign() { return valign_; } + text_placements(); + /** Get a text_placement_info object to use in rendering. + * The returned object creates a list of settings which is + * used to try to find a placement and stores all + * information that is generated by + * the placement finder. + * + * This function usually is implemented as + * text_placement_info_ptr text_placements_XXX::get_placement_info() const + * { + * return text_placement_info_ptr(new text_placement_info_XXX(this)); + * } + */ + 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. + */ + virtual std::set get_all_expressions(); + /** Destructor. */ virtual ~text_placements() {} -protected: - float text_size_; - position displacement_; - horizontal_alignment_e halign_; - justify_alignment_e jalign_; - vertical_alignment_e valign_; - friend class text_placement_info; + + /** List of all properties used as the default for the subclasses. */ + text_symbolizer_properties properties; }; +/** Pointer to object of class text_placements */ typedef boost::shared_ptr text_placements_ptr; + class text_placements_info_dummy; +/** Dummy placement algorithm. Always takes the default value. */ 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; }; +/** Placement info object for dummy placement algorithm. Always takes the default value. */ 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), position_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(); - bool next_position_only(); private: unsigned state; - unsigned position_state; text_placements_dummy const* parent_; }; + } //namespace #endif // MAPNIK_TEXT_PLACEMENTS_HPP diff --git a/include/mapnik/text_placements_list.hpp b/include/mapnik/text_placements_list.hpp new file mode 100644 index 000000000..3453c3010 --- /dev/null +++ b/include/mapnik/text_placements_list.hpp @@ -0,0 +1,64 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Hermann Kraus + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#ifndef TEXT_PLACEMENTS_LIST_HPP +#define TEXT_PLACEMENTS_LIST_HPP +#include + +namespace mapnik { + +class text_placement_info_list; + + +/** Tries a list of placements. */ +class text_placements_list: public text_placements +{ +public: + text_placements_list(); + 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); + unsigned size() const; +private: + std::vector list_; + friend class text_placement_info_list; +}; + +/** List placement strategy. + * See parent class for documentation of each function. */ +class text_placement_info_list : public text_placement_info +{ +public: + 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; + text_placements_list const* parent_; +}; + + +} //namespace +#endif diff --git a/include/mapnik/text_placements_simple.hpp b/include/mapnik/text_placements_simple.hpp index fe0e541ca..575dee4cd 100644 --- a/include/mapnik/text_placements_simple.hpp +++ b/include/mapnik/text_placements_simple.hpp @@ -49,8 +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 get_positions(); private: std::string positions_; std::vector direction_; @@ -63,11 +65,15 @@ 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(); -private: unsigned state; unsigned position_state; text_placements_simple const* parent_; diff --git a/include/mapnik/text_processing.hpp b/include/mapnik/text_processing.hpp new file mode 100644 index 000000000..bd5698fc7 --- /dev/null +++ b/include/mapnik/text_processing.hpp @@ -0,0 +1,179 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#ifndef MAPNIK_TEXT_PROCESSING_HPP +#define MAPNIK_TEXT_PROCESSING_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +namespace mapnik +{ +class processed_text; +struct char_properties; + +enum label_placement_enum { + POINT_PLACEMENT, + LINE_PLACEMENT, + VERTEX_PLACEMENT, + INTERIOR_PLACEMENT, + label_placement_enum_MAX +}; + +DEFINE_ENUM( label_placement_e, label_placement_enum ); + +enum vertical_alignment +{ + V_TOP = 0, + V_MIDDLE, + V_BOTTOM, + V_AUTO, + vertical_alignment_MAX +}; + +DEFINE_ENUM( vertical_alignment_e, vertical_alignment ); + +enum horizontal_alignment +{ + H_LEFT = 0, + H_MIDDLE, + H_RIGHT, + H_AUTO, + horizontal_alignment_MAX +}; + +DEFINE_ENUM( horizontal_alignment_e, horizontal_alignment ); + +enum justify_alignment +{ + J_LEFT = 0, + J_MIDDLE, + J_RIGHT, + justify_alignment_MAX +}; + +DEFINE_ENUM( justify_alignment_e, justify_alignment ); + +enum text_transform +{ + NONE = 0, + UPPERCASE, + LOWERCASE, + CAPITALIZE, + text_transform_MAX +}; +DEFINE_ENUM( text_transform_e, text_transform ); + +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 + +} /* namespace mapnik*/ + +#endif diff --git a/include/mapnik/text_symbolizer.hpp b/include/mapnik/text_symbolizer.hpp index d6c023499..cba2fdfe0 100644 --- a/include/mapnik/text_symbolizer.hpp +++ b/include/mapnik/text_symbolizer.hpp @@ -26,8 +26,6 @@ // mapnik #include #include -#include -#include #include #include @@ -38,6 +36,13 @@ // stl #include +// Warning disabled for the moment +#if (0 && __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define func_deprecated __attribute__ ((deprecated)) +#else +#define func_deprecated +#endif + namespace mapnik { @@ -45,6 +50,7 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base { // Note - we do not use boost::make_shared below as VC2008 and VC2010 are // not able to compile make_shared used within a constructor + text_symbolizer(text_placements_ptr placements = text_placements_ptr(new text_placements_dummy)); text_symbolizer(expression_ptr name, std::string const& face_name, float size, color const& fill, text_placements_ptr placements = text_placements_ptr(new text_placements_dummy) @@ -54,103 +60,75 @@ struct MAPNIK_DECL text_symbolizer : public symbolizer_base ); text_symbolizer(text_symbolizer const& rhs); text_symbolizer& operator=(text_symbolizer const& rhs); - expression_ptr get_name() const; + expression_ptr get_name() const func_deprecated; void set_name(expression_ptr expr); - expression_ptr get_orientation() const; // orienation (rotation angle atm) + expression_ptr get_orientation() const func_deprecated; // orienation (rotation angle atm) void set_orientation(expression_ptr expr); - unsigned get_text_ratio() const; // target ratio for text bounding box in pixels + unsigned get_text_ratio() const func_deprecated; // target ratio for text bounding box in pixels void set_text_ratio(unsigned ratio); - unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio + unsigned get_wrap_width() const func_deprecated; // width to wrap text at, or trigger ratio void set_wrap_width(unsigned ratio); - unsigned char get_wrap_char() const; // character used to wrap lines - std::string get_wrap_char_string() const; // character used to wrap lines as std::string + unsigned char get_wrap_char() const func_deprecated; // character used to wrap lines + std::string get_wrap_char_string() const func_deprecated; // character used to wrap lines as std::string void set_wrap_char(unsigned char character); void set_wrap_char_from_string(std::string const& character); - text_transform_e get_text_transform() const; // text conversion on strings before display + text_transform_e get_text_transform() const func_deprecated; // text conversion on strings before display void set_text_transform(text_transform_e convert); - unsigned get_line_spacing() const; // spacing between lines of text + unsigned get_line_spacing() const func_deprecated; // spacing between lines of text void set_line_spacing(unsigned spacing); - unsigned get_character_spacing() const; // spacing between characters in text + unsigned get_character_spacing() const func_deprecated; // spacing between characters in text void set_character_spacing(unsigned spacing); - unsigned get_label_spacing() const; // spacing between repeated labels on lines + unsigned get_label_spacing() const func_deprecated; // spacing between repeated labels on lines void set_label_spacing(unsigned spacing); - unsigned get_label_position_tolerance() const; //distance the label can be moved on the line to fit, if 0 the default is used + unsigned get_label_position_tolerance() const func_deprecated; //distance the label can be moved on the line to fit, if 0 the default is used void set_label_position_tolerance(unsigned tolerance); - bool get_force_odd_labels() const; // try render an odd amount of labels + bool get_force_odd_labels() const func_deprecated; // try render an odd amount of labels void set_force_odd_labels(bool force); - double get_max_char_angle_delta() const; // maximum change in angle between adjacent characters + double get_max_char_angle_delta() const func_deprecated; // maximum change in angle between adjacent characters void set_max_char_angle_delta(double angle); - float get_text_size() const; + float get_text_size() const func_deprecated; void set_text_size(float size); - std::string const& get_face_name() const; + std::string const& get_face_name() const func_deprecated; void set_face_name(std::string face_name); - font_set const& get_fontset() const; + font_set const& get_fontset() const func_deprecated; void set_fontset(font_set const& fset); - color const& get_fill() const; + color const& get_fill() const func_deprecated; void set_fill(color const& fill); void set_halo_fill(color const& fill); - color const& get_halo_fill() const; + color const& get_halo_fill() const func_deprecated; void set_halo_radius(double radius); - double get_halo_radius() const; + double get_halo_radius() const func_deprecated; void set_label_placement(label_placement_e label_p); - label_placement_e get_label_placement() const; + label_placement_e get_label_placement() const func_deprecated; void set_vertical_alignment(vertical_alignment_e valign); - vertical_alignment_e get_vertical_alignment() const; - void set_anchor(double x, double y); - position const& get_anchor() const; + vertical_alignment_e get_vertical_alignment() const func_deprecated; void set_displacement(double x, double y); void set_displacement(position const& p); - position const& get_displacement() const; + position const& get_displacement() const func_deprecated; void set_avoid_edges(bool avoid); - bool get_avoid_edges() const; + bool get_avoid_edges() const func_deprecated; void set_minimum_distance(double distance); - double get_minimum_distance() const; + double get_minimum_distance() const func_deprecated; void set_minimum_padding(double distance); - double get_minimum_padding() const; + double get_minimum_padding() const func_deprecated; void set_minimum_path_length(double size); - double get_minimum_path_length() const; + double get_minimum_path_length() const func_deprecated; void set_allow_overlap(bool overlap); - bool get_allow_overlap() const; + bool get_allow_overlap() const func_deprecated; void set_text_opacity(double opacity); - double get_text_opacity() const; - bool get_wrap_before() const; // wrap text at wrap_char immediately before current work + double get_text_opacity() const func_deprecated; void set_wrap_before(bool wrap_before); + bool get_wrap_before() const func_deprecated; // wrap text at wrap_char immediately before current work void set_horizontal_alignment(horizontal_alignment_e valign); - horizontal_alignment_e get_horizontal_alignment() const; + horizontal_alignment_e get_horizontal_alignment() const func_deprecated; void set_justify_alignment(justify_alignment_e valign); - justify_alignment_e get_justify_alignment() const; + justify_alignment_e get_justify_alignment() const func_deprecated; text_placements_ptr get_placement_options() const; void set_placement_options(text_placements_ptr placement_options); private: - expression_ptr name_; - expression_ptr orientation_; - std::string face_name_; - font_set fontset_; - unsigned text_ratio_; - unsigned wrap_width_; - unsigned char wrap_char_; - text_transform_e text_transform_; - unsigned line_spacing_; - unsigned character_spacing_; - unsigned label_spacing_; - unsigned label_position_tolerance_; - bool force_odd_labels_; - double max_char_angle_delta_; - color fill_; - color halo_fill_; - double halo_radius_; - label_placement_e label_p_; - position anchor_; - bool avoid_edges_; - double minimum_distance_; - double minimum_padding_; - double minimum_path_length_; - bool overlap_; - double text_opacity_; - bool wrap_before_; text_placements_ptr placement_options_; }; } diff --git a/src/agg/agg_renderer.cpp b/src/agg/agg_renderer.cpp index a2ef94c34..6309758b8 100644 --- a/src/agg/agg_renderer.cpp +++ b/src/agg/agg_renderer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/src/agg/process_glyph_symbolizer.cpp b/src/agg/process_glyph_symbolizer.cpp deleted file mode 100644 index 4a82cdb4b..000000000 --- a/src/agg/process_glyph_symbolizer.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ -//$Id$ - -// mapnik -#include - -// agg -#include "agg_basics.h" -#include "agg_rendering_buffer.h" -#include "agg_pixfmt_rgba.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_scanline_u.h" - -namespace mapnik { - -template -void agg_renderer::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) -{ - face_set_ptr faces = font_manager_.get_face_set(sym.get_face_name()); - stroker_ptr strk = font_manager_.get_stroker(); - if (faces->size() > 0 && strk) - { - // Get x and y from geometry and translate to pixmap coords. - double x, y, z=0.0; - feature.get_geometry(0).label_position(&x, &y); - prj_trans.backward(x,y,z); - t_.forward(&x, &y); - - text_renderer ren(pixmap_, faces, *strk); - - // set fill and halo colors - color fill = sym.eval_color(feature); - ren.set_fill(fill); - if (fill != color("transparent")) { - ren.set_halo_fill(sym.get_halo_fill()); - ren.set_halo_radius(sym.get_halo_radius() * scale_factor_); - } - - // set font size - float size = sym.eval_size(feature); - ren.set_character_size(size * scale_factor_); - faces->set_character_sizes(size * scale_factor_); - - // Get and render text path - // - text_path_ptr path = sym.get_text_path(faces, feature); - // apply displacement - position pos = sym.get_displacement(); - double dx = boost::get<0>(pos); - double dy = boost::get<1>(pos); - path->starting_x = x = x+dx; - path->starting_y = y = y+dy; - - // Prepare glyphs to set internal state and calculate the marker's - // final box so we can check for a valid placement - box2d dim = ren.prepare_glyphs(path.get()); - box2d ext(x-dim.width()/2, y-dim.height()/2, x+dim.width()/2, y+dim.height()/2); - if ((sym.get_allow_overlap() || detector_->has_placement(ext)) && - (!sym.get_avoid_edges() || detector_->extent().contains(ext))) - { - // Placement is valid, render glyph and update detector. - ren.render(x, y); - detector_->insert(ext); - metawriter_with_properties writer = sym.get_metawriter(); - if (writer.first) writer.first->add_box(ext, feature, t_, writer.second); - } - } - else - { - throw config_error( - "Unable to find specified font face in GlyphSymbolizer" - ); - } -} -template void agg_renderer::process(glyph_symbolizer const&, - Feature const&, - proj_transform const&); -} diff --git a/src/agg/process_line_pattern_symbolizer.cpp b/src/agg/process_line_pattern_symbolizer.cpp index 17f9cd191..11c633259 100644 --- a/src/agg/process_line_pattern_symbolizer.cpp +++ b/src/agg/process_line_pattern_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_basics.h" diff --git a/src/agg/process_line_symbolizer.cpp b/src/agg/process_line_symbolizer.cpp index eaef2e7e8..df0c977b3 100644 --- a/src/agg/process_line_symbolizer.cpp +++ b/src/agg/process_line_symbolizer.cpp @@ -24,6 +24,7 @@ // mapnik #include #include +#include // agg #include "agg_basics.h" diff --git a/src/agg/process_markers_symbolizer.cpp b/src/agg/process_markers_symbolizer.cpp index abe8983b8..a4dc9de28 100644 --- a/src/agg/process_markers_symbolizer.cpp +++ b/src/agg/process_markers_symbolizer.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "agg_basics.h" #include "agg_rendering_buffer.h" diff --git a/src/agg/process_polygon_symbolizer.cpp b/src/agg/process_polygon_symbolizer.cpp index 2427cdc3f..991d1018a 100644 --- a/src/agg/process_polygon_symbolizer.cpp +++ b/src/agg/process_polygon_symbolizer.cpp @@ -24,6 +24,7 @@ // mapnik #include #include +#include // agg #include "agg_basics.h" diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index 7d2c02741..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,223 +41,26 @@ void agg_renderer::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - 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); - - 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); } } } diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index e460fa322..d693c15e7 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -24,7 +24,7 @@ // mapnik #include #include -#include +#include namespace mapnik { @@ -33,147 +33,23 @@ void agg_renderer::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { + 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())); - // Use a boost::ptr_vector here instread of std::vector? - std::vector geometries_to_process; - unsigned num_geom = feature.num_geometries(); - for (unsigned i=0; i 0) + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - // 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)); - } - - if (!geometries_to_process.size() > 0) - return; // early return to avoid significant overhead of rendering setup - - typedef coord_transform2 path_type; - - bool placement_found = false; - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - while (!placement_found && placement_options->next()) - { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - UnicodeString 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); - } - - if ( text.length() <= 0 ) continue; - color const& fill = sym.get_fill(); - - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) - { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } - - stroker_ptr strk = font_manager_.get_stroker(); - if (!(faces->size() > 0 && strk)) - { - throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'"); - } - text_renderer ren(pixmap_, faces, *strk); - - ren.set_character_size(placement_options->text_size * scale_factor_); - ren.set_fill(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()); - - box2d dims(0,0,width_,height_); - placement_finder finder(*detector_,dims); - - string_info info(text); - - faces->get_string_info(info); - metawriter_with_properties writer = sym.get_metawriter(); - - BOOST_FOREACH( geometry_type * geom, geometries_to_process ) - { - while (!placement_found && placement_options->next_position_only()) - { - placement text_placement(info, sym, scale_factor_); - text_placement.avoid_edges = sym.get_avoid_edges(); - if (writer.first) - text_placement.collect_extents =true; // needed for inmem metawriter - - if (sym.get_label_placement() == POINT_PLACEMENT || - sym.get_label_placement() == INTERIOR_PLACEMENT) - { - double label_x=0.0; - double label_y=0.0; - double z=0.0; - if (sym.get_label_placement() == POINT_PLACEMENT) - geom->label_position(&label_x, &label_y); - else - geom->label_interior_position(&label_x, &label_y); - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - double angle = 0.0; - expression_ptr angle_expr = sym.get_orientation(); - if (angle_expr) - { - // apply rotation - value_type result = boost::apply_visitor(evaluate(feature),*angle_expr); - angle = result.to_double(); - } - - finder.find_point_placement(text_placement, placement_options, label_x,label_y, - angle, sym.get_line_spacing(), - sym.get_character_spacing()); - finder.update_detector(text_placement); - } - else if ( geom->num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT) - { - path_type path(t_,*geom,prj_trans); - finder.find_line_placements(text_placement, placement_options, path); - } - - if (!text_placement.placements.size()) continue; - placement_found = true; - - 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; - ren.prepare_glyphs(&text_placement.placements[ii]); - ren.render(x,y); - } - - 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); } } } diff --git a/src/build.py b/src/build.py index 62e782106..1520e9dd9 100644 --- a/src/build.py +++ b/src/build.py @@ -143,13 +143,14 @@ source = Split( memory_datasource.cpp stroke.cpp symbolizer.cpp + symbolizer_helpers.cpp arrow.cpp unicode.cpp - glyph_symbolizer.cpp markers_symbolizer.cpp metawriter.cpp raster_colorizer.cpp text_placements.cpp + text_processing.cpp wkt/wkt_factory.cpp metawriter_inmem.cpp metawriter_factory.cpp @@ -221,7 +222,6 @@ source += Split( """ agg/agg_renderer.cpp agg/process_building_symbolizer.cpp - agg/process_glyph_symbolizer.cpp agg/process_line_symbolizer.cpp agg/process_line_pattern_symbolizer.cpp agg/process_text_symbolizer.cpp @@ -242,7 +242,6 @@ source += Split( """ grid/grid_renderer.cpp grid/process_building_symbolizer.cpp - grid/process_glyph_symbolizer.cpp grid/process_line_pattern_symbolizer.cpp grid/process_line_symbolizer.cpp grid/process_markers_symbolizer.cpp @@ -262,7 +261,6 @@ if env['SVG_RENDERER']: # svg backend svg/svg_output_attributes.cpp svg/process_symbolizers.cpp svg/process_building_symbolizer.cpp - svg/process_glyph_symbolizer.cpp svg/process_line_pattern_symbolizer.cpp svg/process_line_symbolizer.cpp svg/process_markers_symbolizer.cpp diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 1609aed72..9be77f314 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -572,11 +573,7 @@ public: void add_text(text_path & path, cairo_face_manager & manager, - face_set_ptr const& faces, - unsigned text_size, - color const& fill, - unsigned halo_radius, - color const& halo_fill) + face_manager &font_manager) { double sx = path.starting_x; double sy = path.starting_y; @@ -587,8 +584,13 @@ public: { int c; double x, y, angle; + char_properties *format; - path.vertex(&c, &x, &y, &angle); + path.vertex(&c, &x, &y, &angle, &format); + + face_set_ptr faces = font_manager.get_face_set(format->face_name, format->fontset); + float text_size = format->text_size; + faces->set_character_sizes(text_size); glyph_ptr glyph = faces->get_glyph(c); @@ -608,42 +610,11 @@ public: set_font_face(manager, glyph->get_face()); glyph_path(glyph->get_index(), sx + x, sy - y); - } - } - - set_line_width(halo_radius); - set_line_join(ROUND_JOIN); - set_color(halo_fill); - stroke(); - - set_color(fill); - - path.rewind(); - - for (int iii = 0; iii < path.num_nodes(); iii++) - { - int c; - double x, y, angle; - - path.vertex(&c, &x, &y, &angle); - - glyph_ptr glyph = faces->get_glyph(c); - - if (glyph) - { - Cairo::Matrix matrix; - - matrix.xx = text_size * cos(angle); - matrix.xy = text_size * sin(angle); - matrix.yx = text_size * -sin(angle); - matrix.yy = text_size * cos(angle); - matrix.x0 = 0; - matrix.y0 = 0; - - set_font_matrix(matrix); - - set_font_face(manager, glyph->get_face()); - + set_line_width(format->halo_radius); + set_line_join(ROUND_JOIN); + set_color(format->halo_fill); + stroke(); + set_color(format->fill); show_glyph(glyph->get_index(), sx + x, sy - y); } } @@ -1080,204 +1051,24 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - 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(); - placement_options->next_position_only(); + 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); - - 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_); } } } @@ -1459,186 +1250,18 @@ void cairo_renderer_base::process(markers_symbolizer const& sym, } } -void cairo_renderer_base::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) -{ - face_set_ptr faces = font_manager_.get_face_set(sym.get_face_name()); - if (faces->size() > 0) - { - // Get x and y from geometry and translate to pixmap coords. - double x, y, z=0.0; - feature.get_geometry(0).label_position(&x, &y); - prj_trans.backward(x,y,z); - t_.forward(&x, &y); - - // set font size - unsigned size = sym.eval_size(feature); - faces->set_character_sizes(size); - - // Get and render text path - // - text_path_ptr path = sym.get_text_path(faces, feature); - // apply displacement - position pos = sym.get_displacement(); - double dx = boost::get<0>(pos); - double dy = boost::get<1>(pos); - path->starting_x = x = x+dx; - path->starting_y = y = y+dy; - - // get fill and halo params - color fill = sym.eval_color(feature); - color halo_fill = sym.get_halo_fill(); - double halo_radius = sym.get_halo_radius(); - if (fill==color("transparent")) - halo_radius = 0; - - double bsize = size/2 + 1; - box2d glyph_ext( - floor(x-bsize), floor(y-bsize), ceil(x+bsize), ceil(y+bsize) - ); - if ((sym.get_allow_overlap() || detector_.has_placement(glyph_ext)) && - (!sym.get_avoid_edges() || detector_.extent().contains(glyph_ext))) - { - // Placement is valid, render glyph and update detector. - cairo_context context(context_); - context.add_text(*path, - face_manager_, - faces, - size, - fill, - halo_radius, - halo_fill - ); - detector_.insert(glyph_ext); - metawriter_with_properties writer = sym.get_metawriter(); - if (writer.first) writer.first->add_box(glyph_ext, feature, t_, writer.second); - } - } - else - { - throw config_error( - "Unable to find specified font face in GlyphSymbolizer" - ); - } -} - void cairo_renderer_base::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - typedef coord_transform2 path_type; + 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_); - bool placement_found = false; - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - while (!placement_found && placement_options->next()) - { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - UnicodeString text = result.to_unicode(); - - if ( sym.get_text_transform() == UPPERCASE) + cairo_context context(context_); + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(NULL); - } - - if (text.length() <= 0) continue; - - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) - { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } - - if (faces->size() == 0) - { - throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'"); - } - cairo_context context(context_); - string_info info(text); - - faces->set_character_sizes(placement_options->text_size); - faces->get_string_info(info); - - placement_finder finder(detector_); - - metawriter_with_properties writer = sym.get_metawriter(); - - unsigned num_geom = feature.num_geometries(); - for (unsigned i=0; inext_position_only()) - { - placement text_placement(info, sym, 1.0); - text_placement.avoid_edges = sym.get_avoid_edges(); - if (writer.first) - text_placement.collect_extents = true; // needed for inmem metawriter - - if (sym.get_label_placement() == POINT_PLACEMENT || - sym.get_label_placement() == INTERIOR_PLACEMENT) - { - double label_x, label_y, z=0.0; - if (sym.get_label_placement() == POINT_PLACEMENT) - geom.label_position(&label_x, &label_y); - else - geom.label_interior_position(&label_x, &label_y); - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - double angle = 0.0; - expression_ptr angle_expr = sym.get_orientation(); - if (angle_expr) - { - // apply rotation - value_type result = boost::apply_visitor(evaluate(feature),*angle_expr); - angle = result.to_double(); - } - - finder.find_point_placement(text_placement, placement_options, - label_x, label_y, - angle, sym.get_line_spacing(), - sym.get_character_spacing()); - finder.update_detector(text_placement); - } - else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT) - { - path_type path(t_, geom, prj_trans); - finder.find_line_placements(text_placement, placement_options, path); - } - - if (!text_placement.placements.size()) continue; - placement_found = true; - - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ii) - { - 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_text(text_placement, faces, feature, t_, writer.second); - } + context.add_text(placement->placements[ii], face_manager_, font_manager_); } } } diff --git a/src/font_engine_freetype.cpp b/src/font_engine_freetype.cpp index 188aaa59d..5e58fa89c 100644 --- a/src/font_engine_freetype.cpp +++ b/src/font_engine_freetype.cpp @@ -19,10 +19,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -//$Id$ // mapnik #include +#include +#include +#include // boost #include @@ -192,9 +194,10 @@ stroker_ptr freetype_engine::create_stroker() return stroker_ptr(); } -font_face_set::dimension_t font_face_set::character_dimensions(const unsigned c) +char_info font_face_set::character_dimensions(const unsigned c) { - std::map::const_iterator itr; + //Check if char is already in cache + std::map::const_iterator itr; itr = dimension_cache_.find(c); if (itr != dimension_cache_.end()) { return itr->second; @@ -222,33 +225,30 @@ font_face_set::dimension_t font_face_set::character_dimensions(const unsigned c) error = FT_Load_Glyph (face, glyph->get_index(), FT_LOAD_NO_HINTING); if ( error ) - return dimension_t(0, 0, 0); + return char_info(); error = FT_Get_Glyph(face->glyph, &image); if ( error ) - return dimension_t(0, 0, 0); + return char_info(); FT_Glyph_Get_CBox(image, ft_glyph_bbox_pixels, &glyph_bbox); FT_Done_Glyph(image); unsigned tempx = face->glyph->advance.x >> 6; - //std::clog << "glyph: " << glyph_index << " x: " << tempx << " y: " << tempy << std::endl; - dimension_t dim(tempx, glyph_bbox.yMax, glyph_bbox.yMin); - //dimension_cache_[c] = dim; would need an default constructor for dimension_t - dimension_cache_.insert(std::pair(c, dim)); + char_info dim(c, tempx, glyph_bbox.yMax, glyph_bbox.yMin, face->size->metrics.height/64.0 /* >> 6 */); + dimension_cache_.insert(std::pair(c, dim)); return dim; } -void font_face_set::get_string_info(string_info & info) + +void font_face_set::get_string_info(string_info & info, UnicodeString const& ustr, char_properties *format) { - unsigned width = 0; - unsigned height = 0; + double avg_height = character_dimensions('X').height(); UErrorCode err = U_ZERO_ERROR; UnicodeString reordered; UnicodeString shaped; - UnicodeString const& ustr = info.get_string(); int32_t length = ustr.length(); UBiDi *bidi = ubidi_openSized(length, 0, &err); @@ -270,10 +270,10 @@ void font_face_set::get_string_info(string_info & info) StringCharacterIterator iter(shaped); for (iter.setToStart(); iter.hasNext();) { UChar ch = iter.nextPostInc(); - dimension_t char_dim = character_dimensions(ch); - info.add_info(ch, char_dim.width, char_dim.height); - width += char_dim.width; - height = (char_dim.height > height) ? char_dim.height : height; + char_info char_dim = character_dimensions(ch); + char_dim.format = format; + char_dim.avg_height = avg_height; + info.add_info(char_dim); } } @@ -286,11 +286,198 @@ void font_face_set::get_string_info(string_info & info) #endif ubidi_close(bidi); - info.set_dimensions(width, height); +} + +template +text_renderer::text_renderer (pixmap_type & pixmap, face_manager &font_manager_, stroker & s) + : pixmap_(pixmap), + font_manager_(font_manager_), + stroker_(s) +{ + +} + +template +box2d text_renderer::prepare_glyphs(text_path *path) +{ + //clear glyphs + glyphs_.clear(); + + FT_Matrix matrix; + FT_Vector pen; + FT_Error error; + + FT_BBox bbox; + bbox.xMin = bbox.yMin = 32000; // Initialize these so we can tell if we + bbox.xMax = bbox.yMax = -32000; // properly grew the bbox later + + for (int i = 0; i < path->num_nodes(); i++) + { + int c; + double x, y, angle; + char_properties *properties; + + path->vertex(&c, &x, &y, &angle, &properties); + +#ifdef MAPNIK_DEBUG + // TODO Enable when we have support for setting verbosity + //std::clog << "prepare_glyphs: " << c << "," << x << + // "," << y << "," << angle << std::endl; +#endif + + FT_BBox glyph_bbox; + FT_Glyph image; + + pen.x = int(x * 64); + pen.y = int(y * 64); + + face_set_ptr faces = font_manager_.get_face_set(properties->face_name, properties->fontset); + faces->set_character_sizes(properties->text_size); + + glyph_ptr glyph = faces->get_glyph(unsigned(c)); + FT_Face face = glyph->get_face()->get_face(); + + matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L ); + matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L ); + matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L ); + matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L ); + + FT_Set_Transform(face, &matrix, &pen); + + error = FT_Load_Glyph(face, glyph->get_index(), FT_LOAD_NO_HINTING); + if ( error ) + continue; + + error = FT_Get_Glyph(face->glyph, &image); + if ( error ) + continue; + + FT_Glyph_Get_CBox(image,ft_glyph_bbox_pixels, &glyph_bbox); + if (glyph_bbox.xMin < bbox.xMin) + bbox.xMin = glyph_bbox.xMin; + if (glyph_bbox.yMin < bbox.yMin) + bbox.yMin = glyph_bbox.yMin; + if (glyph_bbox.xMax > bbox.xMax) + bbox.xMax = glyph_bbox.xMax; + if (glyph_bbox.yMax > bbox.yMax) + bbox.yMax = glyph_bbox.yMax; + + // Check if we properly grew the bbox + if ( bbox.xMin > bbox.xMax ) + { + bbox.xMin = 0; + bbox.yMin = 0; + bbox.xMax = 0; + bbox.yMax = 0; + } + + // take ownership of the glyph + glyphs_.push_back(new glyph_t(image, properties)); + } + + return box2d(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); +} + +template +void text_renderer::render(double x0, double y0) +{ + FT_Error error; + FT_Vector start; + unsigned height = pixmap_.height(); + + start.x = static_cast(x0 * (1 << 6)); + start.y = static_cast((height - y0) * (1 << 6)); + + // now render transformed glyphs + typename glyphs_t::iterator pos; + for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) + { + double halo_radius = pos->properties->halo_radius; + //make sure we've got reasonable values. + if (halo_radius <= 0.0 || halo_radius > 1024.0) continue; + stroker_.init(halo_radius); + FT_Glyph g; + error = FT_Glyph_Copy(pos->image, &g); + if (!error) + { + FT_Glyph_Transform(g,0,&start); + FT_Glyph_Stroke(&g,stroker_.get(),1); + error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); + if ( ! error ) + { + + FT_BitmapGlyph bit = (FT_BitmapGlyph)g; + render_bitmap(&bit->bitmap, pos->properties->halo_fill.rgba(), + bit->left, + height - bit->top, pos->properties->text_opacity); + } + } + FT_Done_Glyph(g); + } + //render actual text + for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) + { + + FT_Glyph_Transform(pos->image,0,&start); + + error = FT_Glyph_To_Bitmap( &(pos->image),FT_RENDER_MODE_NORMAL,0,1); + if ( ! error ) + { + + FT_BitmapGlyph bit = (FT_BitmapGlyph)pos->image; + render_bitmap(&bit->bitmap, pos->properties->fill.rgba(), + bit->left, + height - bit->top, pos->properties->text_opacity); + } + } +} + + +template +void text_renderer::render_id(int feature_id,double x0, double y0, double min_radius) +{ + FT_Error error; + FT_Vector start; + unsigned height = pixmap_.height(); + + start.x = static_cast(x0 * (1 << 6)); + start.y = static_cast((height - y0) * (1 << 6)); + + // now render transformed glyphs + typename glyphs_t::iterator pos; + for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) + { + stroker_.init(std::max(pos->properties->halo_radius, min_radius)); + FT_Glyph g; + error = FT_Glyph_Copy(pos->image, &g); + if (!error) + { + FT_Glyph_Transform(g,0,&start); + FT_Glyph_Stroke(&g,stroker_.get(),1); + error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_NORMAL,0,1); + //error = FT_Glyph_To_Bitmap( &g,FT_RENDER_MODE_MONO,0,1); + if ( ! error ) + { + + FT_BitmapGlyph bit = (FT_BitmapGlyph)g; + render_bitmap_id(&bit->bitmap, feature_id, + bit->left, + height - bit->top); + } + } + FT_Done_Glyph(g); + } } #ifdef MAPNIK_THREADSAFE boost::mutex freetype_engine::mutex_; #endif std::map > freetype_engine::name2file_; +template void text_renderer::render(double, double); +template text_renderer::text_renderer(image_32&, face_manager&, stroker&); +template box2dtext_renderer::prepare_glyphs(text_path*); + +template void text_renderer::render_id(int, double, double, double); +template text_renderer::text_renderer(grid&, face_manager&, stroker&); +template box2dtext_renderer::prepare_glyphs(text_path*); } diff --git a/src/glyph_symbolizer.cpp b/src/glyph_symbolizer.cpp deleted file mode 100644 index 8ef41c638..000000000 --- a/src/glyph_symbolizer.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include -#include - -namespace mapnik -{ - -static const char * angle_mode_strings[] = { - "azimuth", - "trigonometric", - "" -}; - -IMPLEMENT_ENUM( angle_mode_e, angle_mode_strings ) - -text_path_ptr glyph_symbolizer::get_text_path(face_set_ptr const& faces, - Feature const& feature) const -{ - // Try to evaulate expressions against feature - UnicodeString char_ = eval_char(feature); - double angle = eval_angle(feature); - - // calculate displacement so glyph is rotated along center (default pivot is - // lowerbottom corner) - string_info info(char_); - faces->get_string_info(info); - - // XXX: Perhaps this limitation can be overcomed? - if (info.num_characters() != 1) - { - throw config_error("'char' length must be exactly 1"); - } - - character_info ci = info.at(0); - font_face_set::dimension_t cdim = faces->character_dimensions(ci.character); - double cwidth = static_cast(cdim.width)/2.0; - double cheight = static_cast(cdim.height)/2.0; - double xoff = cwidth*cos(angle) - cheight*sin(angle); - double yoff = cwidth*sin(angle) + cheight*cos(angle); - - // Create text path and add character with displacement and angle - text_path_ptr path_ptr = text_path_ptr(new text_path()); - path_ptr->add_node(ci.character, -xoff, -yoff, angle); - return path_ptr; -} - - - -UnicodeString glyph_symbolizer::eval_char(Feature const& feature) const -{ - expression_ptr expr = get_char(); - if (!expr) - throw config_error("No 'char' expression"); - value_type result = boost::apply_visitor( - evaluate(feature), - *expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "char_result=" << result.to_string() << "\n"; -#endif - return result.to_unicode(); -} - -double glyph_symbolizer::eval_angle(Feature const& feature) const -{ - double angle = 0.0; - expression_ptr expr = get_angle(); - if (expr) { - value_type result = boost::apply_visitor( - evaluate(feature), - *expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "angle_result=" << result.to_string() << "\n"; -#endif - angle = result.to_double(); - // normalize to first rotation in case an expression has made it go past - angle = std::fmod(angle, 360); - angle *= (M_PI/180); // convert to radians - if (get_angle_mode()==AZIMUTH) { - // angle is an azimuth, convert into trigonometric angle - angle = std::atan2(std::cos(angle), std::sin(angle)); - } - if (angle<0) - angle += 2*M_PI; - } - return angle; -} - -unsigned glyph_symbolizer::eval_size(Feature const& feature) const -{ - expression_ptr expr = get_size(); - if (!expr) throw config_error("No 'size' expression"); - value_type result = boost::apply_visitor( - evaluate(feature), - *expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "size_result=" << result.to_string() << "\n"; -#endif - float size = static_cast(result.to_double()); -#ifdef MAPNIK_DEBUG - std::clog << "size=" << size << "\n"; -#endif - return size; -} - -color glyph_symbolizer::eval_color(Feature const& feature) const -{ - raster_colorizer_ptr colorizer = get_colorizer(); - if (colorizer) - { - expression_ptr value_expr = get_value(); - if (!value_expr) - { - throw config_error( - "Must define a 'value' expression to use a colorizer" - ); - } - value_type value_result = boost::apply_visitor( - evaluate(feature), - *value_expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "value_result=" << value_result.to_string() << "\n"; -#endif - return colorizer->get_color((float)value_result.to_double()); - } - else - { - expression_ptr color_expr = get_color(); - if (color_expr) - { - value_type color_result = boost::apply_visitor( - evaluate(feature), - *color_expr - ); -#ifdef MAPNIK_DEBUG - std::clog << "color_result=" << color_result.to_string() << "\n"; -#endif - return color(color_result.to_string()); - } - else - { - return color(0,0,0); // black - } - } -} - -} // end mapnik namespace diff --git a/src/grid/grid_renderer.cpp b/src/grid/grid_renderer.cpp index cf6c645d9..c4bada5c7 100644 --- a/src/grid/grid_renderer.cpp +++ b/src/grid/grid_renderer.cpp @@ -28,6 +28,7 @@ #include #include + #include #include #include @@ -35,10 +36,12 @@ #include #include #include +#include #include #include #include + // boost #include diff --git a/src/grid/process_line_pattern_symbolizer.cpp b/src/grid/process_line_pattern_symbolizer.cpp index f7510e35b..1d8a9c577 100644 --- a/src/grid/process_line_pattern_symbolizer.cpp +++ b/src/grid/process_line_pattern_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_line_symbolizer.cpp b/src/grid/process_line_symbolizer.cpp index b04999bff..212da4a1e 100644 --- a/src/grid/process_line_symbolizer.cpp +++ b/src/grid/process_line_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_markers_symbolizer.cpp b/src/grid/process_markers_symbolizer.cpp index 347324326..ba4bed42d 100644 --- a/src/grid/process_markers_symbolizer.cpp +++ b/src/grid/process_markers_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/src/grid/process_point_symbolizer.cpp b/src/grid/process_point_symbolizer.cpp index 88f94a7d8..e3fd66e1c 100644 --- a/src/grid/process_point_symbolizer.cpp +++ b/src/grid/process_point_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/src/grid/process_polygon_pattern_symbolizer.cpp b/src/grid/process_polygon_pattern_symbolizer.cpp index 0f3b63f8b..7cce368ca 100644 --- a/src/grid/process_polygon_pattern_symbolizer.cpp +++ b/src/grid/process_polygon_pattern_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_polygon_symbolizer.cpp b/src/grid/process_polygon_symbolizer.cpp index 6b410a4eb..0beaab3f7 100644 --- a/src/grid/process_polygon_symbolizer.cpp +++ b/src/grid/process_polygon_symbolizer.cpp @@ -27,6 +27,7 @@ #include #include #include +#include // agg #include "agg_rasterizer_scanline_aa.h" diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index d0f61cd67..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,200 +44,36 @@ void grid_renderer::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - 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); - - // 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); - } 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 e88aed6c5..ae61d13d4 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -23,8 +23,7 @@ // mapnik #include -#include -#include +#include namespace mapnik { @@ -33,120 +32,29 @@ void grid_renderer::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - typedef coord_transform2 path_type; - + text_symbolizer_helper, + label_collision_detector4> helper( + sym, feature, prj_trans, + width_, height_, + scale_factor_ * (1.0/pixmap_.get_resolution()), + t_, font_manager_, detector_); bool placement_found = false; - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - while (!placement_found && placement_options->next()) - { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - UnicodeString text = result.to_unicode(); - if ( sym.get_text_transform() == UPPERCASE) + text_renderer ren(pixmap_, font_manager_, *(font_manager_.get_stroker())); + + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + placement_found = true; + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(NULL); - } - - if ( text.length() <= 0 ) continue; - color const& fill = sym.get_fill(); - - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) - { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } - - stroker_ptr strk = font_manager_.get_stroker(); - if (!(faces->size() > 0 && strk)) - { - throw config_error("Unable to find specified font face '" + sym.get_face_name() + "'"); - } - text_renderer ren(pixmap_, faces, *strk); - ren.set_character_size(placement_options->text_size * (scale_factor_ * (1.0/pixmap_.get_resolution()))); - ren.set_fill(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()); - - // /pixmap_.get_resolution() ? - box2d dims(0,0,width_,height_); - placement_finder finder(detector_,dims); - - string_info info(text); - - faces->get_string_info(info); - unsigned num_geom = feature.num_geometries(); - for (unsigned i=0; inext_position_only()) - { - placement text_placement(info, sym, scale_factor_); - text_placement.avoid_edges = sym.get_avoid_edges(); - if (sym.get_label_placement() == POINT_PLACEMENT || - sym.get_label_placement() == INTERIOR_PLACEMENT) - { - double label_x, label_y, z=0.0; - if (sym.get_label_placement() == POINT_PLACEMENT) - geom.label_position(&label_x, &label_y); - else - geom.label_interior_position(&label_x, &label_y); - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - double angle = 0.0; - expression_ptr angle_expr = sym.get_orientation(); - if (angle_expr) - { - // apply rotation - value_type result = boost::apply_visitor(evaluate(feature),*angle_expr); - angle = result.to_double(); - } - - finder.find_point_placement(text_placement, placement_options, - label_x, label_y, - angle, sym.get_line_spacing(), - sym.get_character_spacing()); - - finder.update_detector(text_placement); - } - else if ( geom.num_points() > 1 && sym.get_label_placement() == LINE_PLACEMENT) - { - path_type path(t_,geom,prj_trans); - finder.find_line_placements(text_placement, placement_options, path); - } - - if (!text_placement.placements.size()) continue; - placement_found = true; - - 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; - ren.prepare_glyphs(&text_placement.placements[ii]); - ren.render_id(feature.id(),x,y,2); - } - } + 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); } } - if (placement_found) - pixmap_.add_feature(feature); + if (placement_found) pixmap_.add_feature(feature); + } template void grid_renderer::process(text_symbolizer const&, diff --git a/src/libxml2_loader.cpp b/src/libxml2_loader.cpp index f935243ce..fedfbfd44 100644 --- a/src/libxml2_loader.cpp +++ b/src/libxml2_loader.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -200,8 +201,13 @@ private: } break; case XML_TEXT_NODE: - pt.put_value( (char*) cur_node->content ); - break; + { + std::string trimmed = boost::algorithm::trim_copy(std::string((char*)cur_node->content)); + if (trimmed.empty()) break; + ptree::iterator it = pt.push_back(ptree::value_type("", ptree())); + it->second.put_value(trimmed); + } + break; case XML_COMMENT_NODE: { ptree::iterator it = pt.push_back( diff --git a/src/load_map.cpp b/src/load_map.cpp index 41c07c1d4..b6ddae541 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -48,6 +48,10 @@ #include #include +#include +#include +#include +#include // boost #include @@ -108,7 +112,6 @@ private: void parse_building_symbolizer(rule & rule, ptree const & sym ); void parse_raster_symbolizer(rule & rule, ptree const & sym ); void parse_markers_symbolizer(rule & rule, ptree const & sym ); - void parse_glyph_symbolizer(rule & rule, ptree const & sym ); void parse_raster_colorizer(raster_colorizer_ptr const& rc, ptree const& node ); void parse_stroke(stroke & strk, ptree const & sym); @@ -653,7 +656,7 @@ void map_parser::parse_layer( Map & map, ptree const & lay ) if (child.first == "StyleName") { ensure_attrs(child.second, "StyleName", "none"); - std::string style_name = child.second.data(); + std::string style_name = get_value(child.second, "style name"); if (style_name.empty()) { std::ostringstream ss; @@ -846,10 +849,6 @@ void map_parser::parse_rule( feature_type_style & style, ptree const & r ) { parse_markers_symbolizer(rule, sym.second); } - else if ( sym.first == "GlyphSymbolizer") - { - parse_glyph_symbolizer( rule, sym.second ); - } else if ( sym.first != "MinScaleDenominator" && sym.first != "MaxScaleDenominator" && @@ -1246,28 +1245,34 @@ void map_parser::parse_polygon_pattern_symbolizer( rule & rule, void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym ) { - std::stringstream s; - s << "name,face-name,fontset-name,size,fill,orientation," + std::stringstream s_common; + 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," - << "placements,placement-type," + << "horizontal-alignment,justify-alignment"; + + std::stringstream s_symbolizer; + s_symbolizer << s_common.str() << ",placements,placement-type," << "meta-writer,meta-output"; - ensure_attrs(sym, "TextSymbolizer", s.str()); + ensure_attrs(sym, "TextSymbolizer", s_symbolizer.str()); try { 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+"'")); } @@ -1276,214 +1281,26 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym ) placement_finder = text_placements_ptr(new text_placements_dummy()); } - std::string name; - optional old_name = get_opt_attr(sym, "name"); - if (old_name) { - std::clog << ": ### WARNING: Using 'name' in TextSymbolizer is deprecated (http://trac.mapnik.org/wiki/TextSymbolizer)\n"; - name = *old_name; - } else { - name = get_value(sym, "TextSymbolizer"); - if (name.empty()) throw config_error(std::string("TextSymbolizer needs a non-empty text")); - } - - optional face_name = - get_opt_attr(sym, "face-name"); - - optional fontset_name = - get_opt_attr(sym, "fontset-name"); - - float size = get_attr(sym, "size", 10.0f); - - color c = get_attr(sym, "fill", color(0,0,0)); - - text_symbolizer text_symbol = text_symbolizer(parse_expression(name, "utf8"), size, c, placement_finder); - - optional orientation = get_opt_attr(sym, "orientation"); - if (orientation) - { - text_symbol.set_orientation(parse_expression(*orientation, "utf8")); - } - - if (fontset_name && face_name) - { - throw config_error(std::string("Can't have both face-name and fontset-name")); - } - else if (fontset_name) - { - std::map::const_iterator itr = fontsets_.find(*fontset_name); - if (itr != fontsets_.end()) - { - text_symbol.set_fontset(itr->second); - } - else - { - throw config_error("Unable to find any fontset named '" + *fontset_name + "'"); + placement_finder->properties.from_xml(sym, fontsets_); + if (strict_) ensure_font_face(placement_finder->properties.default_format.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.str()); + text_symbolizer_properties & p = list->add(); + p.from_xml(symIter->second, fontsets_); + if (strict_) ensure_font_face(p.default_format.face_name); } } - else if (face_name) - { - if ( strict_ ) - { - ensure_font_face(*face_name); - } - text_symbol.set_face_name(*face_name); - } - else - { - throw config_error(std::string("Must have face-name or fontset-name")); - } - - double dx = get_attr(sym, "dx", 0.0); - double dy = get_attr(sym, "dy", 0.0); - text_symbol.set_displacement(dx,dy); - - label_placement_e placement = - get_attr(sym, "placement", POINT_PLACEMENT); - text_symbol.set_label_placement( placement ); - - // vertical alignment - vertical_alignment_e default_vertical_alignment = V_AUTO; - - vertical_alignment_e valign = get_attr(sym, "vertical-alignment", default_vertical_alignment); - text_symbol.set_vertical_alignment(valign); - - // halo fill and radius - optional halo_fill = get_opt_attr(sym, "halo-fill"); - if (halo_fill) - { - text_symbol.set_halo_fill( * halo_fill ); - } - optional halo_radius = - get_opt_attr(sym, "halo-radius"); - if (halo_radius) - { - text_symbol.set_halo_radius(*halo_radius); - } - - // text ratio and wrap width - optional text_ratio = - get_opt_attr(sym, "text-ratio"); - if (text_ratio) - { - text_symbol.set_text_ratio(*text_ratio); - } - - optional wrap_width = - get_opt_attr(sym, "wrap-width"); - if (wrap_width) - { - text_symbol.set_wrap_width(*wrap_width); - } - - optional wrap_before = - get_opt_attr(sym, "wrap-before"); - if (wrap_before) - { - text_symbol.set_wrap_before(*wrap_before); - } - - // character used to break long strings - optional wrap_char = - get_opt_attr(sym, "wrap-character"); - if (wrap_char && (*wrap_char).size() > 0) - { - text_symbol.set_wrap_char((*wrap_char)[0]); - } - - // text conversion before rendering - text_transform_e tconvert = - get_attr(sym, "text-transform", NONE); - text_symbol.set_text_transform(tconvert); - - // spacing between text lines - optional line_spacing = get_opt_attr(sym, "line-spacing"); - if (line_spacing) - { - text_symbol.set_line_spacing(*line_spacing); - } - - // tolerance between label spacing along line - optional label_position_tolerance = get_opt_attr(sym, "label-position-tolerance"); - if (label_position_tolerance) - { - text_symbol.set_label_position_tolerance(*label_position_tolerance); - } - - // spacing between characters in text - optional character_spacing = get_opt_attr(sym, "character-spacing"); - if (character_spacing) - { - text_symbol.set_character_spacing(*character_spacing); - } - - // spacing between repeated labels on lines - optional spacing = get_opt_attr(sym, "spacing"); - if (spacing) - { - text_symbol.set_label_spacing(*spacing); - } - - // minimum distance between labels - optional min_distance = get_opt_attr(sym, "minimum-distance"); - if (min_distance) - { - text_symbol.set_minimum_distance(*min_distance); - } - - // minimum distance from edge of the map - optional min_padding = get_opt_attr(sym, "minimum-padding"); - if (min_padding) - { - text_symbol.set_minimum_padding(*min_padding); - } - - // minimum path length - optional min_path_length = get_opt_attr(sym, "minimum-path-length"); - if (min_path_length) - { - text_symbol.set_minimum_path_length(*min_path_length); - } - - // do not render labels around edges - optional avoid_edges = - get_opt_attr(sym, "avoid-edges"); - if (avoid_edges) - { - text_symbol.set_avoid_edges( * avoid_edges); - } - - // allow_overlap - optional allow_overlap = - get_opt_attr(sym, "allow-overlap"); - if (allow_overlap) - { - text_symbol.set_allow_overlap( * allow_overlap ); - } - - // opacity - optional opacity = - get_opt_attr(sym, "opacity"); - if (opacity) - { - text_symbol.set_text_opacity( * opacity ); - } - - // max_char_angle_delta - optional max_char_angle_delta = - get_opt_attr(sym, "max-char-angle-delta"); - if (max_char_angle_delta) - { - text_symbol.set_max_char_angle_delta( (*max_char_angle_delta)*(M_PI/180)); - } - - // horizontal alignment - horizontal_alignment_e halign = get_attr(sym, "horizontal-alignment", H_AUTO); - text_symbol.set_horizontal_alignment(halign); - - // justify alignment - justify_alignment_e jalign = get_attr(sym, "justify-alignment", J_MIDDLE); - text_symbol.set_justify_alignment(jalign); + text_symbolizer text_symbol = text_symbolizer(placement_finder); parse_metawriter_in_symbolizer(text_symbol, sym); rule.append(text_symbol); } @@ -1496,51 +1313,115 @@ 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; - //std::string a[] = {"a","b"}; - 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"; - - ensure_attrs(sym, "ShieldSymbolizer", s.str()); + 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_symbolizer); try { - optional no_text = - get_opt_attr(sym, "no-text"); - std::string name; - optional old_name = get_opt_attr(sym, "name"); - if (old_name) { - std::clog << ": ### WARNING: Using 'name' in ShieldSymbolizer is deprecated (http://trac.mapnik.org/wiki/TextSymbolizer)\n"; - name = *old_name; - } else { - name = get_value(sym, "ShieldSymbolizer"); - if (name.empty() && (!no_text || !*no_text) ) throw config_error(std::string("ShieldSymbolizer needs a non-empty text")); + 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()); } - optional face_name = - get_opt_attr(sym, "face-name"); + placement_finder->properties.from_xml(sym, fontsets_); + if (strict_) ensure_font_face(placement_finder->properties.default_format.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.default_format.face_name); + } + } - optional fontset_name = - get_opt_attr(sym, "fontset-name"); + shield_symbolizer shield_symbol = shield_symbolizer(placement_finder); + /* Symbolizer specific attributes. */ + optional transform_wkt = get_opt_attr(sym, "transform"); + if (transform_wkt) + { + agg::trans_affine tr; + if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr)) + { + std::stringstream ss; + ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'"; + if (strict_) + throw config_error(ss.str()); // value_error here? + else + std::clog << "### WARNING: " << ss << endl; + } + boost::array matrix; + tr.store_to(&matrix[0]); + shield_symbol.set_transform(matrix); + } + // shield displacement + double shield_dx = get_attr(sym, "shield-dx", 0.0); + double shield_dy = get_attr(sym, "shield-dy", 0.0); + shield_symbol.set_shield_displacement(shield_dx,shield_dy); - float size = get_attr(sym, "size", 10.0f); - color fill = get_attr(sym, "fill", color(0,0,0)); + // opacity + optional opacity = get_opt_attr(sym, "opacity"); + if (opacity) + { + shield_symbol.set_opacity(*opacity); + } + + // text-opacity + // TODO: Could be problematic because it is named opacity in TextSymbolizer but opacity has a diffrent meaning here. + optional text_opacity = + get_opt_attr(sym, "text-opacity"); + if (text_opacity) + { + shield_symbol.set_text_opacity( * text_opacity ); + } + + // unlock_image + optional unlock_image = + get_opt_attr(sym, "unlock-image"); + if (unlock_image) + { + shield_symbol.set_unlock_image( * unlock_image ); + } + + parse_metawriter_in_symbolizer(shield_symbol, sym); std::string image_file = get_attr(sym, "file"); optional base = get_opt_attr(sym, "base"); - - optional transform_wkt = get_opt_attr(sym, "transform"); + try { if( base ) @@ -1553,202 +1434,7 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) } image_file = ensure_relative_to_xml(image_file); - - shield_symbolizer shield_symbol(parse_expression(name, "utf8"),size,fill,parse_path(image_file)); - - if (fontset_name && face_name) - { - throw config_error(std::string("Can't have both face-name and fontset-name")); - } - else if (fontset_name) - { - std::map::const_iterator itr = fontsets_.find(*fontset_name); - if (itr != fontsets_.end()) - { - shield_symbol.set_fontset(itr->second); - } - else - { - throw config_error("Unable to find any fontset named '" + *fontset_name + "'"); - } - } - else if (face_name) - { - if ( strict_ ) - { - ensure_font_face(*face_name); - } - shield_symbol.set_face_name(*face_name); - } - else - { - throw config_error(std::string("Must have face-name or fontset-name")); - } - // text displacement (relative to shield_displacement) - double dx = get_attr(sym, "dx", 0.0); - double dy = get_attr(sym, "dy", 0.0); - shield_symbol.set_displacement(dx,dy); - // shield displacement - double shield_dx = get_attr(sym, "shield-dx", 0.0); - double shield_dy = get_attr(sym, "shield-dy", 0.0); - shield_symbol.set_shield_displacement(shield_dx,shield_dy); - - label_placement_e placement = - get_attr(sym, "placement", POINT_PLACEMENT); - shield_symbol.set_label_placement( placement ); - - // don't render shields around edges - optional avoid_edges = - get_opt_attr(sym, "avoid-edges"); - if (avoid_edges) - { - shield_symbol.set_avoid_edges( *avoid_edges); - } - - // halo fill and radius - optional halo_fill = get_opt_attr(sym, "halo-fill"); - if (halo_fill) - { - shield_symbol.set_halo_fill( * halo_fill ); - } - optional halo_radius = - get_opt_attr(sym, "halo-radius"); - if (halo_radius) - { - shield_symbol.set_halo_radius(*halo_radius); - } - - // minimum distance between labels - optional min_distance = get_opt_attr(sym, "minimum-distance"); - if (min_distance) - { - shield_symbol.set_minimum_distance(*min_distance); - } - - // minimum distance from edge of the map - optional min_padding = get_opt_attr(sym, "minimum-padding"); - if (min_padding) - { - shield_symbol.set_minimum_padding(*min_padding); - } - - // spacing between repeated labels on lines - optional spacing = get_opt_attr(sym, "spacing"); - if (spacing) - { - shield_symbol.set_label_spacing(*spacing); - } - - // allow_overlap - optional allow_overlap = - get_opt_attr(sym, "allow-overlap"); - if (allow_overlap) - { - shield_symbol.set_allow_overlap( * allow_overlap ); - } - - // vertical alignment - vertical_alignment_e valign = get_attr(sym, "vertical-alignment", V_MIDDLE); - shield_symbol.set_vertical_alignment(valign); - - // horizontal alignment - horizontal_alignment_e halign = get_attr(sym, "horizontal-alignment", H_MIDDLE); - shield_symbol.set_horizontal_alignment(halign); - - // justify alignment - justify_alignment_e jalign = get_attr(sym, "justify-alignment", J_MIDDLE); - shield_symbol.set_justify_alignment(jalign); - - optional wrap_width = - get_opt_attr(sym, "wrap-width"); - if (wrap_width) - { - shield_symbol.set_wrap_width(*wrap_width); - } - - optional wrap_before = - get_opt_attr(sym, "wrap-before"); - if (wrap_before) - { - shield_symbol.set_wrap_before(*wrap_before); - } - - // character used to break long strings - optional wrap_char = - get_opt_attr(sym, "wrap-character"); - if (wrap_char && (*wrap_char).size() > 0) - { - shield_symbol.set_wrap_char((*wrap_char)[0]); - } - - // text conversion before rendering - text_transform_e tconvert = - get_attr(sym, "text-transform", NONE); - shield_symbol.set_text_transform(tconvert); - - // spacing between text lines - optional line_spacing = get_opt_attr(sym, "line-spacing"); - if (line_spacing) - { - shield_symbol.set_line_spacing(*line_spacing); - } - - // spacing between characters in text - optional character_spacing = get_opt_attr(sym, "character-spacing"); - if (character_spacing) - { - shield_symbol.set_character_spacing(*character_spacing); - } - - // opacity - optional opacity = - get_opt_attr(sym, "opacity"); - if (opacity) - { - shield_symbol.set_opacity( * opacity ); - } - - // text-opacity - optional text_opacity = - get_opt_attr(sym, "text-opacity"); - if (text_opacity) - { - shield_symbol.set_text_opacity( * text_opacity ); - } - - if (transform_wkt) - { - agg::trans_affine tr; - if (!mapnik::svg::parse_transform((*transform_wkt).c_str(),tr)) - { - std::stringstream ss; - ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'"; - if (strict_) - throw config_error(ss.str()); // value_error here? - else - std::clog << "### WARNING: " << ss << endl; - } - boost::array matrix; - tr.store_to(&matrix[0]); - shield_symbol.set_transform(matrix); - } - - // unlock_image - optional unlock_image = - get_opt_attr(sym, "unlock-image"); - if (unlock_image) - { - shield_symbol.set_unlock_image( * unlock_image ); - } - - // no text - if (no_text) - { - shield_symbol.set_no_text( * no_text ); - } - - parse_metawriter_in_symbolizer(shield_symbol, sym); - rule.append(shield_symbol); + shield_symbol.set_filename(parse_path(image_file)); } catch (image_reader_exception const & ex ) { @@ -1763,7 +1449,7 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) std::clog << "### WARNING: " << msg << endl; } } - + rule.append(shield_symbol); } catch (const config_error & ex) { @@ -1994,117 +1680,6 @@ void map_parser::parse_raster_symbolizer( rule & rule, ptree const & sym ) } } -void map_parser::parse_glyph_symbolizer(rule & rule, ptree const & sym) -{ - ensure_attrs(sym, "GlyphSymbolizer", "face-name,char,angle,angle-mode,value,size,color,halo-fill,halo-radius,allow-overlap,avoid-edges,dx,dy,meta-writer,meta-output"); - try - { - // Parse required constructor args - std::string face_name = get_attr(sym, "face-name"); - std::string _char = get_attr(sym, "char"); - - glyph_symbolizer glyph_sym = glyph_symbolizer( - face_name, - parse_expression(_char, "utf8") - ); - - // - // parse and set optional attrs. - // - - // angle - optional angle = - get_opt_attr(sym, "angle"); - if (angle) - glyph_sym.set_angle(parse_expression(*angle, "utf8")); - - angle_mode_e angle_mode = - get_attr(sym, "angle-mode", TRIGONOMETRIC); - glyph_sym.set_angle_mode(angle_mode); - - // value - optional value = - get_opt_attr(sym, "value"); - if (value) - glyph_sym.set_value(parse_expression(*value, "utf8")); - - // size - std::string size = - get_attr(sym, "size"); - glyph_sym.set_size(parse_expression(size, "utf8")); - - // color - optional _color = - get_opt_attr(sym, "color"); - if (_color) - glyph_sym.set_color(parse_expression(*_color, "utf8")); - - // halo_fill - optional halo_fill = get_opt_attr(sym, "halo-fill"); - if (halo_fill) - glyph_sym.set_halo_fill(*halo_fill); - - // halo_radius - optional halo_radius = get_opt_attr( - sym, - "halo-radius"); - if (halo_radius) - glyph_sym.set_halo_radius(*halo_radius); - - // allow_overlap - optional allow_overlap = get_opt_attr( - sym, - "allow-overlap" - ); - if (allow_overlap) - glyph_sym.set_allow_overlap(*allow_overlap); - - // avoid_edges - optional avoid_edges = get_opt_attr( - sym, - "avoid-edges" - ); - if (avoid_edges) - glyph_sym.set_avoid_edges(*avoid_edges); - - // displacement - optional dx = get_opt_attr(sym, "dx"); - optional dy = get_opt_attr(sym, "dy"); - if (dx && dy) - glyph_sym.set_displacement(*dx, *dy); - - // colorizer - ptree::const_iterator childIter = sym.begin(); - ptree::const_iterator endChild = sym.end(); - - for (; childIter != endChild; ++childIter) - { - ptree::value_type const& tag = *childIter; - - if (tag.first == "RasterColorizer") - { - raster_colorizer_ptr colorizer(new raster_colorizer()); - glyph_sym.set_colorizer(colorizer); - parse_raster_colorizer(colorizer, tag.second); - } - else if (tag.first!="" && tag.first!="" ) - { - throw config_error(std::string("Unknown child node. ") + - "Expected 'RasterColorizer' but got '" + - tag.first + "'"); - } - } - - parse_metawriter_in_symbolizer(glyph_sym, sym); - rule.append(glyph_sym); - } - catch (const config_error & ex) - { - ex.append_context("in GlyphSymbolizer"); - throw; - } -} - void map_parser::parse_raster_colorizer(raster_colorizer_ptr const& rc, ptree const& node ) { diff --git a/src/metawriter.cpp b/src/metawriter.cpp index 51a56b247..a97fcb6a5 100644 --- a/src/metawriter.cpp +++ b/src/metawriter.cpp @@ -23,7 +23,7 @@ // Mapnik #include #include -#include +#include // Boost #include @@ -175,8 +175,8 @@ void metawriter_json_stream::add_box(box2d const &box, Feature const& fe } -void metawriter_json_stream::add_text(placement const& p, - face_set_ptr face, +void metawriter_json_stream::add_text(text_placement_info const& p, + face_manager_freetype &font_manager, Feature const& feature, CoordTransform const& t, metawriter_properties const& properties) @@ -193,18 +193,19 @@ void metawriter_json_stream::add_text(placement const& p, Hightest y = baseline of top line */ -// if (p.placements.size()) std::cout << p.info.get_string() << "\n"; 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; + bool inside = false; /* Part of text is inside rendering region */ bool straight = true; - int c; double x, y, angle; + int c; + double x, y, angle; + char_properties *format; current_placement.rewind(); for (int i = 0; i < current_placement.num_nodes(); ++i) { int cx = current_placement.starting_x; int cy = current_placement.starting_y; - current_placement.vertex(&c, &x, &y, &angle); + current_placement.vertex(&c, &x, &y, &angle, &format); if (cx+x >= 0 && cx+x < width_ && cy-y >= 0 && cy-y < height_) inside = true; if (angle > 0.001 || angle < -0.001) straight = false; if (inside && !straight) break; @@ -217,14 +218,13 @@ void metawriter_json_stream::add_text(placement const& p, //Reduce number of polygons double minx = INT_MAX, miny = INT_MAX, maxx = INT_MIN, maxy = INT_MIN; for (int i = 0; i < current_placement.num_nodes(); ++i) { - current_placement.vertex(&c, &x, &y, &angle); - font_face_set::dimension_t ci = face->character_dimensions(c); - if (x < minx) minx = x; - if (x+ci.width > maxx) maxx = x+ci.width; - if (y+ci.height+ci.ymin > maxy) maxy = y+ci.height+ci.ymin; - if (y+ci.ymin < miny) miny = y+ci.ymin; - // std::cout << (char) c << " height:" << ci.height << " ymin:" << ci.ymin << " y:" << y << " miny:"<< miny << " maxy:"<< maxy <<"\n"; - + current_placement.vertex(&c, &x, &y, &angle, &format); + face_set_ptr face = font_manager.get_face_set(format->face_name, format->fontset); + char_info ci = face->character_dimensions(c); + minx = std::min(minx, x); + maxx = std::max(maxx, x+ci.width); + maxy = std::max(maxy, y+ci.ymax); + miny = std::min(miny, y+ci.ymin); } add_box(box2d(current_placement.starting_x+minx, current_placement.starting_y-miny, @@ -240,9 +240,10 @@ void metawriter_json_stream::add_text(placement const& p, if (c != ' ') { *f_ << ","; } - current_placement.vertex(&c, &x, &y, &angle); + current_placement.vertex(&c, &x, &y, &angle, &format); if (c == ' ') continue; - font_face_set::dimension_t ci = face->character_dimensions(c); + face_set_ptr face = font_manager.get_face_set(format->face_name, format->fontset); + char_info ci = face->character_dimensions(c); double x0, y0, x1, y1, x2, y2, x3, y3; double sina = sin(angle); @@ -251,10 +252,10 @@ void metawriter_json_stream::add_text(placement const& p, y0 = current_placement.starting_y - y - cosa*ci.ymin; x1 = x0 + ci.width * cosa; y1 = y0 - ci.width * sina; - x2 = x0 + (ci.width * cosa - ci.height * sina); - y2 = y0 - (ci.width * sina + ci.height * cosa); - x3 = x0 - ci.height * sina; - y3 = y0 - ci.height * cosa; + x2 = x0 + (ci.width * cosa - ci.height() * sina); + y2 = y0 - (ci.width * sina + ci.height() * cosa); + x3 = x0 - ci.height() * sina; + y3 = y0 - ci.height() * cosa; *f_ << "\n [["; write_point(t, x0, y0); diff --git a/src/metawriter_inmem.cpp b/src/metawriter_inmem.cpp index 27025cde0..be7617c13 100644 --- a/src/metawriter_inmem.cpp +++ b/src/metawriter_inmem.cpp @@ -72,9 +72,9 @@ metawriter_inmem::add_box(box2d const& box, Feature const& feature, instances_.push_back(inst); } -void -metawriter_inmem::add_text(placement const& p, - face_set_ptr /*face*/, +void +metawriter_inmem::add_text(text_placement_info const& p, + face_manager_freetype & /*face*/, Feature const& feature, CoordTransform const& /*t*/, metawriter_properties const& properties) { diff --git a/src/placement_finder.cpp b/src/placement_finder.cpp index c965d03cf..da5583033 100644 --- a/src/placement_finder.cpp +++ b/src/placement_finder.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include // agg @@ -49,60 +50,6 @@ namespace mapnik { -placement::placement(string_info & info_, - shield_symbolizer const& sym, - double scale_factor, - unsigned w, unsigned h, - bool has_dimensions_) - : info(info_), - scale_factor_(scale_factor), - label_placement(sym.get_label_placement()), - wrap_width(sym.get_wrap_width()), - wrap_before(sym.get_wrap_before()), - wrap_char(sym.get_wrap_char()), - text_ratio(sym.get_text_ratio()), - label_spacing(scale_factor_ * sym.get_label_spacing()), - label_position_tolerance(sym.get_label_position_tolerance()), - force_odd_labels(sym.get_force_odd_labels()), - max_char_angle_delta(sym.get_max_char_angle_delta()), - minimum_distance(scale_factor_ * sym.get_minimum_distance()), - minimum_padding(scale_factor_ * sym.get_minimum_padding()), - minimum_path_length(0), - avoid_edges(sym.get_avoid_edges()), - has_dimensions(has_dimensions_), - allow_overlap(false), - dimensions(std::make_pair(w,h)), - collect_extents(false), - extents() -{} - -placement::placement(string_info & info_, - text_symbolizer const& sym, - double scale_factor) - : info(info_), - scale_factor_(scale_factor), - label_placement(sym.get_label_placement()), - wrap_width(sym.get_wrap_width()), - wrap_before(sym.get_wrap_before()), - wrap_char(sym.get_wrap_char()), - text_ratio(sym.get_text_ratio()), - label_spacing(scale_factor_ * sym.get_label_spacing()), - label_position_tolerance(sym.get_label_position_tolerance()), - force_odd_labels(sym.get_force_odd_labels()), - max_char_angle_delta(sym.get_max_char_angle_delta()), - minimum_distance(scale_factor_ * sym.get_minimum_distance()), - minimum_padding(scale_factor_ * sym.get_minimum_padding()), - minimum_path_length(scale_factor_ * sym.get_minimum_path_length()), - avoid_edges(sym.get_avoid_edges()), - has_dimensions(false), - allow_overlap(sym.get_allow_overlap()), - dimensions(), - collect_extents(false), - extents() -{} - - -placement::~placement() {} template std::pair get_position_at_distance(double target_distance, T & shape_path) @@ -151,22 +98,26 @@ double get_total_distance(T & shape_path) } template -placement_finder::placement_finder(DetectorT & detector) +placement_finder::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector) : detector_(detector), - dimensions_(detector_.extent()) + 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 -placement_finder::placement_finder(DetectorT & detector, box2d const& extent) +placement_finder::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector, box2d const& extent) : detector_(detector), - dimensions_(extent) + 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 template -void placement_finder::find_point_placements(placement & p, text_placement_info_ptr po, T & shape_path) +void placement_finder::find_point_placements(T & shape_path) { unsigned cmd; double new_x = 0.0; @@ -182,15 +133,15 @@ void placement_finder::find_point_placements(placement & p, text_plac { double x, y; shape_path.vertex(&x,&y); - find_point_placement(p, po, x, y); + find_point_placement(x, y); return; } int num_labels = 1; if (p.label_spacing > 0) - num_labels = static_cast (floor(total_distance / p.label_spacing)); + num_labels = static_cast (floor(total_distance / pi.get_actual_label_spacing())); - if (p.force_odd_labels && num_labels%2 == 0) + if (p.force_odd_labels && num_labels % 2 == 0) num_labels--; if (num_labels <= 0) num_labels = 1; @@ -217,7 +168,7 @@ void placement_finder::find_point_placements(placement & p, text_plac { //Try place at the specified place double new_weight = (segment_length - (distance - target_distance))/segment_length; - find_point_placement(p, po, old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight); + find_point_placement(old_x + (new_x-old_x)*new_weight, old_y + (new_y-old_y)*new_weight); distance -= target_distance; //Consume the spacing gap we have used up target_distance = spacing; //Need to reset the target_distance as it is spacing/2 for the first label. @@ -231,245 +182,270 @@ void placement_finder::find_point_placements(placement & p, text_plac } template -void placement_finder::find_point_placement(placement & p, - text_placement_info_ptr po, - double label_x, - double label_y, - double angle, - unsigned line_spacing, - unsigned character_spacing) +void placement_finder::init_string_size() { - double x, y; - std::auto_ptr current_placement(new placement_element); - - std::pair string_dimensions = p.info.get_dimensions(); - double string_width = string_dimensions.first + (character_spacing *(p.info.num_characters()-1)); - double string_height = string_dimensions.second; + // Get total string size + string_width_ = 0; + string_height_ = 0; + first_line_space_ = 0; + if (!info_.num_characters()) return; //At least one character is required + for (unsigned i = 0; i < info_.num_characters(); i++) + { + char_info const& ci = info_.at(i); + if (!ci.width || !ci.line_height) continue; //Skip empty chars (add no character_spacing for them) + string_width_ += ci.width + ci.format->character_spacing; + string_height_ = std::max(string_height_, ci.line_height+ci.format->line_spacing); + first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height); + } + string_width_ -= info_.at(info_.num_characters()-1).format->character_spacing; //Remove last space +} - // use height of tallest character in the string for the 'line' spacing to obtain consistent line spacing - double max_character_height = string_height; // height of the tallest character in the string + + +template +void placement_finder::find_line_breaks() +{ + bool first_line = true; + line_breaks_.clear(); + line_sizes_.clear(); // check if we need to wrap the string - double wrap_at = string_width + 1.0; - if (p.wrap_width && string_width > p.wrap_width) + double wrap_at = string_width_ + 1.0; + if (p.wrap_width && string_width_ > p.wrap_width) { if (p.text_ratio) - for (double i = 1.0; ((wrap_at = string_width/i)/(string_height*i)) > p.text_ratio && (string_width/i) > p.wrap_width; i += 1.0) ; + for (double i = 1.0; ((wrap_at = string_width_/i)/(string_height_*i)) > p.text_ratio && (string_width_/i) > p.wrap_width; i += 1.0) ; else wrap_at = p.wrap_width; } // work out where our line breaks need to be and the resultant width to the 'wrapped' string - std::vector line_breaks; - std::vector line_widths; - - if ((p.info.num_characters() > 0) && ((wrap_at < string_width) || p.info.has_line_breaks())) + if ((wrap_at < string_width_) || info_.has_line_breaks()) { - int last_wrap_char = 0; - int last_wrap_char_width = 0; - string_width = 0.0; - string_height = 0.0; + first_line_space_ = 0.0; + int last_wrap_char_pos = 0; //Position of last char where wrapping is possible + double last_char_spacing = 0.0; + double last_wrap_char_width = 0.0; //Include char_spacing before and after + string_width_ = 0.0; + string_height_ = 0.0; double line_width = 0.0; - double word_width = 0.0; + double line_height = 0.0; //Height of tallest char in line + double word_width = 0.0; //Current unfinished word width + double word_height = 0.0; + //line_width, word_width does include char width + spacing, but not the spacing after the last char - for (unsigned int ii = 0; ii < p.info.num_characters(); ii++) + for (unsigned int ii = 0; ii < info_.num_characters(); ii++) { - character_info ci; - ci = p.info.at(ii); + char_info const& ci = info_.at(ii); + unsigned c = ci.c; - double cwidth = ci.width + character_spacing; - - unsigned c = ci.character; - word_width += cwidth; - - if ((c == p.wrap_char) || (c == '\n')) + if ((c == ci.format->wrap_char) || (c == '\n')) { - last_wrap_char = ii; - last_wrap_char_width = cwidth; - line_width += word_width; + last_wrap_char_pos = ii; + //No wrap at previous position + line_width += word_width + last_wrap_char_width; + line_height = std::max(line_height, word_height); + last_wrap_char_width = last_char_spacing + ci.width + ci.format->character_spacing; + last_char_spacing = 0.0; word_width = 0.0; + word_height = 0.0; + } else { + //No wrap char + word_width += last_char_spacing + ci.width; + last_char_spacing = ci.format->character_spacing; + word_height = std::max(word_height, ci.line_height + ci.format->line_spacing); + if (first_line) first_line_space_ = std::max(first_line_space_, ci.line_height-ci.avg_height); } // wrap text at first wrap_char after (default) the wrap width or immediately before the current word if ((c == '\n') || - (line_width > 0 && (((line_width - character_spacing) > wrap_at && !p.wrap_before) || - ((line_width + word_width - character_spacing) > wrap_at && p.wrap_before)) )) + (line_width > 0 && ((line_width > wrap_at && !ci.format->wrap_before) || + ((line_width + last_wrap_char_width + word_width) > wrap_at && ci.format->wrap_before)) )) { - // Remove width of breaking space character since it is not rendered and the character_spacing for the last character on the line - line_width -= (last_wrap_char_width + character_spacing); - string_width = string_width > line_width ? string_width : line_width; - string_height += max_character_height; - line_breaks.push_back(last_wrap_char); - line_widths.push_back(line_width); - ii = last_wrap_char; + string_width_ = std::max(string_width_, line_width); //Total width is the longest line + string_height_ += line_height; + line_breaks_.push_back(last_wrap_char_pos); + line_sizes_.push_back(std::make_pair(line_width, line_height)); line_width = 0.0; - word_width = 0.0; + line_height = 0.0; + last_wrap_char_width = 0; //Wrap char supressed + first_line = false; } } - line_width += (word_width - character_spacing); // remove character_spacing from last character on the line - string_width = string_width > line_width ? string_width : line_width; - string_height += max_character_height; - line_breaks.push_back(p.info.num_characters()); - line_widths.push_back(line_width); + line_width += last_wrap_char_width + word_width; + line_height = std::max(line_height, word_height); + string_width_ = std::max(string_width_, line_width); + string_height_ += line_height; + line_sizes_.push_back(std::make_pair(line_width, line_height)); + } else { + //No linebreaks + line_sizes_.push_back(std::make_pair(string_width_, string_height_)); } - if (line_breaks.size() == 0) - { - line_breaks.push_back(p.info.num_characters()); - line_widths.push_back(string_width); + line_breaks_.push_back(info_.num_characters()); +} + + + +template +void placement_finder::init_alignment() +{ + valign_ = p.valign; + if (valign_ == V_AUTO) { + if (p.displacement.second > 0.0) + valign_ = V_BOTTOM; + else if (p.displacement.second < 0.0) + valign_ = V_TOP; + else + valign_ = V_MIDDLE; } - int total_lines = line_breaks.size(); - p.info.set_dimensions( string_width, (string_height + (line_spacing * (total_lines-1))) ); + halign_ = p.halign; + if (halign_ == H_AUTO) { + if (p.displacement.first > 0.0) + halign_ = H_RIGHT; + else if (p.displacement.first < 0.0) + halign_ = H_LEFT; + else + halign_ = H_MIDDLE; + } +} + +template +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 - - vertical_alignment_e real_valign = po->valign; - if (real_valign == V_AUTO) { - if (po->displacement.get<1>() > 0.0) - real_valign = V_BOTTOM; - else if (po->displacement.get<1>() < 0.0) - real_valign = V_TOP; - else - real_valign = V_MIDDLE; + if (valign_ == V_TOP) + current_placement->starting_y -= 0.5 * string_height_; // move center up by 1/2 the total height + else if (valign_ == V_BOTTOM) { + current_placement->starting_y += 0.5 * string_height_; // move center down by the 1/2 the total height + current_placement->starting_y -= first_line_space_; + } else if (valign_ == V_MIDDLE) { + current_placement->starting_y -= first_line_space_/2.0; } - horizontal_alignment_e real_halign = po->halign; - if (real_halign == H_AUTO) { - if (po->displacement.get<0>() > 0.0) - real_halign = H_RIGHT; - else if (po->displacement.get<0>() < 0.0) - real_halign = H_LEFT; - else - real_halign = H_MIDDLE; - } - - if (real_valign == V_TOP) - current_placement->starting_y -= 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center up by 1/2 the total height - - else if (real_valign == V_BOTTOM) - current_placement->starting_y += 0.5 * (string_height + (line_spacing * (total_lines-1))); // move center down by the 1/2 the total height - - // correct placement for error, but BOTTOM does not need to be adjusted - // (text rendering is at text_size, but line placement is by line_height (max_character_height), - // and the rendering adds the extra space below the characters) - if (real_valign == V_TOP ) - current_placement->starting_y -= (po->text_size - max_character_height); // move up by the error - - else if (real_valign == V_MIDDLE) - current_placement->starting_y -= ((po->text_size - max_character_height) / 2.0); // move up by 1/2 the error - // set horizontal position to middle of text current_placement->starting_x = label_x; // no adjustment, default is MIDDLE - - if (real_halign == H_LEFT) - current_placement->starting_x -= 0.5 * string_width; // move center left by 1/2 the string width - - else if (real_halign == H_RIGHT) - current_placement->starting_x += 0.5 * string_width; // move center right by 1/2 the string width + if (halign_ == H_LEFT) + current_placement->starting_x -= 0.5 * string_width_; // move center left by 1/2 the string width + else if (halign_ == H_RIGHT) + 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 += p.scale_factor_ * boost::tuples::get<0>(po->displacement); - current_placement->starting_y += p.scale_factor_ * boost::tuples::get<1>(po->displacement); + current_placement->starting_x += pi.get_scale_factor() * p.displacement.first; + current_placement->starting_y += pi.get_scale_factor() * p.displacement.second; + +} + +template +void placement_finder::find_point_placement(double label_x, double label_y, double angle) +{ + init_string_size(); + find_line_breaks(); + init_alignment(); + + double rad = M_PI * angle/180.0; + double cosa = std::cos(rad); + double sina = std::sin(rad); + + double x, y; + std::auto_ptr current_placement(new text_path); + + adjust_position(current_placement.get(), label_x, label_y); // presets for first line unsigned int line_number = 0; - unsigned int index_to_wrap_at = line_breaks[0]; - double line_width = line_widths[0]; + unsigned int index_to_wrap_at = line_breaks_[0]; + double line_width = line_sizes_[0].first; + double line_height = line_sizes_[0].second; + //TODO: Understand and document this // set for upper left corner of text envelope for the first line, bottom left of first character - x = -(line_width / 2.0); - if (p.info.get_rtl()==false) - { - y = (0.5 * (string_height + (line_spacing * (total_lines-1)))) - max_character_height; - } + y = (string_height_ / 2.0) - line_height; + + // adjust for desired justification + //TODO: Understand and document this + if (p.jalign == J_LEFT) + x = -(string_width_ / 2.0); + else if (p.jalign == J_RIGHT) + x = (string_width_ / 2.0) - line_width; else - { - y = -(0.5 * (string_height + (line_spacing * (total_lines-1)))) + max_character_height; - } - - // if needed, adjust for desired justification (J_MIDDLE is the default) - if( po->jalign == J_LEFT ) - x = -(string_width / 2.0); - - else if (po->jalign == J_RIGHT) - x = (string_width / 2.0) - line_width; + x = -(line_width / 2.0); // save each character rendering position and build envelope as go thru loop std::queue< box2d > c_envelopes; - for (unsigned i = 0; i < p.info.num_characters(); i++) + for (unsigned i = 0; i < info_.num_characters(); i++) { - character_info ci; - ci = p.info.at(i); + char_info const& ci = info_.at(i); - double cwidth = ci.width + character_spacing; + double cwidth = ci.width + ci.format->character_spacing; - unsigned c = ci.character; + unsigned c = ci.c; if (i == index_to_wrap_at) { - index_to_wrap_at = line_breaks[++line_number]; - line_width = line_widths[line_number]; + index_to_wrap_at = line_breaks_[++line_number]; + line_width = line_sizes_[line_number].first; + line_height= line_sizes_[line_number].second; - if (p.info.get_rtl()==false) - { - y -= (max_character_height + line_spacing); // move position down to line start - } - else - { - y += (max_character_height + line_spacing); // move position up to line start - } + y -= line_height; // move position down to line start // reset to begining of line position - x = ((po->jalign == J_LEFT)? -(string_width / 2.0): ((po->jalign == J_RIGHT)? ((string_width /2.0) - line_width): -(line_width / 2.0))); + if (p.jalign == J_LEFT) + x = -(string_width_ / 2.0); + else if (p.jalign == J_RIGHT) + x = (string_width_ / 2.0) - line_width; + else + x = -(line_width / 2.0); continue; } else { // place the character relative to the center of the string envelope - double rad = M_PI * angle/180.0; - double cosa = fast_cos(rad); - double sina = fast_sin(rad); - double dx = x * cosa - y*sina; double dy = x * sina + y*cosa; - current_placement->add_node(c, dx, dy, rad); + current_placement->add_node(c, dx, dy, rad, ci.format); // compute the Bounding Box for each character and test for: // overlap, minimum distance or edge avoidance - exit if condition occurs box2d e; - if (p.has_dimensions) + /*x axis: left to right, y axis: top to bottom (negative values higher)*/ + if (pi.has_dimensions) { - e.init(current_placement->starting_x - (p.dimensions.first/2.0), // Top Left - current_placement->starting_y - (p.dimensions.second/2.0), + e.init(current_placement->starting_x - (pi.dimensions.first/2.0), // Top Left + current_placement->starting_y - (pi.dimensions.second/2.0), - current_placement->starting_x + (p.dimensions.first/2.0), // Bottom Right - current_placement->starting_y + (p.dimensions.second/2.0)); + current_placement->starting_x + (pi.dimensions.first/2.0), // Bottom Right + current_placement->starting_y + (pi.dimensions.second/2.0)); } else { e.init(current_placement->starting_x + dx, // Bottom Left - current_placement->starting_y - dy, + current_placement->starting_y - dy - ci.ymin, /*ymin usually <0 */ current_placement->starting_x + dx + ci.width, // Top Right - current_placement->starting_y - dy - max_character_height); + current_placement->starting_y - dy - ci.ymax); } // if there is an overlap with existing envelopes, then exit - no placement - if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e,p.minimum_distance))) + if (!detector_.extent().intersects(e) || (!p.allow_overlap && !detector_.has_point_placement(e, pi.get_actual_minimum_distance()))) { return; + } // if avoid_edges test dimensions contains e - if (p.avoid_edges && !dimensions_.contains(e)) + if (p.avoid_edges && !dimensions_.contains(e)) { return; + } - if (p.minimum_padding > 0) + if (p.minimum_padding > 0) { - box2d epad(e.minx()-p.minimum_padding, - e.miny()-p.minimum_padding, - e.maxx()+p.minimum_padding, - e.maxy()+p.minimum_padding); + double min_pad = pi.get_actual_minimum_padding(); + box2d epad(e.minx()-min_pad, + e.miny()-min_pad, + e.maxx()+min_pad, + e.maxy()+min_pad); if (!dimensions_.contains(epad)) { return; @@ -483,9 +459,9 @@ void placement_finder::find_point_placement(placement & p, } // 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, @@ -502,18 +478,19 @@ void placement_finder::find_point_placement(placement & p, // since there was no early exit, add the character envelopes to the placements' envelopes while( !c_envelopes.empty() ) { - p.envelopes.push( c_envelopes.front() ); + pi.envelopes.push( c_envelopes.front() ); c_envelopes.pop(); } - p.placements.push_back(current_placement.release()); + pi.placements.push_back(current_placement.release()); } template template -void placement_finder::find_line_placements(placement & p, text_placement_info_ptr po, PathT & shape_path) +void placement_finder::find_line_placements(PathT & shape_path) { + init_string_size(); unsigned cmd; double new_x = 0.0; double new_y = 0.0; @@ -556,29 +533,26 @@ void placement_finder::find_line_placements(placement & p, text_place return; double distance = 0.0; - std::pair string_dimensions = p.info.get_dimensions(); - double string_width = string_dimensions.first; - - double displacement = boost::tuples::get<1>(po->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 + if (total_distance < string_width_) //Can't place any strings return; //If there is no spacing then just do one label, otherwise calculate how many there should be int num_labels = 1; if (p.label_spacing > 0) - num_labels = static_cast (floor(total_distance / (p.label_spacing + string_width))); + num_labels = static_cast (floor(total_distance / (pi.get_actual_label_spacing() + string_width_))); - if (p.force_odd_labels && num_labels%2 == 0) + if (p.force_odd_labels && (num_labels % 2 == 0)) num_labels--; if (num_labels <= 0) num_labels = 1; //Now we know how many labels we are going to place, calculate the spacing so that they will get placed evenly double spacing = total_distance / num_labels; - double target_distance = (spacing - string_width) / 2; // first label should be placed at half the spacing + double target_distance = (spacing - string_width_) / 2; // first label should be placed at half the spacing //Calculate or read out the tolerance double tolerance_delta, tolerance; @@ -620,7 +594,7 @@ void placement_finder::find_line_placements(placement & p, text_place { //Record details for the start of the string placement int orientation = 0; - std::auto_ptr current_placement = get_placement_offset(p, 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) @@ -639,22 +613,20 @@ void placement_finder::find_line_placements(placement & p, text_place } anglesum /= current_placement->nodes_.size(); //Now it is angle average - double disp_x = p.scale_factor_ * displacement*fast_cos(anglesum+M_PI/2); - double disp_y = p.scale_factor_ * displacement*fast_sin(anglesum+M_PI/2); //Offset all the characters by this angle for (unsigned i = 0; i < current_placement->nodes_.size(); i++) { - current_placement->nodes_[i].x += disp_x; - current_placement->nodes_[i].y += disp_y; + current_placement->nodes_[i].x += pi.get_scale_factor() * displacement*cos(anglesum+M_PI/2); + current_placement->nodes_[i].y += pi.get_scale_factor() * displacement*sin(anglesum+M_PI/2); } } - bool status = test_placement(p, current_placement, orientation); + bool status = test_placement(current_placement, orientation); if (status) //We have successfully placed one { - p.placements.push_back(current_placement.release()); - update_detector(p); + pi.placements.push_back(current_placement.release()); + update_detector(); //Totally break out of the loops diff = tolerance; @@ -663,8 +635,8 @@ void placement_finder::find_line_placements(placement & p, text_place else { //If we've failed to place, remove all the envelopes we've added up - while (!p.envelopes.empty()) - p.envelopes.pop(); + while (!pi.envelopes.empty()) + pi.envelopes.pop(); } //Don't need to loop twice when diff = 0 @@ -684,7 +656,7 @@ void placement_finder::find_line_placements(placement & p, text_place } template -std::auto_ptr placement_finder::get_placement_offset(placement & p, 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) @@ -693,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]) @@ -702,15 +674,14 @@ 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 string_height = p.info.get_dimensions().second; double old_x = path_positions[index-1].x; double old_y = path_positions[index-1].y; @@ -723,12 +694,12 @@ 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; current_placement->starting_y = old_y + dy*distance/segment_length; - double angle = fast_atan2(-dy, dx); + double angle = atan2(-dy, dx); bool orientation_forced = (orientation != 0); //Wether the orientation was set by the caller if (!orientation_forced) @@ -736,21 +707,18 @@ std::auto_ptr placement_finder::get_placement_offs unsigned upside_down_char_count = 0; //Count of characters that are placed upside down. - for (unsigned i = 0; i < p.info.num_characters(); ++i) + for (unsigned i = 0; i < info_.num_characters(); ++i) { - character_info ci; - unsigned c; + // grab the next character according to the orientation + char_info const &ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1); + unsigned c = ci.c; double last_character_angle = angle; - // grab the next character according to the orientation - ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1); - c = ci.character; - //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; @@ -778,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; @@ -815,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; @@ -826,19 +794,20 @@ std::auto_ptr placement_finder::get_placement_offs double render_y = start_y; //Center the text on the line - render_x += (((double)string_height/2.0) - 1.0)*sina; - render_y += (((double)string_height/2.0) - 1.0)*cosa; + double char_height = ci.avg_height; + render_x -= char_height/2.0*cos(render_angle+M_PI/2); + render_y += char_height/2.0*sin(render_angle+M_PI/2); if (orientation < 0) { // rotate in place - render_x += ci.width*cosa - (string_height-2)*sina; - render_y -= ci.width*sina + (string_height-2)*cosa; + render_x += ci.width*cos(render_angle) - (char_height-2)*sin(render_angle); + render_y -= ci.width*sin(render_angle) + (char_height-2)*cos(render_angle); render_angle += M_PI; } current_placement->add_node(c,render_x - current_placement->starting_x, -render_y + current_placement->starting_y, - render_angle); + render_angle, ci.format); //Normalise to 0 <= angle < 2PI while (render_angle >= 2*M_PI) @@ -851,19 +820,19 @@ std::auto_ptr placement_finder::get_placement_offs } //If we placed too many characters upside down - if (upside_down_char_count >= p.info.num_characters()/2.0) + if (upside_down_char_count >= info_.num_characters()/2.0) { //if we auto-detected the orientation then retry with the opposite orientation if (!orientation_forced) { orientation = -orientation; - current_placement = get_placement_offset(p, path_positions, path_distances, orientation, initial_index, initial_distance); + current_placement = get_placement_offset(path_positions, path_distances, orientation, initial_index, initial_distance); } else { //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); } } @@ -871,57 +840,50 @@ std::auto_ptr placement_finder::get_placement_offs } template -bool placement_finder::test_placement(placement & p, const std::auto_ptr & current_placement, const int & orientation) +bool placement_finder::test_placement(const std::auto_ptr & current_placement, const int & orientation) { - std::pair string_dimensions = p.info.get_dimensions(); - - double string_height = string_dimensions.second; - - //Create and test envelopes bool status = true; - for (unsigned i = 0; i < p.info.num_characters(); ++i) + for (unsigned i = 0; i < info_.num_characters(); ++i) { // grab the next character according to the orientation - character_info ci = orientation > 0 ? p.info.at(i) : p.info.at(p.info.num_characters() - i - 1); + char_info const& ci = orientation > 0 ? info_.at(i) : info_.at(info_.num_characters() - i - 1); int c; double x, y, angle; - current_placement->vertex(&c, &x, &y, &angle); + char_properties *properties; + current_placement->vertex(&c, &x, &y, &angle, &properties); x = current_placement->starting_x + x; y = current_placement->starting_y - y; if (orientation < 0) { - double sina = fast_sin(angle); - double cosa = fast_cos(angle); // rotate in place - x += ci.width*cosa - (string_height-2)*sina; - y -= ci.width*sina + (string_height-2)*cosa; + /* TODO: What's the meaning of -2? */ + x += ci.width*cos(angle) - (string_height_-2)*sin(angle); + y -= ci.width*sin(angle) + (string_height_-2)*cos(angle); angle += M_PI; } box2d e; - if (p.has_dimensions) + if (pi.has_dimensions) { - e.init(x, y, x + p.dimensions.first, y + p.dimensions.second); + e.init(x, y, x + pi.dimensions.first, y + pi.dimensions.second); } else { - double sina = fast_sin(angle); - double cosa = fast_cos(angle); // put four corners of the letter into envelope - e.init(x, y, x + ci.width*cosa, - y - ci.width*sina); - e.expand_to_include(x - ci.height*sina, - y - ci.height*cosa); - e.expand_to_include(x + (ci.width*cosa - ci.height*sina), - y - (ci.width*sina + ci.height*cosa)); + e.init(x, y, x + ci.width*cos(angle), + y - ci.width*sin(angle)); + e.expand_to_include(x - ci.height()*sin(angle), + y - ci.height()*cos(angle)); + e.expand_to_include(x + (ci.width*cos(angle) - ci.height()*sin(angle)), + y - (ci.width*sin(angle) + ci.height()*cos(angle))); } if (!detector_.extent().intersects(e) || - !detector_.has_placement(e, p.info.get_string(), p.minimum_distance)) + !detector_.has_placement(e, info_.get_string(), pi.get_actual_minimum_distance())) { //std::clog << "No Intersects:" << !dimensions_.intersects(e) << ": " << e << " @ " << dimensions_ << std::endl; - //std::clog << "No Placements:" << !detector_.has_placement(e, p.info.get_string(), p.minimum_distance) << std::endl; + //std::clog << "No Placements:" << !detector_.has_placement(e, info.get_string(), p.minimum_distance) << std::endl; status = false; break; } @@ -932,19 +894,20 @@ bool placement_finder::test_placement(placement & p, const std::auto_ status = false; break; } - if (p.minimum_padding > 0) + if (p.minimum_padding > 0) { - box2d epad(e.minx()-p.minimum_padding, - e.miny()-p.minimum_padding, - e.maxx()+p.minimum_padding, - e.maxy()+p.minimum_padding); + double min_pad = pi.get_actual_minimum_padding(); + box2d epad(e.minx()-min_pad, + e.miny()-min_pad, + e.maxx()+min_pad, + e.maxy()+min_pad); if (!dimensions_.contains(epad)) { status = false; break; } } - p.envelopes.push(e); + pi.envelopes.push(e); } current_placement->rewind(); @@ -1000,27 +963,27 @@ void placement_finder::find_line_circle_intersection( } template -void placement_finder::update_detector(placement & p) +void placement_finder::update_detector() { bool first = true; // add the bboxes to the detector and remove from the placement - while (!p.envelopes.empty()) + while (!pi.envelopes.empty()) { - box2d e = p.envelopes.front(); - detector_.insert(e, p.info.get_string()); - p.envelopes.pop(); + box2d e = pi.envelopes.front(); + detector_.insert(e, info_.get_string()); + pi.envelopes.pop(); - if (p.collect_extents) + if (pi.collect_extents) { if(first) { first = false; - p.extents = e; + pi.extents = e; } else { - p.extents.expand_to_include(e); + pi.extents.expand_to_include(e); } } } @@ -1036,7 +999,7 @@ typedef coord_transform2 PathType; typedef label_collision_detector4 DetectorType; template class placement_finder; -template void placement_finder::find_point_placements (placement&, text_placement_info_ptr po, PathType & ); -template void placement_finder::find_line_placements (placement&, text_placement_info_ptr po, PathType & ); +template void placement_finder::find_point_placements(PathType &); +template void placement_finder::find_line_placements(PathType &); } // namespace diff --git a/src/save_map.cpp b/src/save_map.cpp index 3cf56b014..13912a52d 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include // boost #include @@ -187,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 @@ -197,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); } } @@ -310,100 +308,6 @@ public: add_metawriter_attributes(sym_node, sym); } - void operator () ( glyph_symbolizer const& sym) - { - ptree &node = rule_.push_back( - ptree::value_type("GlyphSymbolizer", ptree()) - )->second; - - glyph_symbolizer dfl("", expression_ptr()); - - // face_name - set_attr( node, "face-name", sym.get_face_name() ); - - // char - if (sym.get_char()) { - const std::string &str = - to_expression_string(*sym.get_char()); - set_attr( node, "char", str ); - } - - // angle - if (sym.get_angle()) { - const std::string &str = - to_expression_string(*sym.get_angle()); - set_attr( node, "angle", str ); - } - - // value - if (sym.get_value()) { - const std::string &str = - to_expression_string(*sym.get_value()); - set_attr( node, "value", str ); - } - - // size - if (sym.get_size()) { - const std::string &str = - to_expression_string(*sym.get_size()); - set_attr( node, "size", str ); - } - - // color - if (sym.get_color()) { - const std::string &str = - to_expression_string(*sym.get_color()); - set_attr( node, "color", str ); - } - - // colorizer - if (sym.get_colorizer()) { - serialize_raster_colorizer(node, sym.get_colorizer(), - explicit_defaults_); - } - - // allow_overlap - if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_ ) - { - set_attr( node, "allow-overlap", sym.get_allow_overlap() ); - } - // avoid_edges - if (sym.get_avoid_edges() != dfl.get_avoid_edges() || explicit_defaults_ ) - { - set_attr( node, "avoid-edges", sym.get_avoid_edges() ); - } - - // displacement - position displacement = sym.get_displacement(); - if ( displacement.get<0>() != dfl.get_displacement().get<0>() || explicit_defaults_ ) - { - set_attr( node, "dx", displacement.get<0>() ); - } - if ( displacement.get<1>() != dfl.get_displacement().get<1>() || explicit_defaults_ ) - { - set_attr( node, "dy", displacement.get<1>() ); - } - - // halo fill & radius - const color & c = sym.get_halo_fill(); - if ( c != dfl.get_halo_fill() || explicit_defaults_ ) - { - set_attr( node, "halo-fill", c ); - } - - if (sym.get_halo_radius() != dfl.get_halo_radius() || explicit_defaults_ ) - { - set_attr( node, "halo-radius", sym.get_halo_radius() ); - } - - // angle_mode - if (sym.get_angle_mode() != dfl.get_angle_mode() || explicit_defaults_ ) - { - set_attr( node, "angle-mode", sym.get_angle_mode() ); - } - add_metawriter_attributes(node, sym); - } - private: serialize_symbolizer(); @@ -451,134 +355,28 @@ private: } void add_font_attributes(ptree & node, const text_symbolizer & sym) { - expression_ptr const& expr = sym.get_name(); - const std::string & name = to_expression_string(*expr); - - if (!name.empty()) { - ptree& text_node = node.push_back(ptree::value_type("", ptree()))->second; - text_node.put_value(name); + text_placements_ptr p = sym.get_placement_options(); + p->properties.to_xml(node, explicit_defaults_); + /* Known types: + - text_placements_dummy: no handling required + - text_placements_simple: positions string + - text_placements_list: list string + */ + text_placements_simple *simple = dynamic_cast(p.get()); + text_placements_list *list = dynamic_cast(p.get()); + if (simple) { + set_attr(node, "placment-type", "simple"); + set_attr(node, "placements", simple->get_positions()); } - const std::string & face_name = sym.get_face_name(); - if ( ! face_name.empty() ) { - set_attr( node, "face-name", face_name ); - } - const std::string & fontset_name = sym.get_fontset().get_name(); - if ( ! fontset_name.empty() ) { - set_attr( node, "fontset-name", fontset_name ); - } - - set_attr( node, "size", sym.get_text_size() ); - set_attr( node, "fill", sym.get_fill() ); - - // pseudo-default-construct a text_symbolizer. It is used - // to avoid printing ofattributes with default values without - // repeating the default values here. - // maybe add a real, explicit default-ctor? - // FIXME - text_symbolizer dfl(expression_ptr(), "", - 0, color(0,0,0) ); - - position displacement = sym.get_displacement(); - if ( displacement.get<0>() != dfl.get_displacement().get<0>() || explicit_defaults_ ) - { - set_attr( node, "dx", displacement.get<0>() ); - } - if ( displacement.get<1>() != dfl.get_displacement().get<1>() || explicit_defaults_ ) - { - set_attr( node, "dy", displacement.get<1>() ); - } - - if (sym.get_label_placement() != dfl.get_label_placement() || explicit_defaults_ ) - { - set_attr( node, "placement", sym.get_label_placement() ); - } - - if (sym.get_vertical_alignment() != dfl.get_vertical_alignment() || explicit_defaults_ ) - { - set_attr( node, "vertical-alignment", sym.get_vertical_alignment() ); - } - - if (sym.get_halo_radius() != dfl.get_halo_radius() || explicit_defaults_ ) - { - set_attr( node, "halo-radius", sym.get_halo_radius() ); - } - const color & c = sym.get_halo_fill(); - if ( c != dfl.get_halo_fill() || explicit_defaults_ ) - { - set_attr( node, "halo-fill", c ); - } - if (sym.get_text_ratio() != dfl.get_text_ratio() || explicit_defaults_ ) - { - set_attr( node, "text-ratio", sym.get_text_ratio() ); - } - if (sym.get_wrap_width() != dfl.get_wrap_width() || explicit_defaults_ ) - { - set_attr( node, "wrap-width", sym.get_wrap_width() ); - } - if (sym.get_wrap_before() != dfl.get_wrap_before() || explicit_defaults_ ) - { - set_attr( node, "wrap-before", sym.get_wrap_before() ); - } - if (sym.get_wrap_char() != dfl.get_wrap_char() || explicit_defaults_ ) - { - set_attr( node, "wrap-character", std::string(1, sym.get_wrap_char()) ); - } - if (sym.get_text_transform() != dfl.get_text_transform() || explicit_defaults_ ) - { - set_attr( node, "text-transform", sym.get_text_transform() ); - } - if (sym.get_line_spacing() != dfl.get_line_spacing() || explicit_defaults_ ) - { - set_attr( node, "line-spacing", sym.get_line_spacing() ); - } - if (sym.get_character_spacing() != dfl.get_character_spacing() || explicit_defaults_ ) - { - set_attr( node, "character-spacing", sym.get_character_spacing() ); - } - if (sym.get_label_position_tolerance() != dfl.get_label_position_tolerance() || explicit_defaults_ ) - { - set_attr( node, "label-position-tolerance", sym.get_label_position_tolerance() ); - } - if (sym.get_label_spacing() != dfl.get_label_spacing() || explicit_defaults_ ) - { - set_attr( node, "spacing", sym.get_label_spacing() ); - } - if (sym.get_minimum_distance() != dfl.get_minimum_distance() || explicit_defaults_ ) - { - set_attr( node, "minimum-distance", sym.get_minimum_distance() ); - } - if (sym.get_minimum_padding() != dfl.get_minimum_padding() || explicit_defaults_ ) - { - set_attr( node, "minimum-padding", sym.get_minimum_padding() ); - } - if (sym.get_minimum_path_length() != dfl.get_minimum_path_length() || explicit_defaults_ ) - { - set_attr( node, "minimum-path-length", sym.get_minimum_path_length() ); - } - if (sym.get_allow_overlap() != dfl.get_allow_overlap() || explicit_defaults_ ) - { - set_attr( node, "allow-overlap", sym.get_allow_overlap() ); - } - if (sym.get_avoid_edges() != dfl.get_avoid_edges() || explicit_defaults_ ) - { - set_attr( node, "avoid-edges", sym.get_avoid_edges() ); - } - // for shield_symbolizer this is later overridden - if (sym.get_text_opacity() != dfl.get_text_opacity() || explicit_defaults_ ) - { - set_attr( node, "opacity", sym.get_text_opacity() ); - } - if (sym.get_max_char_angle_delta() != dfl.get_max_char_angle_delta() || explicit_defaults_ ) - { - set_attr( node, "max-char-angle-delta", sym.get_max_char_angle_delta() ); - } - if (sym.get_horizontal_alignment() != dfl.get_horizontal_alignment() || explicit_defaults_ ) - { - set_attr( node, "horizontal-alignment", sym.get_horizontal_alignment() ); - } - if (sym.get_justify_alignment() != dfl.get_justify_alignment() || explicit_defaults_ ) - { - set_attr( node, "justify-alignment", sym.get_justify_alignment() ); + if (list) { + set_attr(node, "placment-type", "list"); + unsigned i; + text_symbolizer_properties *dfl = &(list->properties); + for (i=0; i < list->size(); i++) { + ptree &placement_node = node.push_back(ptree::value_type("Placement", ptree()))->second; + list->get(i).to_xml(placement_node, explicit_defaults_, *dfl); + dfl = &(list->get(i)); + } } } diff --git a/src/shield_symbolizer.cpp b/src/shield_symbolizer.cpp index fcf54a73e..c5a03a453 100644 --- a/src/shield_symbolizer.cpp +++ b/src/shield_symbolizer.cpp @@ -34,6 +34,14 @@ namespace mapnik { +shield_symbolizer::shield_symbolizer(text_placements_ptr placements) + : text_symbolizer(placements), + symbolizer_with_image(), + unlock_image_(false), + shield_displacement_(0,0) +{ +} + shield_symbolizer::shield_symbolizer( expression_ptr name, std::string const& face_name, @@ -43,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) { } @@ -56,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) { } @@ -71,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/svg/process_glyph_symbolizer.cpp b/src/svg/process_glyph_symbolizer.cpp deleted file mode 100644 index 7ac35bb4d..000000000 --- a/src/svg/process_glyph_symbolizer.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/***************************************************************************** - * - * This file is part of Mapnik (c++ mapping toolkit) - * - * Copyright (C) 2011 Artem Pavlenko - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - *****************************************************************************/ -//$Id$ - -// mapnik -#include - -namespace mapnik -{ - template - void svg_renderer::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) - { - // nothing yet. - } - - template void svg_renderer >::process(glyph_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); -} diff --git a/src/symbolizer.cpp b/src/symbolizer.cpp index 4eba20036..e954aa5a1 100644 --- a/src/symbolizer.cpp +++ b/src/symbolizer.cpp @@ -72,7 +72,7 @@ metawriter_with_properties symbolizer_base::get_metawriter() const symbolizer_with_image::symbolizer_with_image(path_expression_ptr file) : image_filename_( file ), - opacity_(1.0f) + image_opacity_(1.0f) { matrix_[0] = 1.0; @@ -85,7 +85,7 @@ symbolizer_with_image::symbolizer_with_image(path_expression_ptr file) symbolizer_with_image::symbolizer_with_image( symbolizer_with_image const& rhs) : image_filename_(rhs.image_filename_), - opacity_(rhs.opacity_), + image_opacity_(rhs.image_opacity_), matrix_(rhs.matrix_) {} path_expression_ptr symbolizer_with_image::get_filename() const @@ -120,12 +120,12 @@ std::string const symbolizer_with_image::get_transform_string() const void symbolizer_with_image::set_opacity(float opacity) { - opacity_ = opacity; + image_opacity_ = opacity; } float symbolizer_with_image::get_opacity() const { - return opacity_; + return image_opacity_; } } // end of namespace mapnik diff --git a/src/symbolizer_helpers.cpp b/src/symbolizer_helpers.cpp new file mode 100644 index 000000000..a71811d25 --- /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.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 aca06a5ac..d3703ce02 100644 --- a/src/text_placements.cpp +++ b/src/text_placements.cpp @@ -22,11 +22,16 @@ #include #include +#include +#include +#include +#include #include #include #include #include +#include namespace mapnik { @@ -36,14 +41,340 @@ using boost::spirit::ascii::space; using phoenix::push_back; using phoenix::ref; using qi::_1; +using boost::optional; + +text_symbolizer_properties::text_symbolizer_properties() : + label_placement(POINT_PLACEMENT), + halign(H_AUTO), + jalign(J_MIDDLE), + valign(V_AUTO), + label_spacing(0), + label_position_tolerance(0), + avoid_edges(false), + minimum_distance(0.0), + minimum_padding(0.0), + max_char_angle_delta(22.5 * M_PI/180.0), + force_odd_labels(false), + allow_overlap(false), + text_ratio(0), + wrap_width(0), + tree_() +{ + +} + +void text_symbolizer_properties::process(processed_text &output, Feature const& feature) const +{ + output.clear(); + if (tree_) { + tree_->apply(default_format, feature, output); + } else { +#ifdef MAPNIK_DEBUG + std::cerr << "Warning: text_symbolizer_properties can't produce text: No formating tree!\n"; +#endif + } +} + +void text_symbolizer_properties::set_format_tree(formating::node_ptr tree) +{ + tree_ = tree; +} + +formating::node_ptr text_symbolizer_properties::format_tree() const +{ + return tree_; +} + +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_; + optional valign_ = get_opt_attr(sym, "vertical-alignment"); + if (valign_) valign = *valign_; + optional text_ratio_ = get_opt_attr(sym, "text-ratio"); + if (text_ratio_) text_ratio = *text_ratio_; + optional wrap_width_ = get_opt_attr(sym, "wrap-width"); + if (wrap_width_) wrap_width = *wrap_width_; + optional label_position_tolerance_ = get_opt_attr(sym, "label-position-tolerance"); + if (label_position_tolerance_) label_position_tolerance = *label_position_tolerance_; + optional spacing_ = get_opt_attr(sym, "spacing"); + if (spacing_) label_spacing = *spacing_; + optional minimum_distance_ = get_opt_attr(sym, "minimum-distance"); + if (minimum_distance_) minimum_distance = *minimum_distance_; + optional min_padding_ = get_opt_attr(sym, "minimum-padding"); + if (min_padding_) minimum_padding = *min_padding_; + optional min_path_length_ = get_opt_attr(sym, "minimum-path-length"); + if (min_path_length_) minimum_path_length = *min_path_length_; + optional avoid_edges_ = get_opt_attr(sym, "avoid-edges"); + if (avoid_edges_) avoid_edges = *avoid_edges_; + optional allow_overlap_ = get_opt_attr(sym, "allow-overlap"); + if (allow_overlap_) allow_overlap = *allow_overlap_; + optional halign_ = get_opt_attr(sym, "horizontal-alignment"); + if (halign_) halign = *halign_; + optional jalign_ = get_opt_attr(sym, "justify-alignment"); + if (jalign_) jalign = *jalign_; + /* Attributes needing special care */ + optional orientation_ = get_opt_attr(sym, "orientation"); + if (orientation_) orientation = parse_expression(*orientation_, "utf8"); + optional dx = get_opt_attr(sym, "dx"); + if (dx) displacement.first = *dx; + optional dy = get_opt_attr(sym, "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); + + optional name_ = get_opt_attr(sym, "name"); + if (name_) { + std::clog << "### WARNING: Using 'name' in TextSymbolizer/ShieldSymbolizer is deprecated!\n"; + set_old_style_expression(parse_expression(*name_, "utf8")); + } + + default_format.from_xml(sym, fontsets); + formating::node_ptr n(formating::node::from_xml(sym)); + if (n) set_format_tree(n); +} + +void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_symbolizer_properties const &dfl) const +{ + if (orientation) + { + const std::string & orientationstr = to_expression_string(*orientation); + if (!dfl.orientation || orientationstr != to_expression_string(*(dfl.orientation)) || explicit_defaults) { + set_attr(node, "orientation", orientationstr); + } + } + + if (displacement.first != dfl.displacement.first || explicit_defaults) + { + set_attr(node, "dx", displacement.first); + } + if (displacement.second != dfl.displacement.second || explicit_defaults) + { + set_attr(node, "dy", displacement.second); + } + if (label_placement != dfl.label_placement || explicit_defaults) + { + set_attr(node, "placement", label_placement); + } + if (valign != dfl.valign || explicit_defaults) + { + set_attr(node, "vertical-alignment", valign); + } + if (text_ratio != dfl.text_ratio || explicit_defaults) + { + set_attr(node, "text-ratio", text_ratio); + } + if (wrap_width != dfl.wrap_width || explicit_defaults) + { + set_attr(node, "wrap-width", wrap_width); + } + if (label_position_tolerance != dfl.label_position_tolerance || explicit_defaults) + { + set_attr(node, "label-position-tolerance", label_position_tolerance); + } + if (label_spacing != dfl.label_spacing || explicit_defaults) + { + set_attr(node, "spacing", label_spacing); + } + if (minimum_distance != dfl.minimum_distance || explicit_defaults) + { + set_attr(node, "minimum-distance", minimum_distance); + } + if (minimum_padding != dfl.minimum_padding || explicit_defaults) + { + set_attr(node, "minimum-padding", minimum_padding); + } + if (minimum_path_length != dfl.minimum_path_length || explicit_defaults) + { + set_attr(node, "minimum-path-length", minimum_path_length); + } + if (allow_overlap != dfl.allow_overlap || explicit_defaults) + { + set_attr(node, "allow-overlap", allow_overlap); + } + if (avoid_edges != dfl.avoid_edges || explicit_defaults) + { + set_attr(node, "avoid-edges", avoid_edges); + } + if (max_char_angle_delta != dfl.max_char_angle_delta || explicit_defaults) + { + set_attr(node, "max-char-angle-delta", max_char_angle_delta); + } + if (halign != dfl.halign || explicit_defaults) + { + set_attr(node, "horizontal-alignment", halign); + } + if (jalign != dfl.jalign || explicit_defaults) + { + set_attr(node, "justify-alignment", jalign); + } + if (valign != dfl.valign || explicit_defaults) + { + set_attr(node, "vertical-alignment", valign); + } + default_format.to_xml(node, explicit_defaults, dfl.default_format); + if (tree_) tree_->to_xml(node); +} + + +std::set text_symbolizer_properties::get_all_expressions() const +{ + std::set result; + if (tree_) tree_->add_expressions(result); + return result; +} + +void text_symbolizer_properties::set_old_style_expression(expression_ptr expr) +{ + tree_ = formating::node_ptr(new formating::text_node(expr)); +} + +char_properties::char_properties() : + text_size(10.0), + character_spacing(0), + line_spacing(0), + text_opacity(1.0), + wrap_before(false), + wrap_char(' '), + text_transform(NONE), + fill(color(0,0,0)), + halo_fill(color(255,255,255)), + halo_radius(0) +{ + +} + +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_; + optional character_spacing_ = get_opt_attr(sym, "character-spacing"); + if (character_spacing_) character_spacing = *character_spacing_; + optional fill_ = get_opt_attr(sym, "fill"); + if (fill_) fill = *fill_; + optional halo_fill_ = get_opt_attr(sym, "halo-fill"); + if (halo_fill_) halo_fill = *halo_fill_; + optional halo_radius_ = get_opt_attr(sym, "halo-radius"); + if (halo_radius_) halo_radius = *halo_radius_; + optional wrap_before_ = get_opt_attr(sym, "wrap-before"); + if (wrap_before_) wrap_before = *wrap_before_; + optional tconvert_ = get_opt_attr(sym, "text-transform"); + if (tconvert_) text_transform = *tconvert_; + optional line_spacing_ = get_opt_attr(sym, "line-spacing"); + if (line_spacing_) line_spacing = *line_spacing_; + optional opacity_ = get_opt_attr(sym, "opacity"); + if (opacity_) text_opacity = *opacity_; + optional wrap_char_ = get_opt_attr(sym, "wrap-character"); + if (wrap_char_ && (*wrap_char_).size() > 0) wrap_char = ((*wrap_char_)[0]); + optional face_name_ = get_opt_attr(sym, "face-name"); + if (face_name_) + { + face_name = *face_name_; + } + optional fontset_name_ = get_opt_attr(sym, "fontset-name"); + if (fontset_name_) { + std::map::const_iterator itr = fontsets.find(*fontset_name_); + if (itr != fontsets.end()) + { + fontset = itr->second; + } else + { + throw config_error("Unable to find any fontset named '" + *fontset_name_ + "'"); + } + } + if (!face_name.empty() && !fontset.get_name().empty()) + { + throw config_error(std::string("Can't have both face-name and fontset-name")); + } + if (face_name.empty() && fontset.get_name().empty()) + { + throw config_error(std::string("Must have face-name or fontset-name")); + } +} + +void char_properties::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl) const +{ + const std::string & fontset_name = fontset.get_name(); + const std::string & dfl_fontset_name = dfl.fontset.get_name(); + if (fontset_name != dfl_fontset_name || explicit_defaults) + { + set_attr(node, "fontset-name", fontset_name); + } + + if (face_name != dfl.face_name || explicit_defaults) + { + set_attr(node, "face-name", face_name); + } + + if (text_size != dfl.text_size || explicit_defaults) + { + set_attr(node, "size", text_size); + } + + if (fill != dfl.fill || explicit_defaults) + { + set_attr(node, "fill", fill); + } + if (halo_radius != dfl.halo_radius || explicit_defaults) + { + set_attr(node, "halo-radius", halo_radius); + } + if (halo_fill != dfl.halo_fill || explicit_defaults) + { + set_attr(node, "halo-fill", halo_fill); + } + if (wrap_before != dfl.wrap_before || explicit_defaults) + { + set_attr(node, "wrap-before", wrap_before); + } + if (wrap_char != dfl.wrap_char || explicit_defaults) + { + set_attr(node, "wrap-character", std::string(1, wrap_char)); + } + if (text_transform != dfl.text_transform || explicit_defaults) + { + set_attr(node, "text-transform", text_transform); + } + if (line_spacing != dfl.line_spacing || explicit_defaults) + { + set_attr(node, "line-spacing", line_spacing); + } + if (character_spacing != dfl.character_spacing || explicit_defaults) + { + set_attr(node, "character-spacing", character_spacing); + } + // for shield_symbolizer this is later overridden + if (text_opacity != dfl.text_opacity || explicit_defaults) + { + set_attr(node, "opacity", text_opacity); + } +} + +/************************************************************************/ + +text_placements::text_placements() : properties() +{ +} + +std::set text_placements::get_all_expressions() +{ + std::set result, tmp; + tmp = properties.get_all_expressions(); + result.insert(tmp.begin(), tmp.end()); + result.insert(properties.orientation); + return result; +} /************************************************************************/ -text_placement_info::text_placement_info(text_placements const* parent): - displacement(parent->displacement_), - text_size(parent->text_size_), halign(parent->halign_), jalign(parent->jalign_), - valign(parent->valign_) +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) { } @@ -55,73 +386,66 @@ bool text_placement_info_dummy::next() return true; } -bool text_placement_info_dummy::next_position_only() +text_placement_info_ptr text_placements_dummy::get_placement_info( + double scale_factor, dimension_type dim, bool has_dimensions) const { - if (position_state) return false; - position_state++; - return true; + return text_placement_info_ptr(new text_placement_info_dummy( + this, scale_factor, dim, has_dimensions)); } -text_placement_info_ptr text_placements_dummy::get_placement_info() const -{ - return text_placement_info_ptr(new text_placement_info_dummy(this)); -} - - /************************************************************************/ bool text_placement_info_simple::next() { - position_state = 0; - if (state == 0) { - text_size = parent_->text_size_; - } else { - if (state > parent_->text_sizes_.size()) return false; - text_size = parent_->text_sizes_[state-1]; + while (1) { + if (state > 0) + { + if (state > parent_->text_sizes_.size()) return false; + properties.default_format.text_size = parent_->text_sizes_[state-1]; + } + if (!next_position_only()) { + state++; + position_state = 0; + } else { + break; + } } - state++; return true; } bool text_placement_info_simple::next_position_only() { + const position &pdisp = parent_->properties.displacement; + position &displacement = properties.displacement; if (position_state >= parent_->direction_.size()) return false; directions_t dir = parent_->direction_[position_state]; switch (dir) { case EXACT_POSITION: - displacement = parent_->displacement_; + displacement = pdisp; break; case NORTH: - displacement = boost::make_tuple(0, -abs(parent_->displacement_.get<1>())); + displacement = std::make_pair(0, -abs(pdisp.second)); break; case EAST: - displacement = boost::make_tuple(abs(parent_->displacement_.get<0>()), 0); + displacement = std::make_pair(abs(pdisp.first), 0); break; case SOUTH: - displacement = boost::make_tuple(0, abs(parent_->displacement_.get<1>())); + displacement = std::make_pair(0, abs(pdisp.second)); break; case WEST: - displacement = boost::make_tuple(-abs(parent_->displacement_.get<0>()), 0); + displacement = std::make_pair(-abs(pdisp.first), 0); break; case NORTHEAST: - displacement = boost::make_tuple( - abs(parent_->displacement_.get<0>()), - -abs(parent_->displacement_.get<1>())); + displacement = std::make_pair(abs(pdisp.first), -abs(pdisp.second)); break; case SOUTHEAST: - displacement = boost::make_tuple( - abs(parent_->displacement_.get<0>()), - abs(parent_->displacement_.get<1>())); + displacement = std::make_pair(abs(pdisp.first), abs(pdisp.second)); break; case NORTHWEST: - displacement = boost::make_tuple( - -abs(parent_->displacement_.get<0>()), - -abs(parent_->displacement_.get<1>())); + displacement = std::make_pair(-abs(pdisp.first), -abs(pdisp.second)); break; case SOUTHWEST: - displacement = boost::make_tuple( - -abs(parent_->displacement_.get<0>()), - abs(parent_->displacement_.get<1>())); + displacement = std::make_pair(-abs(pdisp.first), abs(pdisp.second)); break; default: std::cerr << "WARNING: Unknown placement\n"; @@ -130,13 +454,14 @@ 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)); } -/** Positiion string: [POS][SIZE] +/** 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 @@ -191,4 +516,77 @@ 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_ +} + +/***************************************************************************/ + +bool text_placement_info_list::next() +{ + if (state == 0) { + properties = parent_->properties; + } else { + if (state-1 >= parent_->list_.size()) return false; + properties = parent_->list_[state-1]; + } + state++; + return true; +} + +text_symbolizer_properties & text_placements_list::add() +{ + if (list_.size()) { + text_symbolizer_properties &last = list_.back(); + list_.push_back(last); //Preinitialize with old values + } else { + list_.push_back(properties); + } + return list_.back(); +} + +text_symbolizer_properties & text_placements_list::get(unsigned i) +{ + return list_[i]; +} + +/***************************************************************************/ + +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, + scale_factor, dim, has_dimensions)); +} + +text_placements_list::text_placements_list() : text_placements(), list_(0) +{ + +} + +std::set text_placements_list::get_all_expressions() +{ + std::set result, tmp; + tmp = properties.get_all_expressions(); + result.insert(tmp.begin(), tmp.end()); + result.insert(properties.orientation); + + std::vector::const_iterator it; + for (it=list_.begin(); it != list_.end(); it++) + { + tmp = it->get_all_expressions(); + result.insert(tmp.begin(), tmp.end()); + result.insert(it->orientation); + } + return result; +} + +unsigned text_placements_list::size() const +{ + return list_.size(); +} + + } //namespace diff --git a/src/text_processing.cpp b/src/text_processing.cpp new file mode 100644 index 000000000..b571e34d2 --- /dev/null +++ b/src/text_processing.cpp @@ -0,0 +1,399 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace mapnik { +using boost::property_tree::ptree; +using boost::optional; + +namespace formating { + +void node::to_xml(boost::property_tree::ptree &xml) const +{ + //TODO: Should this throw a config_error? +#ifdef MAPNIK_DEBUG + std::cerr << "Error: Trying to write unsupported node type to XML.\n"; +#endif +} + +node_ptr node::from_xml(boost::property_tree::ptree const& xml) +{ + 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(); + } +} + +void node::add_expressions(std::set &expressions) const +{ + //Do nothing by default +} + +/************************************************************/ + +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 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_text::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 text_node::get_text() const +{ + return text_; +} + +/************************************************************/ + +format_node::format_node(): + node(), + fill_(), + child_() +{ + +} + +void format_node::to_xml(ptree &xml) const +{ + 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); +} + + +node_ptr format_node::from_xml(ptree const& xml) +{ + 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... */ + 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 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 format_node::set_text_size(optional text_size) +{ + text_size_ = text_size; +} + +void format_node::set_character_spacing(optional character_spacing) +{ + character_spacing_ = character_spacing; +} + +void format_node::set_line_spacing(optional line_spacing) +{ + line_spacing_ = line_spacing; +} + +void format_node::set_text_opacity(optional text_opacity) +{ + text_opacity_ = text_opacity; +} + +void format_node::set_wrap_before(optional wrap_before) +{ + wrap_before_ = wrap_before; +} + +void format_node::set_wrap_char(optional wrap_char) +{ + wrap_char_ = wrap_char; +} + +void format_node::set_text_transform(optional text_transform) +{ + text_transform_ = text_transform; +} + +void format_node::set_fill(optional c) +{ + fill_ = c; +} + +void format_node::set_halo_fill(optional c) +{ + halo_fill_ = c; +} + +void format_node::set_halo_radius(optional radius) +{ + halo_radius_ = radius; +} +} //namespace formating + +/************************************************************/ + +void processed_text::push_back(processed_expression const& exp) +{ + expr_list_.push_back(exp); +} + +processed_text::expression_list::const_iterator processed_text::begin() const +{ + return expr_list_.begin(); +} + +processed_text::expression_list::const_iterator processed_text::end() const +{ + return expr_list_.end(); +} + +processed_text::processed_text(face_manager & font_manager, double scale_factor) + : font_manager_(font_manager), scale_factor_(scale_factor) +{ + +} + +void processed_text::clear() +{ + info_.clear(); + expr_list_.clear(); +} + + +string_info &processed_text::get_string_info() +{ + 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) + { + char_properties const &p = itr->p; + face_set_ptr faces = font_manager_.get_face_set(p.face_name, p.fontset); + if (faces->size() <= 0) + { + throw config_error("Unable to find specified font face '" + p.face_name + "'"); + } + faces->set_character_sizes(p.text_size * scale_factor_); + faces->get_string_info(info_, itr->str, &(itr->p)); + info_.add_text(itr->str); + } + return info_; +} + + + +} /* namespace */ diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp index 00a91401d..ed67c4c8e 100644 --- a/src/text_symbolizer.cpp +++ b/src/text_symbolizer.cpp @@ -26,6 +26,7 @@ #include // boost #include +#include namespace mapnik { @@ -85,462 +86,363 @@ static const char * text_transform_strings[] = { IMPLEMENT_ENUM( text_transform_e, text_transform_strings ) +text_symbolizer::text_symbolizer(text_placements_ptr placements) + : symbolizer_base(), + placement_options_(placements) +{ + +} text_symbolizer::text_symbolizer(expression_ptr name, std::string const& face_name, float size, color const& fill, text_placements_ptr placements) : symbolizer_base(), - name_(name), - face_name_(face_name), - //fontset_(default_fontset), - text_ratio_(0), - wrap_width_(0), - wrap_char_(' '), - text_transform_(NONE), - line_spacing_(0), - character_spacing_(0), - label_spacing_(0), - label_position_tolerance_(0), - force_odd_labels_(false), - max_char_angle_delta_(22.5 * M_PI/180.0), - fill_(fill), - halo_fill_(color(255,255,255)), - halo_radius_(0.0), - label_p_(POINT_PLACEMENT), - anchor_(0.0,0.5), - avoid_edges_(false), - minimum_distance_(0.0), - minimum_padding_(0.0), - minimum_path_length_(0.0), - overlap_(false), - text_opacity_(1.0), - wrap_before_(false), placement_options_(placements) { + set_name(name); + set_face_name(face_name); set_text_size(size); + set_fill(fill); } text_symbolizer::text_symbolizer(expression_ptr name, float size, color const& fill, text_placements_ptr placements) : symbolizer_base(), - name_(name), - //face_name_(""), - //fontset_(default_fontset), - text_ratio_(0), - wrap_width_(0), - wrap_char_(' '), - text_transform_(NONE), - line_spacing_(0), - character_spacing_(0), - label_spacing_(0), - label_position_tolerance_(0), - force_odd_labels_(false), - max_char_angle_delta_(22.5 * M_PI/180.0), - fill_(fill), - halo_fill_(color(255,255,255)), - halo_radius_(0.0), - label_p_(POINT_PLACEMENT), - anchor_(0.0,0.5), - avoid_edges_(false), - minimum_distance_(0.0), - minimum_padding_(0.0), - minimum_path_length_(0.0), - overlap_(false), - text_opacity_(1.0), - wrap_before_(false), placement_options_(placements) { + set_name(name); set_text_size(size); + set_fill(fill); } text_symbolizer::text_symbolizer(text_symbolizer const& rhs) : symbolizer_base(rhs), - name_(rhs.name_), - orientation_(rhs.orientation_), - face_name_(rhs.face_name_), - fontset_(rhs.fontset_), - text_ratio_(rhs.text_ratio_), - wrap_width_(rhs.wrap_width_), - wrap_char_(rhs.wrap_char_), - text_transform_(rhs.text_transform_), - line_spacing_(rhs.line_spacing_), - character_spacing_(rhs.character_spacing_), - label_spacing_(rhs.label_spacing_), - label_position_tolerance_(rhs.label_position_tolerance_), - force_odd_labels_(rhs.force_odd_labels_), - max_char_angle_delta_(rhs.max_char_angle_delta_), - fill_(rhs.fill_), - halo_fill_(rhs.halo_fill_), - halo_radius_(rhs.halo_radius_), - label_p_(rhs.label_p_), - anchor_(rhs.anchor_), - avoid_edges_(rhs.avoid_edges_), - minimum_distance_(rhs.minimum_distance_), - minimum_padding_(rhs.minimum_padding_), - minimum_path_length_(rhs.minimum_path_length_), - overlap_(rhs.overlap_), - text_opacity_(rhs.text_opacity_), - wrap_before_(rhs.wrap_before_), - placement_options_(rhs.placement_options_) /*TODO: Copy options! */ {} + placement_options_(rhs.placement_options_) /*TODO: Copy options! */ +{ +} text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other) { if (this == &other) return *this; - name_ = other.name_; - orientation_ = other.orientation_; - face_name_ = other.face_name_; - fontset_ = other.fontset_; - text_ratio_ = other.text_ratio_; - wrap_width_ = other.wrap_width_; - wrap_char_ = other.wrap_char_; - text_transform_ = other.text_transform_; - line_spacing_ = other.line_spacing_; - character_spacing_ = other.character_spacing_; - label_spacing_ = other.label_spacing_; - label_position_tolerance_ = other.label_position_tolerance_; - force_odd_labels_ = other.force_odd_labels_; - max_char_angle_delta_ = other.max_char_angle_delta_; - fill_ = other.fill_; - halo_fill_ = other.halo_fill_; - halo_radius_ = other.halo_radius_; - label_p_ = other.label_p_; - anchor_ = other.anchor_; - avoid_edges_ = other.avoid_edges_; - minimum_distance_ = other.minimum_distance_; - minimum_padding_ = other.minimum_padding_; - minimum_path_length_ = other.minimum_path_length_; - overlap_ = other.overlap_; - text_opacity_ = other.text_opacity_; - wrap_before_ = other.wrap_before_; - placement_options_ = other.placement_options_; /*TODO?*/ - std::cout << "TODO: Metawriter (text_symbolizer::operator=)\n"; + placement_options_ = other.placement_options_; /*TODO: Copy options? */ + std::clog << "TODO: Metawriter (text_symbolizer::operator=)\n"; return *this; } expression_ptr text_symbolizer::get_name() const { - return name_; + return expression_ptr(); } void text_symbolizer::set_name(expression_ptr name) { - name_ = name; + placement_options_->properties.set_old_style_expression(name); } expression_ptr text_symbolizer::get_orientation() const { - return orientation_; + return placement_options_->properties.orientation; } void text_symbolizer::set_orientation(expression_ptr orientation) { - orientation_ = orientation; + placement_options_->properties.orientation = orientation; } std::string const& text_symbolizer::get_face_name() const { - return face_name_; + return placement_options_->properties.default_format.face_name; } void text_symbolizer::set_face_name(std::string face_name) { - face_name_ = face_name; + placement_options_->properties.default_format.face_name = face_name; } void text_symbolizer::set_fontset(font_set const& fontset) { - fontset_ = fontset; + placement_options_->properties.default_format.fontset = fontset; } font_set const& text_symbolizer::get_fontset() const { - return fontset_; + return placement_options_->properties.default_format.fontset; } unsigned text_symbolizer::get_text_ratio() const { - return text_ratio_; + return placement_options_->properties.text_ratio; } void text_symbolizer::set_text_ratio(unsigned ratio) { - text_ratio_ = ratio; + placement_options_->properties.text_ratio = ratio; } unsigned text_symbolizer::get_wrap_width() const { - return wrap_width_; + return placement_options_->properties.wrap_width; } void text_symbolizer::set_wrap_width(unsigned width) { - wrap_width_ = width; + placement_options_->properties.wrap_width = width; } bool text_symbolizer::get_wrap_before() const { - return wrap_before_; + return placement_options_->properties.default_format.wrap_before; } void text_symbolizer::set_wrap_before(bool wrap_before) { - wrap_before_ = wrap_before; + placement_options_->properties.default_format.wrap_before = wrap_before; } unsigned char text_symbolizer::get_wrap_char() const { - return wrap_char_; + return placement_options_->properties.default_format.wrap_char; } std::string text_symbolizer::get_wrap_char_string() const { - return std::string(1, wrap_char_); + return std::string(1, placement_options_->properties.default_format.wrap_char); } void text_symbolizer::set_wrap_char(unsigned char character) { - wrap_char_ = character; + placement_options_->properties.default_format.wrap_char = character; } void text_symbolizer::set_wrap_char_from_string(std::string const& character) { - wrap_char_ = (character)[0]; + placement_options_->properties.default_format.wrap_char = (character)[0]; } text_transform_e text_symbolizer::get_text_transform() const { - return text_transform_; + return placement_options_->properties.default_format.text_transform; } void text_symbolizer::set_text_transform(text_transform_e convert) { - text_transform_ = convert; + placement_options_->properties.default_format.text_transform = convert; } unsigned text_symbolizer::get_line_spacing() const { - return line_spacing_; + return placement_options_->properties.default_format.line_spacing; } void text_symbolizer::set_line_spacing(unsigned spacing) { - line_spacing_ = spacing; + placement_options_->properties.default_format.line_spacing = spacing; } unsigned text_symbolizer::get_character_spacing() const { - return character_spacing_; + return placement_options_->properties.default_format.character_spacing; } void text_symbolizer::set_character_spacing(unsigned spacing) { - character_spacing_ = spacing; + placement_options_->properties.default_format.character_spacing = spacing; } unsigned text_symbolizer::get_label_spacing() const { - return label_spacing_; + return placement_options_->properties.label_spacing; } void text_symbolizer::set_label_spacing(unsigned spacing) { - label_spacing_ = spacing; + placement_options_->properties.label_spacing = spacing; } unsigned text_symbolizer::get_label_position_tolerance() const { - return label_position_tolerance_; + return placement_options_->properties.label_position_tolerance; } void text_symbolizer::set_label_position_tolerance(unsigned tolerance) { - label_position_tolerance_ = tolerance; + placement_options_->properties.label_position_tolerance = tolerance; } bool text_symbolizer::get_force_odd_labels() const { - return force_odd_labels_; + return placement_options_->properties.force_odd_labels; } void text_symbolizer::set_force_odd_labels(bool force) { - force_odd_labels_ = force; + placement_options_->properties.force_odd_labels = force; } double text_symbolizer::get_max_char_angle_delta() const { - return max_char_angle_delta_; + return placement_options_->properties.max_char_angle_delta; } void text_symbolizer::set_max_char_angle_delta(double angle) { - max_char_angle_delta_ = angle; + placement_options_->properties.max_char_angle_delta = angle; } void text_symbolizer::set_text_size(float size) { - placement_options_->set_default_text_size(size); + placement_options_->properties.default_format.text_size = size; } float text_symbolizer::get_text_size() const { - return placement_options_->get_default_text_size(); + return placement_options_->properties.default_format.text_size; } void text_symbolizer::set_fill(color const& fill) { - fill_ = fill; + placement_options_->properties.default_format.fill = fill; } color const& text_symbolizer::get_fill() const { - return fill_; + return placement_options_->properties.default_format.fill; } void text_symbolizer::set_halo_fill(color const& fill) { - halo_fill_ = fill; + placement_options_->properties.default_format.halo_fill = fill; } color const& text_symbolizer::get_halo_fill() const { - return halo_fill_; + return placement_options_->properties.default_format.halo_fill; } void text_symbolizer::set_halo_radius(double radius) { - halo_radius_ = radius; + placement_options_->properties.default_format.halo_radius = radius; } double text_symbolizer::get_halo_radius() const { - return halo_radius_; + return placement_options_->properties.default_format.halo_radius; } void text_symbolizer::set_label_placement(label_placement_e label_p) { - label_p_ = label_p; + placement_options_->properties.label_placement = label_p; } label_placement_e text_symbolizer::get_label_placement() const { - return label_p_; + return placement_options_->properties.label_placement; } -void text_symbolizer::set_anchor(double x, double y) +void text_symbolizer::set_displacement(double x, double y) { - anchor_ = boost::make_tuple(x,y); -} - -position const& text_symbolizer::get_anchor() const -{ - return anchor_; -} - -void text_symbolizer::set_displacement(double x, double y) -{ - placement_options_->set_default_displacement(boost::make_tuple(x,y)); + placement_options_->properties.displacement = std::make_pair(x,y); } void text_symbolizer::set_displacement(position const& p) { - placement_options_->set_default_displacement(p); + placement_options_->properties.displacement = p; } position const& text_symbolizer::get_displacement() const { - return placement_options_->get_default_displacement(); + return placement_options_->properties.displacement; } bool text_symbolizer::get_avoid_edges() const { - return avoid_edges_; + return placement_options_->properties.avoid_edges; } void text_symbolizer::set_avoid_edges(bool avoid) { - avoid_edges_ = avoid; + placement_options_->properties.avoid_edges = avoid; } double text_symbolizer::get_minimum_distance() const { - return minimum_distance_; + return placement_options_->properties.minimum_distance; } void text_symbolizer::set_minimum_distance(double distance) { - minimum_distance_ = distance; + placement_options_->properties.minimum_distance = distance; } double text_symbolizer::get_minimum_padding() const { - return minimum_padding_; + return placement_options_->properties.minimum_padding; } void text_symbolizer::set_minimum_padding(double distance) { - minimum_padding_ = distance; + placement_options_->properties.minimum_padding = distance; } double text_symbolizer::get_minimum_path_length() const { - return minimum_path_length_; + return placement_options_->properties.minimum_path_length; } void text_symbolizer::set_minimum_path_length(double size) { - minimum_path_length_ = size; + placement_options_->properties.minimum_path_length = size; } void text_symbolizer::set_allow_overlap(bool overlap) { - overlap_ = overlap; + placement_options_->properties.allow_overlap = overlap; } bool text_symbolizer::get_allow_overlap() const { - return overlap_; + return placement_options_->properties.allow_overlap; } void text_symbolizer::set_text_opacity(double text_opacity) { - text_opacity_ = text_opacity; + placement_options_->properties.default_format.text_opacity = text_opacity; } double text_symbolizer::get_text_opacity() const { - return text_opacity_; + return placement_options_->properties.default_format.text_opacity; } void text_symbolizer::set_vertical_alignment(vertical_alignment_e valign) { - placement_options_->set_default_valign(valign); + placement_options_->properties.valign = valign; } vertical_alignment_e text_symbolizer::get_vertical_alignment() const { - return placement_options_->get_default_valign(); + return placement_options_->properties.valign; } void text_symbolizer::set_horizontal_alignment(horizontal_alignment_e halign) { - placement_options_->set_default_halign(halign); + placement_options_->properties.halign = halign; } horizontal_alignment_e text_symbolizer::get_horizontal_alignment() const { - return placement_options_->get_default_halign(); + return placement_options_->properties.halign; } void text_symbolizer::set_justify_alignment(justify_alignment_e jalign) { - placement_options_->set_default_jalign(jalign); + placement_options_->properties.jalign = jalign; } justify_alignment_e text_symbolizer::get_justify_alignment() const { - return placement_options_->get_default_jalign(); + return placement_options_->properties.jalign; } text_placements_ptr text_symbolizer::get_placement_options() const diff --git a/tests/data/good_maps/also_and_else_filter.xml b/tests/data/good_maps/also_and_else_filter.xml index c4acbe946..10d7c3ac5 100644 --- a/tests/data/good_maps/also_and_else_filter.xml +++ b/tests/data/good_maps/also_and_else_filter.xml @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/tests/data/good_maps/glyph_symbolizer.xml b/tests/data/good_maps/glyph_symbolizer.xml deleted file mode 100644 index 62f73d999..000000000 --- a/tests/data/good_maps/glyph_symbolizer.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - \ No newline at end of file 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/data/good_maps/sqlite_attachdb.xml b/tests/data/good_maps/sqlite_attachdb.xml index 2703bd165..476e4c853 100644 --- a/tests/data/good_maps/sqlite_attachdb.xml +++ b/tests/data/good_maps/sqlite_attachdb.xml @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/tests/data/good_maps/unique_filter_map.xml b/tests/data/good_maps/unique_filter_map.xml index acb3485e8..796d90c6d 100644 --- a/tests/data/good_maps/unique_filter_map.xml +++ b/tests/data/good_maps/unique_filter_map.xml @@ -22,4 +22,4 @@ shape - \ No newline at end of file + diff --git a/tests/data/placement/clean.sh b/tests/data/placement/clean.sh deleted file mode 100755 index a6f192b13..000000000 --- a/tests/data/placement/clean.sh +++ /dev/null @@ -1,5 +0,0 @@ -rm -f list-[0-9][0-9]0-agg.png -rm -f simple-[0-9][0-9]0-agg.png -rm -f simple-{E,N,NE,NW,N,SE,SW,S,W}-500-agg.png -rm -f list-out.xml simple-out.xml - diff --git a/tests/python_tests/glyph_symbolizer_test.py b/tests/python_tests/glyph_symbolizer_test.py deleted file mode 100644 index bdf6d8a8c..000000000 --- a/tests/python_tests/glyph_symbolizer_test.py +++ /dev/null @@ -1,110 +0,0 @@ -#encoding: utf8 -#!/usr/bin/env python - -from nose.tools import * - -from utilities import execution_path, save_data, contains_word - -import os, mapnik - -def test_renders_with_agg(): - sym = mapnik.GlyphSymbolizer("DejaVu Sans Condensed", - mapnik.Expression("'í'")) - sym.allow_overlap = True - sym.angle = mapnik.Expression("[azimuth]+90") #+90 so the top of the glyph points upwards - sym.size = mapnik.Expression("[value]") - sym.color = mapnik.Expression("'#ff0000'") - - _map = create_map_and_append_symbolyzer(sym) - if _map: - im = mapnik.Image(_map.width,_map.height) - mapnik.render(_map, im) - save_data('agg_glyph_symbolizer.png', im.tostring('png')) - assert contains_word('\xff\x00\x00\xff', im.tostring()) - -def test_renders_with_cairo(): - if not mapnik.has_pycairo(): - return - sym = mapnik.GlyphSymbolizer("DejaVu Sans Condensed", - mapnik.Expression("'í'")) - sym.allow_overlap = True - sym.angle = mapnik.Expression("[azimuth]+90") #+90 so the top of the glyph points upwards - sym.size = mapnik.Expression("[value]") - sym.color = mapnik.Expression("'#ff0000'") - _map = create_map_and_append_symbolyzer(sym) - if _map: - from cStringIO import StringIO - import cairo - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 256, 256) - mapnik.render(_map, surface) - im = mapnik.Image.from_cairo(surface) - save_data('cairo_glyph_symbolizer.png', im.tostring('png')) - assert contains_word('\xff\x00\x00\xff', im.tostring()) - -def test_load_save_load_map(): - map = mapnik.Map(256,256) - in_map = "../data/good_maps/glyph_symbolizer.xml" - try: - mapnik.load_map(map, in_map) - style = map.find_style('arrows') - sym = style.rules[0].symbols[0] - assert isinstance(sym, mapnik.GlyphSymbolizer) - assert sym.angle_mode == mapnik.angle_mode.AZIMUTH - - out_map = mapnik.save_map_to_string(map).decode('utf8') - map = mapnik.Map(256,256) - mapnik.load_map_from_string(map, out_map.encode('utf8')) - assert 'GlyphSymbolizer' in out_map - # make sure non-ascii characters are well supported since most interesting - # glyphs for symbology are usually in that range - assert u'í' in out_map, out_map - except RuntimeError, e: - # only test datasources that we have installed - if not 'Could not create datasource' in str(e): - raise RuntimeError(e) - -# -# Utilities and setup code -# - -def setup(): - # All of the paths used are relative, if we run the tests - # from another directory we need to chdir() - os.chdir(execution_path('.')) - -def create_map_and_append_symbolyzer(sym): - srs = '+init=epsg:32630' - lyr = mapnik.Layer('arrows') - try: - lyr.datasource = mapnik.Shapefile( - file = '../data/shp/arrows.shp', - ) - lyr.srs = srs - _map = mapnik.Map(256,256, srs) - style = mapnik.Style() - rule = mapnik.Rule() - rule.symbols.append(sym) - - # put a test symbolizer to see what is the azimuth being read - ts = mapnik.TextSymbolizer(mapnik.Expression('[azimuth]'), - "DejaVu Sans Book", - 10, - mapnik.Color("black")) - ts.allow_overlap = True - rule.symbols.append(ts) - - style.rules.append(rule) - _map.append_style('foo', style) - lyr.styles.append('foo') - _map.layers.append(lyr) - _map.zoom_to_box(mapnik.Box2d(0,0,8,8)) - return _map - except RuntimeError, e: - # only test datasources that we have installed - if not 'Could not create datasource' in str(e): - raise RuntimeError(e) - -if __name__ == "__main__": - setup() - [eval(run)() for run in dir() if 'test_' in run] - diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 9313bc507..7981cb16a 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -24,12 +24,11 @@ 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) eq_(s.character_spacing,0) - eq_(str(s.name), str(mapnik.Expression('[Field Name]'))) + #eq_(str(s.name), str(mapnik2.Expression('[Field Name]'))) name field is no longer supported eq_(s.face_name, 'DejaVu Sans Bold') eq_(s.allow_overlap, False) eq_(s.fill, mapnik.Color('#000000')) @@ -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) @@ -198,7 +197,7 @@ def test_linesymbolizer_init(): def test_textsymbolizer_init(): ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black')) - eq_(str(ts.name), str(mapnik.Expression('[Field_Name]'))) +# eq_(str(ts.name), str(mapnik2.Expression('[Field_Name]'))) name field is no longer supported eq_(ts.face_name, 'Font Name') eq_(ts.text_size, 8) eq_(ts.fill, mapnik.Color('black')) 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/clean.sh b/tests/visual_tests/clean.sh new file mode 100755 index 000000000..5184aa6ca --- /dev/null +++ b/tests/visual_tests/clean.sh @@ -0,0 +1,5 @@ +rm -f list-[0-9][0-9]0-agg.png +rm -f simple-[0-9][0-9]0-agg.png +rm -f *-500-agg.png +rm -f *-out.xml + 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/list-100-reference.png b/tests/visual_tests/list-100-reference.png new file mode 100644 index 000000000..3b3cedbb2 Binary files /dev/null and b/tests/visual_tests/list-100-reference.png differ diff --git a/tests/visual_tests/list-150-reference.png b/tests/visual_tests/list-150-reference.png new file mode 100644 index 000000000..61dc7964d Binary files /dev/null and b/tests/visual_tests/list-150-reference.png differ diff --git a/tests/visual_tests/list-200-reference.png b/tests/visual_tests/list-200-reference.png new file mode 100644 index 000000000..14714cdf0 Binary files /dev/null and b/tests/visual_tests/list-200-reference.png differ diff --git a/tests/visual_tests/list-250-reference.png b/tests/visual_tests/list-250-reference.png new file mode 100644 index 000000000..a88f4707b Binary files /dev/null and b/tests/visual_tests/list-250-reference.png differ diff --git a/tests/visual_tests/list-300-reference.png b/tests/visual_tests/list-300-reference.png new file mode 100644 index 000000000..5d500aad9 Binary files /dev/null and b/tests/visual_tests/list-300-reference.png differ diff --git a/tests/visual_tests/list-400-reference.png b/tests/visual_tests/list-400-reference.png new file mode 100644 index 000000000..caee65ba5 Binary files /dev/null and b/tests/visual_tests/list-400-reference.png differ diff --git a/tests/visual_tests/list-600-reference.png b/tests/visual_tests/list-600-reference.png new file mode 100644 index 000000000..46a5c7198 Binary files /dev/null and b/tests/visual_tests/list-600-reference.png differ diff --git a/tests/visual_tests/list-800-reference.png b/tests/visual_tests/list-800-reference.png new file mode 100644 index 000000000..b362592ef Binary files /dev/null and b/tests/visual_tests/list-800-reference.png differ diff --git a/tests/visual_tests/list.xml b/tests/visual_tests/list.xml new file mode 100644 index 000000000..93ef3726d --- /dev/null +++ b/tests/visual_tests/list.xml @@ -0,0 +1,27 @@ + + + + + + My Style + + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/points.dbf b/tests/visual_tests/points.dbf new file mode 100644 index 000000000..ca544e92a Binary files /dev/null and b/tests/visual_tests/points.dbf differ diff --git a/tests/visual_tests/points.osm b/tests/visual_tests/points.osm new file mode 100644 index 000000000..086f0a479 --- /dev/null +++ b/tests/visual_tests/points.osm @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/visual_tests/points.shp b/tests/visual_tests/points.shp new file mode 100644 index 000000000..e2fb4a604 Binary files /dev/null and b/tests/visual_tests/points.shp differ 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/simple-100-reference.png b/tests/visual_tests/simple-100-reference.png new file mode 100644 index 000000000..e70c9c0f6 Binary files /dev/null and b/tests/visual_tests/simple-100-reference.png differ diff --git a/tests/visual_tests/simple-150-reference.png b/tests/visual_tests/simple-150-reference.png new file mode 100644 index 000000000..666ca6d66 Binary files /dev/null and b/tests/visual_tests/simple-150-reference.png differ diff --git a/tests/visual_tests/simple-200-reference.png b/tests/visual_tests/simple-200-reference.png new file mode 100644 index 000000000..aad33f405 Binary files /dev/null and b/tests/visual_tests/simple-200-reference.png differ diff --git a/tests/visual_tests/simple-250-reference.png b/tests/visual_tests/simple-250-reference.png new file mode 100644 index 000000000..a43a8016f Binary files /dev/null and b/tests/visual_tests/simple-250-reference.png differ diff --git a/tests/visual_tests/simple-300-reference.png b/tests/visual_tests/simple-300-reference.png new file mode 100644 index 000000000..f753151da Binary files /dev/null and b/tests/visual_tests/simple-300-reference.png differ diff --git a/tests/visual_tests/simple-400-reference.png b/tests/visual_tests/simple-400-reference.png new file mode 100644 index 000000000..ebc03be70 Binary files /dev/null and b/tests/visual_tests/simple-400-reference.png differ diff --git a/tests/visual_tests/simple-600-reference.png b/tests/visual_tests/simple-600-reference.png new file mode 100644 index 000000000..ffc72053a Binary files /dev/null and b/tests/visual_tests/simple-600-reference.png differ diff --git a/tests/visual_tests/simple-800-reference.png b/tests/visual_tests/simple-800-reference.png new file mode 100644 index 000000000..19c7acd32 Binary files /dev/null and b/tests/visual_tests/simple-800-reference.png differ diff --git a/tests/data/placement/simple-E-500-reference.png b/tests/visual_tests/simple-E-500-reference.png similarity index 100% rename from tests/data/placement/simple-E-500-reference.png rename to tests/visual_tests/simple-E-500-reference.png diff --git a/tests/data/placement/simple-E.xml b/tests/visual_tests/simple-E.xml similarity index 100% rename from tests/data/placement/simple-E.xml rename to tests/visual_tests/simple-E.xml diff --git a/tests/data/placement/simple-N-500-reference.png b/tests/visual_tests/simple-N-500-reference.png similarity index 100% rename from tests/data/placement/simple-N-500-reference.png rename to tests/visual_tests/simple-N-500-reference.png diff --git a/tests/data/placement/simple-N.xml b/tests/visual_tests/simple-N.xml similarity index 100% rename from tests/data/placement/simple-N.xml rename to tests/visual_tests/simple-N.xml diff --git a/tests/data/placement/simple-NE-500-reference.png b/tests/visual_tests/simple-NE-500-reference.png similarity index 100% rename from tests/data/placement/simple-NE-500-reference.png rename to tests/visual_tests/simple-NE-500-reference.png diff --git a/tests/data/placement/simple-NE.xml b/tests/visual_tests/simple-NE.xml similarity index 100% rename from tests/data/placement/simple-NE.xml rename to tests/visual_tests/simple-NE.xml diff --git a/tests/data/placement/simple-NW-500-reference.png b/tests/visual_tests/simple-NW-500-reference.png similarity index 100% rename from tests/data/placement/simple-NW-500-reference.png rename to tests/visual_tests/simple-NW-500-reference.png diff --git a/tests/data/placement/simple-NW.xml b/tests/visual_tests/simple-NW.xml similarity index 100% rename from tests/data/placement/simple-NW.xml rename to tests/visual_tests/simple-NW.xml diff --git a/tests/data/placement/simple-S-500-reference.png b/tests/visual_tests/simple-S-500-reference.png similarity index 100% rename from tests/data/placement/simple-S-500-reference.png rename to tests/visual_tests/simple-S-500-reference.png diff --git a/tests/data/placement/simple-S.xml b/tests/visual_tests/simple-S.xml similarity index 100% rename from tests/data/placement/simple-S.xml rename to tests/visual_tests/simple-S.xml diff --git a/tests/data/placement/simple-SE-500-reference.png b/tests/visual_tests/simple-SE-500-reference.png similarity index 100% rename from tests/data/placement/simple-SE-500-reference.png rename to tests/visual_tests/simple-SE-500-reference.png diff --git a/tests/data/placement/simple-SE.xml b/tests/visual_tests/simple-SE.xml similarity index 100% rename from tests/data/placement/simple-SE.xml rename to tests/visual_tests/simple-SE.xml diff --git a/tests/data/placement/simple-SW-500-reference.png b/tests/visual_tests/simple-SW-500-reference.png similarity index 100% rename from tests/data/placement/simple-SW-500-reference.png rename to tests/visual_tests/simple-SW-500-reference.png diff --git a/tests/data/placement/simple-SW.xml b/tests/visual_tests/simple-SW.xml similarity index 100% rename from tests/data/placement/simple-SW.xml rename to tests/visual_tests/simple-SW.xml diff --git a/tests/data/placement/simple-W-500-reference.png b/tests/visual_tests/simple-W-500-reference.png similarity index 100% rename from tests/data/placement/simple-W-500-reference.png rename to tests/visual_tests/simple-W-500-reference.png diff --git a/tests/data/placement/simple-W.xml b/tests/visual_tests/simple-W.xml similarity index 100% rename from tests/data/placement/simple-W.xml rename to tests/visual_tests/simple-W.xml diff --git a/tests/visual_tests/simple.xml b/tests/visual_tests/simple.xml new file mode 100644 index 000000000..cc008f598 --- /dev/null +++ b/tests/visual_tests/simple.xml @@ -0,0 +1,24 @@ + + + + + + My Style + + + shape + points.shp + + + + + + diff --git a/tests/data/placement/test.py b/tests/visual_tests/test.py similarity index 73% rename from tests/data/placement/test.py rename to tests/visual_tests/test.py index 1fabccbce..616b0cb4b 100755 --- a/tests/data/placement/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"] + "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,11 +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) \ No newline at end of file + m = render(filename, 500) + mapnik.save_map(m, "%s-out.xml" % filename) + diff --git a/utils/upgrade_map_xml/upgrade_map_xml.py b/utils/upgrade_map_xml/upgrade_map_xml.py index 1a69450d0..64fa17f87 100755 --- a/utils/upgrade_map_xml/upgrade_map_xml.py +++ b/utils/upgrade_map_xml/upgrade_map_xml.py @@ -204,9 +204,6 @@ def upgrade(input_xml,output_xml=None,indent_xml=True): for sym in rule.findall('BuildingSymbolizer') or []: fixup_sym_attributes(sym) underscore2dash(sym) - for sym in rule.findall('GlyphSymbolizer') or []: - fixup_sym_attributes(sym) - underscore2dash(sym) for sym in rule.findall('MarkersSymbolizer') or []: fixup_sym_attributes(sym) underscore2dash(sym) 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" diff --git a/workspace/bindings.pri b/workspace/bindings.pri index acd4f6753..2798142e7 100644 --- a/workspace/bindings.pri +++ b/workspace/bindings.pri @@ -17,7 +17,6 @@ SOURCES += \ $$PWD/../bindings/python/mapnik_featureset.cpp \ $$PWD/../bindings/python/mapnik_font_engine.cpp \ $$PWD/../bindings/python/mapnik_geometry.cpp \ - $$PWD/../bindings/python/mapnik_glyph_symbolizer.cpp \ $$PWD/../bindings/python/mapnik_grid.cpp \ $$PWD/../bindings/python/mapnik_grid_view.cpp \ $$PWD/../bindings/python/mapnik_image.cpp \ diff --git a/workspace/mapnik.pro b/workspace/mapnik.pro index 666252555..5f9f9c615 100644 --- a/workspace/mapnik.pro +++ b/workspace/mapnik.pro @@ -80,7 +80,6 @@ HEADERS += \ ../include/mapnik/geometry.hpp \ ../include/mapnik/geom_util.hpp \ ../include/mapnik/global.hpp \ - ../include/mapnik/glyph_symbolizer.hpp \ ../include/mapnik/gradient.hpp \ ../include/mapnik/graphics.hpp \ ../include/mapnik/hextree.hpp \ @@ -92,7 +91,6 @@ HEADERS += \ ../include/mapnik/image_view.hpp \ ../include/mapnik/jpeg_io.hpp \ ../include/mapnik/label_collision_detector.hpp \ - ../include/mapnik/label_placement.hpp \ ../include/mapnik/layer.hpp \ ../include/mapnik/libxml2_loader.hpp \ ../include/mapnik/line_pattern_symbolizer.hpp \ @@ -162,7 +160,6 @@ HEADERS += \ SOURCES += \ ../src/agg/agg_renderer.cpp \ ../src/agg/process_building_symbolizer.cpp \ - ../src/agg/process_glyph_symbolizer.cpp \ ../src/agg/process_line_pattern_symbolizer.cpp \ ../src/agg/process_line_symbolizer.cpp \ ../src/agg/process_markers_symbolizer.cpp \ @@ -174,7 +171,6 @@ SOURCES += \ ../src/agg/process_text_symbolizer.cpp \ ../src/grid/grid_renderer.cpp \ ../src/grid/process_building_symbolizer.cpp \ - ../src/grid/process_glyph_symbolizer.cpp \ ../src/grid/process_line_pattern_symbolizer.cpp \ ../src/grid/process_line_symbolizer.cpp \ ../src/grid/process_markers_symbolizer.cpp \ @@ -188,7 +184,6 @@ SOURCES += \ ../src/svg/svg_output_attributes.cpp \ ../src/svg/process_symbolizers.cpp \ ../src/svg/process_building_symbolizer.cpp \ - ../src/svg/process_glyph_symbolizer.cpp \ ../src/svg/process_line_pattern_symbolizer.cpp \ ../src/svg/process_line_symbolizer.cpp \ ../src/svg/process_markers_symbolizer.cpp \ @@ -210,7 +205,6 @@ SOURCES += \ ../src/filter_factory.cpp \ ../src/font_engine_freetype.cpp \ ../src/font_set.cpp \ - ../src/glyph_symbolizer.cpp \ ../src/gradient.cpp \ ../src/graphics.cpp \ ../src/image_reader.cpp \