From e803b1c2ecbce7e761fffbf5abdf03be6822218a Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Tue, 1 Feb 2011 08:13:08 +0000 Subject: [PATCH] add 'interior' placement option to text_symbolizr - patch from Toby Collet - closes #709 --- bindings/python/mapnik_text_symbolizer.cpp | 1 + include/mapnik/geometry.hpp | 69 ++++++++++++++++++++++ include/mapnik/text_symbolizer.hpp | 1 + src/agg/process_shield_symbolizer.cpp | 4 +- src/agg/process_text_symbolizer.cpp | 8 ++- src/cairo_renderer.cpp | 15 +++-- src/text_symbolizer.cpp | 1 + 7 files changed, 91 insertions(+), 8 deletions(-) diff --git a/bindings/python/mapnik_text_symbolizer.cpp b/bindings/python/mapnik_text_symbolizer.cpp index c75b339e4..8e269b933 100644 --- a/bindings/python/mapnik_text_symbolizer.cpp +++ b/bindings/python/mapnik_text_symbolizer.cpp @@ -165,6 +165,7 @@ void export_text_symbolizer() .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",TOP) diff --git a/include/mapnik/geometry.hpp b/include/mapnik/geometry.hpp index 38f7e6799..5aff4e3e9 100644 --- a/include/mapnik/geometry.hpp +++ b/include/mapnik/geometry.hpp @@ -105,6 +105,75 @@ public: } return result; } + + void label_interior_position(double *x, double *y) const + { + // start with the default label position + label_position(x,y); + unsigned size = cont_.size(); + // if we are not a polygon, or the default is within the polygon we are done + if (size < 3 || hit_test(*x,*y,0)) + return; + + // otherwise we find a horizontal line across the polygon and then return the + // center of the widest intersection between the polygon and the line. + + std::vector intersections; // only need to store the X as we know the y + + double x0, y0; + rewind(0); + unsigned command = vertex(&x0, &y0); + double x1,y1; + while (SEG_END != (command=vertex(&x1, &y1))) + { + if (command != SEG_MOVETO) + { + // if the segments overlap + if (y0==y1) + { + if (y0==*y) + { + double xi = (x0+x1)/2.0; + intersections.push_back(xi); + } + } + // if the path segment crosses the bisector + else if ((y0 <= *y && y1 >= *y) || + (y0 >= *y && y1 <= *y)) + { + // then calculate the intersection + double xi = x0; + if (x0 != x1) + { + double m = (y1-y0)/(x1-x0); + double c = y0 - m*x0; + xi = (*y-c)/m; + } + + intersections.push_back(xi); + } + } + x0 = x1; + y0 = y1; + } + // no intersections we just return the default + if (intersections.empty()) + return; + x0=intersections[0]; + double max_width = 0; + for (unsigned ii = 1; ii < intersections.size(); ++ii) + { + double x1=intersections[ii]; + double xc=(x0+x1)/2.0; + double width = fabs(x1-x0); + if (width > max_width && hit_test(xc,*y,0)) + { + *x=xc; + max_width = width; + } + } + } + void label_position(double *x, double *y) const { unsigned size = cont_.size(); diff --git a/include/mapnik/text_symbolizer.hpp b/include/mapnik/text_symbolizer.hpp index 9542b4a47..da05565c7 100644 --- a/include/mapnik/text_symbolizer.hpp +++ b/include/mapnik/text_symbolizer.hpp @@ -45,6 +45,7 @@ enum label_placement_enum { POINT_PLACEMENT, LINE_PLACEMENT, VERTEX_PLACEMENT, + INTERIOR_PLACEMENT, label_placement_enum_MAX }; diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index 43d72d72f..6477ba9f9 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -121,7 +121,7 @@ void agg_renderer::process(shield_symbolizer const& sym, 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) + 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); @@ -138,6 +138,8 @@ void agg_renderer::process(shield_symbolizer const& sym, 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); diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index f812be48a..965424ce4 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -95,10 +95,14 @@ void agg_renderer::process(text_symbolizer const& sym, { placement text_placement(info,sym,scale_factor_); text_placement.avoid_edges = sym.get_avoid_edges(); - if (sym.get_label_placement() == POINT_PLACEMENT) + if (sym.get_label_placement() == POINT_PLACEMENT || + sym.get_label_placement() == INTERIOR_PLACEMENT) { double label_x, label_y, z=0.0; - geom.label_position(&label_x, &label_y); + 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); diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index dd9df0901..ffa408ca3 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -1141,7 +1141,7 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, 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) + 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); @@ -1158,6 +1158,8 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, 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); @@ -1550,11 +1552,14 @@ void cairo_renderer_base::process(text_symbolizer const& sym, path_type path(t_, geom, prj_trans); placement text_placement(info, sym, 1.0); - if (sym.get_label_placement() == POINT_PLACEMENT) + if (sym.get_label_placement() == POINT_PLACEMENT || + sym.get_label_placement() == INTERIOR_PLACEMENT) { - double label_x, label_y, z = 0.0; - - geom.label_position(&label_x, &label_y); + 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); diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp index 572d69756..f46c948e1 100644 --- a/src/text_symbolizer.cpp +++ b/src/text_symbolizer.cpp @@ -34,6 +34,7 @@ static const char * label_placement_strings[] = { "point", "line", "vertex", + "interior", "" };