From d7fcefc848cf9bebb71f81e307b7f6424afde993 Mon Sep 17 00:00:00 2001 From: Artem Pavlenko Date: Fri, 27 Oct 2006 17:29:39 +0000 Subject: [PATCH] 1. applied text-wrap patch from Robert Coup. Thanks! 2. by default use tinyxml as a property_tree parser 3. modified load_map.cpp to allow to test new features --- bindings/python/mapnik_map.cpp | 1 + bindings/python/mapnik_text_symbolizer.cpp | 8 +- include/mapnik/font_engine_freetype.hpp | 14 +- include/mapnik/line_pattern_symbolizer.hpp | 2 +- include/mapnik/placement_finder.hpp | 5 +- include/mapnik/text_symbolizer.hpp | 13 +- src/SConscript | 9 +- src/agg_renderer.cpp | 2 + src/load_map.cpp | 34 +- src/placement_finder.cpp | 735 +++++++++++++-------- src/text_symbolizer.cpp | 116 ++-- 11 files changed, 592 insertions(+), 347 deletions(-) diff --git a/bindings/python/mapnik_map.cpp b/bindings/python/mapnik_map.cpp index 5cf5dc1c8..c19ac28e8 100644 --- a/bindings/python/mapnik_map.cpp +++ b/bindings/python/mapnik_map.cpp @@ -110,6 +110,7 @@ void export_map() .add_property("layers",make_function (&Map::layers,return_value_policy()), "Get the list of layers in this map.") + .def("find_style",&Map::find_style,return_value_policy()) .def_pickle(map_pickle_suite()) ; } diff --git a/bindings/python/mapnik_text_symbolizer.cpp b/bindings/python/mapnik_text_symbolizer.cpp index efa332734..b065144f8 100644 --- a/bindings/python/mapnik_text_symbolizer.cpp +++ b/bindings/python/mapnik_text_symbolizer.cpp @@ -36,13 +36,19 @@ void export_text_symbolizer() .value("LINE_PLACEMENT",mapnik::line_placement) .value("POINT_PLACEMENT",mapnik::point_placement) ; - + class_("TextSymbolizer", init()) .add_property("halo_fill",make_function( &text_symbolizer::get_halo_fill, return_value_policy()), &text_symbolizer::set_halo_fill) + .add_property("wrap_width", + &text_symbolizer::get_wrap_width, + &text_symbolizer::set_wrap_width) + .add_property("text_ratio", + &text_symbolizer::get_text_ratio, + &text_symbolizer::set_text_ratio) .add_property("halo_radius", &text_symbolizer::get_halo_radius, &text_symbolizer::set_halo_radius) diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp index 2613ec0c0..59b977518 100644 --- a/include/mapnik/font_engine_freetype.hpp +++ b/include/mapnik/font_engine_freetype.hpp @@ -264,6 +264,8 @@ namespace mapnik double x, y, angle; path->vertex(&c, &x, &y, &angle); +// std::clog << " prepare_glyph: " << (unsigned char)c << "," << x << "," << y << "," << angle << std::endl; + FT_BBox glyph_bbox; FT_Glyph image; @@ -363,8 +365,8 @@ namespace mapnik width += char_dim.first; height = char_dim.second > height ? char_dim.second : height; + } - info->set_dimensions(width, height); } @@ -376,10 +378,14 @@ namespace mapnik start.x = unsigned(x0 * (1 << 6)); start.y = unsigned((height - y0) * (1 << 6)); + +// std::clog << "Render text at: " << x0 << "," << y0 << " " << start.x << "," << start.y << std::endl; + // now render transformed glyphs typename glyphs_t::iterator pos; - - if (halo_radius_ > 0) + + //make sure we've got reasonable values. + if (halo_radius_ > 0 && halo_radius_ < 256) { //render halo for ( pos = glyphs_.begin(); pos != glyphs_.end();++pos) @@ -463,6 +469,8 @@ namespace mapnik mapnik::Color fill_; mapnik::Color halo_fill_; int halo_radius_; + unsigned text_ratio_; + unsigned wrap_width_; glyphs_t glyphs_; }; } diff --git a/include/mapnik/line_pattern_symbolizer.hpp b/include/mapnik/line_pattern_symbolizer.hpp index c408eecac..e6a69f308 100644 --- a/include/mapnik/line_pattern_symbolizer.hpp +++ b/include/mapnik/line_pattern_symbolizer.hpp @@ -34,7 +34,7 @@ namespace mapnik line_pattern_symbolizer(std::string const& file, std::string const& type, unsigned width,unsigned height); - + line_pattern_symbolizer(line_pattern_symbolizer const& rhs); ImageData32 const& get_pattern() const; private: diff --git a/include/mapnik/placement_finder.hpp b/include/mapnik/placement_finder.hpp index 55de82395..e9f351dba 100644 --- a/include/mapnik/placement_finder.hpp +++ b/include/mapnik/placement_finder.hpp @@ -67,7 +67,7 @@ namespace mapnik double starting_y; text_path path; - + //helpers std::pair get_position_at_distance(double target_distance); @@ -75,6 +75,9 @@ namespace mapnik void clear_envelopes(); double total_distance_; //cache for distance + + int wrap_width; + int text_ratio; }; class placement_finder : boost::noncopyable diff --git a/include/mapnik/text_symbolizer.hpp b/include/mapnik/text_symbolizer.hpp index f20b582b1..257cf49b6 100644 --- a/include/mapnik/text_symbolizer.hpp +++ b/include/mapnik/text_symbolizer.hpp @@ -38,12 +38,17 @@ namespace mapnik struct MAPNIK_DECL text_symbolizer { - text_symbolizer(std::string const& name,std::string const& face_name, unsigned size,Color const& fill); + text_symbolizer(std::string const& name,std::string const& face_name, + unsigned size,Color const& fill); text_symbolizer(text_symbolizer const& rhs); text_symbolizer& operator=(text_symbolizer const& rhs); std::string const& get_name() const; + unsigned get_text_ratio() const; // target ratio for text bounding box in pixels + void set_text_ratio(unsigned ratio); + unsigned get_wrap_width() const; // target ratio for text bounding box in pixels + void set_wrap_width(unsigned ratio); unsigned get_text_size() const; - std::string const& get_face_name() const; + std::string const& get_face_name() const; Color const& get_fill() const; void set_halo_fill(Color const& fill); Color const& get_halo_fill() const; @@ -57,8 +62,10 @@ namespace mapnik position const& get_displacement() const; private: std::string name_; - std::string face_name_; + std::string face_name_; unsigned size_; + unsigned text_ratio_; + unsigned wrap_width_; Color fill_; Color halo_fill_; unsigned halo_radius_; diff --git a/src/SConscript b/src/SConscript index 60f2de5d7..d4c4a78ba 100644 --- a/src/SConscript +++ b/src/SConscript @@ -62,9 +62,16 @@ source = Split( scale_denominator.cpp """ ) +source += Split( + """ + ../tinyxml/tinystr.cpp + ../tinyxml/tinyxml.cpp + ../tinyxml/tinyxmlerror.cpp + ../tinyxml/tinyxmlparser.cpp + """) mapnik = env.SharedLibrary('mapnik', source, LIBS=libraries, LINKFLAGS=linkflags) env.Alias(target='install', source=env.Install(prefix + '/' + env['LIBDIR_SCHEMA'], mapnik)) -includes = glob.glob('../include/mapnik' + '/*.hpp') +includes = glob.glob('../include/mapnik/*.hpp') env.Alias(target='install', source=env.Install(prefix+'/include/mapnik', includes)) diff --git a/src/agg_renderer.cpp b/src/agg_renderer.cpp index 102364d36..5acd3adf3 100644 --- a/src/agg_renderer.cpp +++ b/src/agg_renderer.cpp @@ -502,6 +502,8 @@ namespace mapnik ren.get_string_info(text, &info); placement text_placement(&info, &t_, &prj_trans, geom, sym.get_label_placement()); + text_placement.text_ratio = sym.get_text_ratio(); + text_placement.wrap_width = sym.get_wrap_width(); bool found = finder_.find_placement(&text_placement); if (!found) { diff --git a/src/load_map.cpp b/src/load_map.cpp index 9e1cca821..32c7d94c1 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -27,9 +27,6 @@ #include #include #include - -// use tinyxml -#define BOOST_PROPERTY_TREE_XML_PARSER_TINYXML #include // mapnik @@ -166,6 +163,37 @@ namespace mapnik { text_symbol.set_label_placement(line_placement); } + // halo fill and radius + boost::optional halo_fill = + sym.second.get_optional(".halo_fill"); + + if (halo_fill) + { + text_symbol.set_halo_fill + (color_factory::from_string(halo_fill->c_str())); + } + boost::optional halo_radius = + sym.second.get_optional(".halo_radius"); + if (halo_radius) + { + text_symbol.set_halo_radius(*halo_radius); + } + + // text ratio and wrap width + boost::optional text_ratio = + sym.second.get_optional(".text_ratio"); + + if (text_ratio) + { + text_symbol.set_text_ratio(*text_ratio); + } + + boost::optional wrap_width = + sym.second.get_optional(".wrap_width"); + if (wrap_width) + { + text_symbol.set_wrap_width(*wrap_width); + } rule.append(text_symbol); } diff --git a/src/placement_finder.cpp b/src/placement_finder.cpp index 40cc5ee73..7a3b12ed3 100644 --- a/src/placement_finder.cpp +++ b/src/placement_finder.cpp @@ -40,339 +40,340 @@ namespace mapnik { - placement::placement(string_info *info_, CoordTransform *ctrans_, const proj_transform *proj_trans_, geometry_ptr geom_, std::pair dimensions_) - : info(info_), ctrans(ctrans_), proj_trans(proj_trans_), geom(geom_), label_placement(point_placement), dimensions(dimensions_), has_dimensions(true), shape_path(*ctrans_, *geom_, *proj_trans_), total_distance_(-1.0) - { - } - - //For text - placement::placement(string_info *info_, CoordTransform *ctrans_, const proj_transform *proj_trans_, geometry_ptr geom_, label_placement_e placement_) - : info(info_), ctrans(ctrans_), proj_trans(proj_trans_), geom(geom_), label_placement(placement_), has_dimensions(false), shape_path(*ctrans_, *geom_, *proj_trans_), total_distance_(-1.0) - { - } - - placement::~placement() - { - } - - std::pair placement::get_position_at_distance(double target_distance) - { - double old_x, old_y, new_x, new_y; - double x, y; - x = y = 0.0; - - double distance = 0.0; - - shape_path.rewind(0); - shape_path.vertex(&new_x,&new_y); - for (unsigned i = 0; i < geom->num_points() - 1; i++) + placement::placement(string_info *info_, CoordTransform *ctrans_, const proj_transform *proj_trans_, geometry_ptr geom_, std::pair dimensions_) + : info(info_), ctrans(ctrans_), proj_trans(proj_trans_), geom(geom_), label_placement(point_placement), dimensions(dimensions_), has_dimensions(true), shape_path(*ctrans_, *geom_, *proj_trans_), total_distance_(-1.0), wrap_width(0), text_ratio(0) { - double dx, dy; - - old_x = new_x; - old_y = new_y; - - shape_path.vertex(&new_x,&new_y); - - dx = new_x - old_x; - dy = new_y - old_y; - - double segment_length = sqrt(dx*dx + dy*dy); - - distance += segment_length; - if (distance > target_distance) - { - x = new_x - dx*(distance - target_distance)/segment_length; - y = new_y - dy*(distance - target_distance)/segment_length; - - break; - } } - - return std::pair(x, y); - } - double placement::get_total_distance() - { - if (total_distance_ < 0.0) + //For text + placement::placement(string_info *info_, CoordTransform *ctrans_, const proj_transform *proj_trans_, geometry_ptr geom_, label_placement_e placement_) + : info(info_), ctrans(ctrans_), proj_trans(proj_trans_), geom(geom_), label_placement(placement_), has_dimensions(false), shape_path(*ctrans_, *geom_, *proj_trans_), total_distance_(-1.0), wrap_width(0), text_ratio(0) { - double old_x, old_y, new_x, new_y; - - shape_path.rewind(0); - - shape_path.vertex(&old_x,&old_y); - - total_distance_ = 0.0; - - for (unsigned i = 0; i < geom->num_points() - 1; i++) - { - double dx, dy; - - shape_path.vertex(&new_x,&new_y); - - dx = new_x - old_x; - dy = new_y - old_y; - - total_distance_ += sqrt(dx*dx + dy*dy); - - old_x = new_x; - old_y = new_y; - } } - - return total_distance_; - } - void placement::clear_envelopes() - { - while (!envelopes.empty()) - envelopes.pop(); - } - - - - placement_finder::placement_finder(Envelope e) - : detector_(e) - { - } - - bool placement_finder::find_placement(placement *p) - { - if (p->label_placement == point_placement) + placement::~placement() { - return find_placement_horizontal(p); } - else if (p->label_placement == line_placement) + + std::pair placement::get_position_at_distance(double target_distance) { - return find_placement_follow(p); - } + double old_x, old_y, new_x, new_y; + double x, y; + x = y = 0.0; - return false; - } - - bool placement_finder::find_placement_follow(placement *p) - { - std::pair string_dimensions = p->info->get_dimensions(); - double string_width = string_dimensions.first; -// double string_height = string_dimensions.second; + double distance = 0.0; - double distance = p->get_total_distance(); - - //~ double delta = string_width/distance; - double delta = distance/100.0; - - for (double i = 0; i < (distance - string_width)/2.0; i += delta) - { - p->clear_envelopes(); - - if ( build_path_follow(p, (distance - string_width)/2.0 + i) ) { - update_detector(p); - return true; - } - - p->clear_envelopes(); - - if ( build_path_follow(p, (distance - string_width)/2.0 - i) ) { - update_detector(p); - return true; - } - } - - p->starting_x = 0; - p->starting_y = 0; - - return false; - } - - bool placement_finder::find_placement_horizontal(placement *p) - { - double distance = p->get_total_distance(); - //~ double delta = string_width/distance; - double delta = distance/100.0; - - for (double i = 0; i < distance/2.0; i += delta) - { - p->clear_envelopes(); - - if ( build_path_horizontal(p, distance/2.0 + i) ) { - update_detector(p); - return true; - } - - p->clear_envelopes(); - - if ( build_path_horizontal(p, distance/2.0 - i) ) { - update_detector(p); - return true; - } - } - - p->starting_x = 0; - p->starting_y = 0; - - return false; - } - - void placement_finder::update_detector(placement *p) - { - while (!p->envelopes.empty()) - { - Envelope e = p->envelopes.front(); - - detector_.insert(e); - - p->envelopes.pop(); - } - } - - bool placement_finder::build_path_follow(placement *p, double target_distance) - { - double new_x, new_y, old_x, old_y; - unsigned cur_node = 0; - - double angle = 0.0; - int orientation = 0; - - p->path.clear(); - - double x, y; - x = y = 0.0; - - double distance = 0.0; - - std::pair string_dimensions = p->info->get_dimensions(); -// double string_width = string_dimensions.first; - double string_height = string_dimensions.second; - - p->shape_path.rewind(0); - p->shape_path.vertex(&new_x,&new_y); - for (unsigned i = 0; i < p->geom->num_points() - 1; i++) - { - double dx, dy; - - cur_node++; - - old_x = new_x; - old_y = new_y; - - p->shape_path.vertex(&new_x,&new_y); - - dx = new_x - old_x; - dy = new_y - old_y; - - double segment_length = sqrt(dx*dx + dy*dy); - - distance += segment_length; - if (distance > target_distance) + shape_path.rewind(0); + shape_path.vertex(&new_x,&new_y); + for (unsigned i = 0; i < geom->num_points() - 1; i++) { - p->starting_x = new_x - dx*(distance - target_distance)/segment_length; - p->starting_y = new_y - dy*(distance - target_distance)/segment_length; + double dx, dy; - angle = atan2(-dy, dx); + old_x = new_x; + old_y = new_y; - if (angle > M_PI/2 || angle <= -M_PI/2) { - orientation = -1; - } - else { - orientation = 1; + shape_path.vertex(&new_x,&new_y); + + dx = new_x - old_x; + dy = new_y - old_y; + + double segment_length = sqrt(dx*dx + dy*dy); + + distance += segment_length; + if (distance > target_distance) + { + x = new_x - dx*(distance - target_distance)/segment_length; + y = new_y - dy*(distance - target_distance)/segment_length; + + break; } + } + + return std::pair(x, y); + } + + double placement::get_total_distance() + { + if (total_distance_ < 0.0) + { + double old_x, old_y, new_x, new_y; + + shape_path.rewind(0); + + shape_path.vertex(&old_x,&old_y); - distance -= target_distance; - - break; + total_distance_ = 0.0; + + for (unsigned i = 0; i < geom->num_points() - 1; i++) + { + double dx, dy; + + shape_path.vertex(&new_x,&new_y); + + dx = new_x - old_x; + dy = new_y - old_y; + + total_distance_ += sqrt(dx*dx + dy*dy); + + old_x = new_x; + old_y = new_y; + } + } + + return total_distance_; + } + + void placement::clear_envelopes() + { + while (!envelopes.empty()) + envelopes.pop(); + } + + + + placement_finder::placement_finder(Envelope e) + : detector_(e) + { + } + + bool placement_finder::find_placement(placement *p) + { + if (p->label_placement == point_placement) + { + return find_placement_horizontal(p); + } + else if (p->label_placement == line_placement) + { + return find_placement_follow(p); + } + + return false; + } + + bool placement_finder::find_placement_follow(placement *p) + { + std::pair string_dimensions = p->info->get_dimensions(); + double string_width = string_dimensions.first; + // double string_height = string_dimensions.second; + + double distance = p->get_total_distance(); + + //~ double delta = string_width/distance; + double delta = distance/100.0; + + for (double i = 0; i < (distance - string_width)/2.0; i += delta) + { + p->clear_envelopes(); + + if ( build_path_follow(p, (distance - string_width)/2.0 + i) ) { + update_detector(p); + return true; + } + + p->clear_envelopes(); + + if ( build_path_follow(p, (distance - string_width)/2.0 - i) ) { + update_detector(p); + return true; + } + } + + p->starting_x = 0; + p->starting_y = 0; + + return false; + } + + bool placement_finder::find_placement_horizontal(placement *p) + { + double distance = p->get_total_distance(); + //~ double delta = string_width/distance; + double delta = distance/100.0; + + for (double i = 0; i < distance/2.0; i += delta) + { + p->clear_envelopes(); + + if ( build_path_horizontal(p, distance/2.0 + i) ) { + update_detector(p); + return true; + } + + p->clear_envelopes(); + + if ( build_path_horizontal(p, distance/2.0 - i) ) { + update_detector(p); + return true; + } + } + + p->starting_x = 0; + p->starting_y = 0; + + return false; + } + + void placement_finder::update_detector(placement *p) + { + while (!p->envelopes.empty()) + { + Envelope e = p->envelopes.front(); + + detector_.insert(e); + + p->envelopes.pop(); } } - for (unsigned i = 0; i < p->info->num_characters(); i++) + bool placement_finder::build_path_follow(placement *p, double target_distance) { - character_info ci; - unsigned c; - - while (distance <= 0) { + double new_x, new_y, old_x, old_y; + unsigned cur_node = 0; + + double angle = 0.0; + int orientation = 0; + + p->path.clear(); + + double x, y; + x = y = 0.0; + + double distance = 0.0; + + std::pair string_dimensions = p->info->get_dimensions(); + // double string_width = string_dimensions.first; + double string_height = string_dimensions.second; + + p->shape_path.rewind(0); + p->shape_path.vertex(&new_x,&new_y); + for (unsigned i = 0; i < p->geom->num_points() - 1; i++) + { double dx, dy; cur_node++; - - if (cur_node >= p->geom->num_points()) { - break; - } - + old_x = new_x; old_y = new_y; p->shape_path.vertex(&new_x,&new_y); - + dx = new_x - old_x; dy = new_y - old_y; + + double segment_length = sqrt(dx*dx + dy*dy); + + distance += segment_length; + if (distance > target_distance) + { + p->starting_x = new_x - dx*(distance - target_distance)/segment_length; + p->starting_y = new_y - dy*(distance - target_distance)/segment_length; - angle = atan2(-dy, dx ); + angle = atan2(-dy, dx); + + if (angle > M_PI/2 || angle <= -M_PI/2) { + orientation = -1; + } + else { + orientation = 1; + } + + distance -= target_distance; - distance += sqrt(dx*dx+dy*dy); + break; + } } - if (orientation == -1) { - ci = p->info->at(p->info->num_characters() - i - 1); - } - else + for (unsigned i = 0; i < p->info->num_characters(); i++) { - ci = p->info->at(i); - } - c = ci.character; + character_info ci; + unsigned c; + + while (distance <= 0) { + double dx, dy; - Envelope e; - if (p->has_dimensions) - { - e.init(x, y, x + p->dimensions.first, y + p->dimensions.second); - } + cur_node++; + + if (cur_node >= p->geom->num_points()) { + break; + } + + old_x = new_x; + old_y = new_y; - if (orientation == -1) { - x = new_x - (distance - ci.width)*cos(angle); - y = new_y + (distance - ci.width)*sin(angle); + p->shape_path.vertex(&new_x,&new_y); - //Center the text on the line. - x += (((double)string_height/2.0) - 1.0)*cos(angle+M_PI/2); - y -= (((double)string_height/2.0) - 1.0)*sin(angle+M_PI/2); + dx = new_x - old_x; + dy = new_y - old_y; + + angle = atan2(-dy, dx ); + + distance += sqrt(dx*dx+dy*dy); + } + + if (orientation == -1) { + ci = p->info->at(p->info->num_characters() - i - 1); + } + else + { + ci = p->info->at(i); + } + c = ci.character; + + Envelope e; + if (p->has_dimensions) + { + e.init(x, y, x + p->dimensions.first, y + p->dimensions.second); + } + + if (orientation == -1) { + x = new_x - (distance - ci.width)*cos(angle); + y = new_y + (distance - ci.width)*sin(angle); + + //Center the text on the line. + x += (((double)string_height/2.0) - 1.0)*cos(angle+M_PI/2); + y -= (((double)string_height/2.0) - 1.0)*sin(angle+M_PI/2); - if (!p->has_dimensions) - { - e.init(x, y, x + ci.width*cos(angle+M_PI), y - ci.width*sin(angle+M_PI)); - e.expand_to_include(x - ci.height*sin(angle+M_PI), y - ci.height*cos(angle+M_PI)); - e.expand_to_include(x + (ci.width*cos(angle+M_PI) - ci.height*sin(angle+M_PI)), y - (ci.width*sin(angle+M_PI) + ci.height*cos(angle+M_PI))); + if (!p->has_dimensions) + { + e.init(x, y, x + ci.width*cos(angle+M_PI), y - ci.width*sin(angle+M_PI)); + e.expand_to_include(x - ci.height*sin(angle+M_PI), y - ci.height*cos(angle+M_PI)); + e.expand_to_include(x + (ci.width*cos(angle+M_PI) - ci.height*sin(angle+M_PI)), y - (ci.width*sin(angle+M_PI) + ci.height*cos(angle+M_PI))); + } } - } - else - { - x = new_x - distance*cos(angle); - y = new_y + distance*sin(angle); - - //Center the text on the line. - x += (((double)string_height/2.0) - 1.0)*cos(angle-M_PI/2); - y -= (((double)string_height/2.0) - 1.0)*sin(angle-M_PI/2); - - if (!p->has_dimensions) + else { - 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))); + x = new_x - distance*cos(angle); + y = new_y + distance*sin(angle); + + //Center the text on the line. + x += (((double)string_height/2.0) - 1.0)*cos(angle-M_PI/2); + y -= (((double)string_height/2.0) - 1.0)*sin(angle-M_PI/2); + + if (!p->has_dimensions) + { + 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_.has_placement(e)) + { + return false; + } + + p->envelopes.push(e); + + p->path.add_node(c, x - p->starting_x, -y + p->starting_y, (orientation == -1 ? angle + M_PI : angle)); + + distance -= ci.width; } - - if (!detector_.has_placement(e)) - { - return false; - } - - p->envelopes.push(e); - - p->path.add_node(c, x - p->starting_x, -y + p->starting_y, (orientation == -1 ? angle + M_PI : angle)); - - distance -= ci.width; - } - return true; - } - - bool placement_finder::build_path_horizontal(placement *p, double target_distance) + return true; + } + + /* + bool placement_finder::build_path_horizontal(placement *p, double target_distance) { double x, y; @@ -433,7 +434,159 @@ namespace mapnik } return true; } + */ + + bool placement_finder::build_path_horizontal(placement *p, double target_distance) + { + + p->path.clear(); + + std::pair string_dimensions = p->info->get_dimensions(); + double string_width = string_dimensions.first; + double string_height = string_dimensions.second; + + // check if we need to wrap the string + double wrap_at = string_width + 1; + if (p->wrap_width && string_width > p->wrap_width) + { + if (p->text_ratio) + for (int i = 1; ((wrap_at = string_width/i)/(string_height*i)) > p->text_ratio && (string_width/i) > p->wrap_width; ++i); + else + wrap_at = p->wrap_width; + //std::clog << "Wrapping string at" << wrap_at << std::endl; + } - + // work out where our line breaks need to be + std::vector line_breaks; + std::vector line_widths; + if (wrap_at < string_width && p->info->num_characters() > 0) + { + int line_count=0; + int last_space = 0; + string_width = 0; + string_height = 0; + double line_width = 0; + double line_height = 0; + double word_width = 0; + double word_height = 0; + for (unsigned int ii = 0; ii < p->info->num_characters(); ii++) + { + character_info ci; + ci = p->info->at(ii); + + unsigned c = ci.character; + word_width += ci.width; + word_height = word_height > ci.height ? word_height : ci.height; + ++line_count; + + if (c == ' ') + { + last_space = ii; + line_width += word_width; + line_height = line_height > word_height ? line_height : word_height; + word_width = 0; + word_height = 0; + } + if (line_width > 0 && line_width > wrap_at) + { + string_width = string_width > line_width ? string_width : line_width; + string_height += line_height; + line_breaks.push_back(last_space); + line_widths.push_back(line_width); + ii = last_space; + line_count = 0; + line_width = 0; + line_height = 0; + word_width = 0; + word_height = 0; + } + } + line_width += word_width; + string_width = string_width > line_width ? string_width : line_width; + line_breaks.push_back(p->info->num_characters() + 1); + line_widths.push_back(line_width); + } + if (line_breaks.size() == 0) + { + line_breaks.push_back(p->info->num_characters() + 1); + line_widths.push_back(string_width); + } + + p->info->set_dimensions(string_width, string_height); + + if (p->geom->type() == LineString) + { + std::pair starting_pos = p->get_position_at_distance(target_distance); + + p->starting_x = starting_pos.first; + p->starting_y = starting_pos.second; + } + else + { + p->geom->label_position(&p->starting_x, &p->starting_y); + // TODO: + // We would only want label position in final 'paper' coords. + // Move view and proj transforms to e.g. label_position(x,y,proj_trans,ctrans)? + double z=0; + p->proj_trans->backward(p->starting_x, p->starting_y, z); + p->ctrans->forward(&p->starting_x, &p->starting_y); + } + + double line_height = 0; + unsigned int line_number = 0; + unsigned int index_to_wrap_at = line_breaks[line_number]; + double line_width = line_widths[line_number]; + + double x = -line_width/2.0; + double y = -string_height/2.0 + 1.0; + + for (unsigned i = 0; i < p->info->num_characters(); i++) + { + character_info ci; + ci = p->info->at(i); + + unsigned c = ci.character; + + if (i == index_to_wrap_at) + { + index_to_wrap_at = line_breaks[++line_number]; + line_width = line_widths[line_number]; + y -= line_height; + x = -line_width/2.0; + line_height = 0; + continue; + } + else + { + p->path.add_node(c, x, y, 0.0); + + Envelope e; + if (p->has_dimensions) + { + e.init(p->starting_x - (p->dimensions.first/2.0), + p->starting_y - (p->dimensions.second/2.0), + p->starting_x + (p->dimensions.first/2.0), + p->starting_y + (p->dimensions.second/2.0)); + } + else + { + e.init(p->starting_x + x, + p->starting_y - y, + p->starting_x + x + ci.width, + p->starting_y - y - ci.height); + } + + if (!detector_.has_placement(e)) + { + return false; + } + + p->envelopes.push(e); + } + x += ci.width; + line_height = line_height > ci.height ? line_height : ci.height; + } + return true; + } } diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp index 286622ae8..7aec757bc 100644 --- a/src/text_symbolizer.cpp +++ b/src/text_symbolizer.cpp @@ -22,113 +22,143 @@ //$Id$ + +#include + #include namespace mapnik { text_symbolizer::text_symbolizer(std::string const& name, std::string const& face_name, unsigned size,Color const& fill) - : name_(name), - face_name_(face_name), - size_(size), - fill_(fill), - halo_fill_(Color(255,255,255)), - halo_radius_(0), - label_p_(point_placement), - anchor_(0.0,0.5), - displacement_(0.0,0.0) {} + : name_(name), + face_name_(face_name), + size_(size), + text_ratio_(0), + wrap_width_(0), + fill_(fill), + halo_fill_(Color(255,255,255)), + halo_radius_(0), + label_p_(point_placement), + anchor_(0.0,0.5), + displacement_(0.0,0.0) {} text_symbolizer::text_symbolizer(text_symbolizer const& rhs) - : name_(rhs.name_), - face_name_(rhs.face_name_), - size_(rhs.size_), - fill_(rhs.fill_), - halo_fill_(rhs.halo_fill_), - halo_radius_(rhs.halo_radius_), - label_p_(rhs.label_p_), - anchor_(rhs.anchor_), - displacement_(rhs.displacement_) {} + : name_(rhs.name_), + face_name_(rhs.face_name_), + size_(rhs.size_), + text_ratio_(rhs.text_ratio_), + wrap_width_(rhs.wrap_width_), + fill_(rhs.fill_), + halo_fill_(rhs.halo_fill_), + halo_radius_(rhs.halo_radius_), + label_p_(rhs.label_p_), + anchor_(rhs.anchor_), + displacement_(rhs.displacement_) {} text_symbolizer& text_symbolizer::operator=(text_symbolizer const& other) { - if (this == &other) - return *this; - name_ = other.name_; - face_name_ = other.face_name_; - size_ = other.size_; - fill_ = other.fill_; - halo_fill_ = other.halo_fill_; - label_p_ = other.label_p_; - anchor_ = other.anchor_; - displacement_ = other.displacement_; - return *this; + if (this == &other) + return *this; + name_ = other.name_; + face_name_ = other.face_name_; + size_ = other.size_; + text_ratio_ = other.text_ratio_; + wrap_width_ = other.wrap_width_; + fill_ = other.fill_; + halo_fill_ = other.halo_fill_; + halo_radius_ = other.halo_radius_; + label_p_ = other.label_p_; + anchor_ = other.anchor_; + displacement_ = other.displacement_; + return *this; } std::string const& text_symbolizer::get_name() const { - return name_; + return name_; } std::string const& text_symbolizer::get_face_name() const { - return face_name_; + return face_name_; } + unsigned text_symbolizer::get_text_ratio() const + { + return text_ratio_; + } + + void text_symbolizer::set_text_ratio(unsigned ratio) + { + text_ratio_ = ratio; + } + + unsigned text_symbolizer::get_wrap_width() const + { + return wrap_width_; + } + + void text_symbolizer::set_wrap_width(unsigned width) + { + wrap_width_ = width; + } + unsigned text_symbolizer::get_text_size() const { - return size_; + return size_; } Color const& text_symbolizer::get_fill() const { - return fill_; + return fill_; } void text_symbolizer::set_halo_fill(Color const& fill) { - halo_fill_ = fill; + halo_fill_ = fill; } Color const& text_symbolizer::get_halo_fill() const { - return halo_fill_; + return halo_fill_; } void text_symbolizer::set_halo_radius(unsigned radius) { - halo_radius_ = radius; + halo_radius_ = radius; } unsigned text_symbolizer::get_halo_radius() const { - return halo_radius_; + return halo_radius_; } void text_symbolizer::set_label_placement(label_placement_e label_p) { - label_p_ = label_p; + label_p_ = label_p; } label_placement_e text_symbolizer::get_label_placement() const { - return label_p_; + return label_p_; } void text_symbolizer::set_anchor(double x, double y) { - anchor_ = boost::make_tuple(x,y); + anchor_ = boost::make_tuple(x,y); } position const& text_symbolizer::get_anchor () const { - return anchor_; + return anchor_; } void text_symbolizer::set_displacement(double x, double y) { - displacement_ = boost::make_tuple(x,y); + displacement_ = boost::make_tuple(x,y); } position const& text_symbolizer::get_displacement() const { - return displacement_; + return displacement_; } }