Merge remote-tracking branch 'herm/textplacement-merge'

This commit is contained in:
Hermann Kraus 2012-01-31 16:37:37 +01:00
commit 06ce3f7eec
135 changed files with 3574 additions and 4019 deletions

View file

@ -649,7 +649,6 @@ __all__ = [
'FontEngine',
'FontSet',
'Geometry2d',
'GlyphSymbolizer',
'Image',
'ImageView',
'Grid',

View file

@ -1,121 +0,0 @@
#include <boost/python.hpp>
#include <mapnik/glyph_symbolizer.hpp>
#include "mapnik_enumeration.hpp"
#include <boost/tuple/tuple.hpp>
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<double,double> 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<double>(arg[0]),extract<double>(arg[1]));
}
}
void export_glyph_symbolizer()
{
enumeration_<angle_mode_e>("angle_mode")
.value("AZIMUTH", AZIMUTH)
.value("TRIGONOMETRIC", TRIGONOMETRIC)
;
class_<glyph_symbolizer>("GlyphSymbolizer",
init<std::string,mapnik::expression_ptr>())
.add_property("face_name",
make_function(&glyph_symbolizer::get_face_name,
return_value_policy<copy_const_reference>()),
&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<copy_const_reference>()),
&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"
)
;
}

View file

@ -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 <mapnik/scale_denominator.hpp>
#include <mapnik/value_error.hpp>
#include <mapnik/save_map.hpp>
#include <mapnik/scale_denominator.hpp>
#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();

View file

@ -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<raster_symbolizer,symbolizer>();
implicitly_convertible<shield_symbolizer,symbolizer>();
implicitly_convertible<text_symbolizer,symbolizer>();
implicitly_convertible<glyph_symbolizer,symbolizer>();
implicitly_convertible<markers_symbolizer,symbolizer>();
class_<rule::symbolizers>("Symbolizers",init<>("TODO"))

View file

@ -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<double,double> 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<double,double> 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<double>(arg[0]),extract<double>(arg[1]));
}
tuple get_anchor(const shield_symbolizer& t)
{
boost::tuple<double,double> 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<double>(arg[0]),extract<double>(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)

View file

@ -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<std::string>
{
@ -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<markers_symbolizer>(symbol);
}
const glyph_symbolizer& glyph_( const symbolizer& symbol )
{
return boost::get<glyph_symbolizer>(symbol);
}
void export_symbolizer()
{
using namespace boost::python;
@ -202,10 +190,6 @@ void export_symbolizer()
.def("markers",markers_,
return_value_policy<copy_const_reference>())
.def("glyph",glyph_,
return_value_policy<copy_const_reference>())
;
}

View file

@ -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 <boost/python.hpp>
#include <mapnik/text_placements.hpp>
#include "mapnik_enumeration.hpp"
#include <mapnik/expression_string.hpp>
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<double>(arg[0]);
double y = extract<double>(arg[0]);
t.displacement = std::make_pair(x, y);
}
}
void export_text_placement()
{
using namespace boost::python;
enumeration_<label_placement_e>("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_e>("vertical_alignment")
.value("TOP",V_TOP)
.value("MIDDLE",V_MIDDLE)
.value("BOTTOM",V_BOTTOM)
.value("AUTO",V_AUTO)
;
enumeration_<horizontal_alignment_e>("horizontal_alignment")
.value("LEFT",H_LEFT)
.value("MIDDLE",H_MIDDLE)
.value("RIGHT",H_RIGHT)
.value("AUTO",H_AUTO)
;
enumeration_<justify_alignment_e>("justify_alignment")
.value("LEFT",J_LEFT)
.value("MIDDLE",J_MIDDLE)
.value("RIGHT",J_RIGHT)
;
enumeration_<text_transform_e>("text_transform")
.value("NONE",NONE)
.value("UPPERCASE",UPPERCASE)
.value("LOWERCASE",LOWERCASE)
.value("CAPITALIZE",CAPITALIZE)
;
class_<text_symbolizer_properties>("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.*/
;
}

View file

@ -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<double>(arg[0]),extract<double>(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<double>(arg[0]),extract<double>(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<bool>(state[9]));
tuple anch = extract<tuple>(state[10]);
double x = extract<double>(anch[0]);
double y = extract<double>(anch[1]);
t.set_anchor(x,y);
t.set_force_odd_labels(extract<bool>(state[10]));
t.set_force_odd_labels(extract<bool>(state[11]));
t.set_max_char_angle_delta(extract<double>(state[12]));
list extras = extract<list>(state[13]);
t.set_max_char_angle_delta(extract<double>(state[11]));
list extras = extract<list>(state[12]);
t.set_wrap_char_from_string(extract<std::string>(extras[0]));
t.set_line_spacing(extract<unsigned>(extras[1]));
t.set_character_spacing(extract<unsigned>(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_e>("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,

40
docs/textrendering.gv Normal file
View file

@ -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}
}

BIN
docs/textrendering.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

226
docs/textrendering.svg Normal file
View file

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.26.3 (20100126.1600)
-->
<!-- Title: textrendering Pages: 1 -->
<svg width="733pt" height="782pt"
viewBox="0.00 0.00 733.00 782.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 778)">
<title>textrendering</title>
<polygon fill="white" stroke="white" points="-4,5 -4,-778 730,-778 730,5 -4,5"/>
<!-- Renderer -->
<g id="node1" class="node"><title>Renderer</title>
<ellipse fill="none" stroke="red" cx="220" cy="-216" rx="55.0898" ry="18"/>
<text text-anchor="middle" x="220" y="-211.9" font-family="Times Roman,serif" font-size="14.00">Renderer</text>
</g>
<!-- text_symbolizer_helper -->
<g id="node24" class="node"><title>text_symbolizer_helper</title>
<ellipse fill="none" stroke="black" cx="257" cy="-108" rx="123.188" ry="18"/>
<text text-anchor="middle" x="257" y="-103.9" font-family="Times Roman,serif" font-size="14.00">text_symbolizer_helper</text>
</g>
<!-- Renderer&#45;&gt;text_symbolizer_helper -->
<g id="edge26" class="edge"><title>Renderer&#45;&gt;text_symbolizer_helper</title>
<path fill="none" stroke="red" d="M226.277,-197.679C232.106,-180.665 240.865,-155.097 247.541,-135.609"/>
<polygon fill="red" stroke="red" points="250.886,-136.645 250.816,-126.05 244.264,-134.376 250.886,-136.645"/>
<text text-anchor="middle" x="269.5" y="-157.9" font-family="Times Roman,serif" font-size="14.00">creates</text>
</g>
<!-- text_placements -->
<g id="node2" class="node"><title>text_placements</title>
<polygon fill="none" stroke="black" points="320,-684 190,-684 190,-648 320,-648 320,-684"/>
<text text-anchor="middle" x="255" y="-661.9" font-family="Times Roman,serif" font-size="14.00">text_placements</text>
</g>
<!-- text_placement_info -->
<g id="node3" class="node"><title>text_placement_info</title>
<polygon fill="none" stroke="black" points="245,-594 89,-594 89,-558 245,-558 245,-594"/>
<text text-anchor="middle" x="167" y="-571.9" font-family="Times Roman,serif" font-size="14.00">text_placement_info</text>
</g>
<!-- text_placements&#45;&gt;text_placement_info -->
<g id="edge8" class="edge"><title>text_placements&#45;&gt;text_placement_info</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M205.049,-647.841C196.813,-643.062 189.015,-637.176 183,-630 176.839,-622.65 173.05,-613.005 170.721,-603.902"/>
<polygon fill="black" stroke="black" points="174.125,-603.084 168.661,-594.007 167.272,-604.511 174.125,-603.084"/>
<text text-anchor="middle" x="255.5" y="-616.9" font-family="Times Roman,serif" font-size="14.00">get_placement_info()</text>
</g>
<!-- text_symbolizer_properties -->
<g id="node10" class="node"><title>text_symbolizer_properties</title>
<ellipse fill="none" stroke="black" cx="342" cy="-486" rx="141.756" ry="18"/>
<text text-anchor="middle" x="342" y="-481.9" font-family="Times Roman,serif" font-size="14.00">text_symbolizer_properties</text>
</g>
<!-- text_placements&#45;&gt;text_symbolizer_properties -->
<g id="edge6" class="edge"><title>text_placements&#45;&gt;text_symbolizer_properties</title>
<path fill="none" stroke="black" d="M308.282,-647.955C315.947,-643.223 322.938,-637.323 328,-630 351.634,-595.806 350.563,-545.645 346.851,-514.45"/>
<polygon fill="black" stroke="black" points="350.281,-513.701 345.464,-504.264 343.345,-514.645 350.281,-513.701"/>
<text text-anchor="middle" x="383" y="-571.9" font-family="Times Roman,serif" font-size="14.00">properties</text>
</g>
<!-- text_placement_info&#45;&gt;Renderer -->
<g id="edge36" class="edge"><title>text_placement_info&#45;&gt;Renderer</title>
<path fill="none" stroke="red" d="M167.966,-557.734C168.843,-539.556 170,-510.858 170,-486 170,-486 170,-486 170,-306 170,-281.884 183.888,-258.188 197.026,-241.169"/>
<polygon fill="red" stroke="red" points="199.765,-243.348 203.351,-233.378 194.331,-238.936 199.765,-243.348"/>
<text text-anchor="middle" x="197" y="-391.9" font-family="Times Roman,serif" font-size="14.00">used by</text>
</g>
<!-- text_placement_info&#45;&gt;text_placement_info -->
<g id="edge14" class="edge"><title>text_placement_info&#45;&gt;text_placement_info</title>
<path fill="none" stroke="black" d="M245.347,-583.319C255.944,-582.092 263,-579.652 263,-576 263,-573.66 260.104,-571.818 255.237,-570.474"/>
<polygon fill="black" stroke="black" points="255.811,-567.021 245.347,-568.681 254.562,-573.909 255.811,-567.021"/>
<text text-anchor="middle" x="284.5" y="-571.9" font-family="Times Roman,serif" font-size="14.00">next()</text>
</g>
<!-- text_placement_info&#45;&gt;text_symbolizer_properties -->
<g id="edge10" class="edge"><title>text_placement_info&#45;&gt;text_symbolizer_properties</title>
<path fill="none" stroke="black" d="M195.152,-557.999C212.864,-547.001 236.382,-532.987 258,-522 267.963,-516.936 278.802,-511.934 289.282,-507.342"/>
<polygon fill="black" stroke="black" points="290.973,-510.425 298.77,-503.251 288.201,-503.997 290.973,-510.425"/>
<text text-anchor="middle" x="293" y="-526.9" font-family="Times Roman,serif" font-size="14.00">properties</text>
</g>
<!-- text_path -->
<g id="node14" class="node"><title>text_path</title>
<ellipse fill="none" stroke="black" cx="58" cy="-108" rx="57.8712" ry="18"/>
<text text-anchor="middle" x="58" y="-103.9" font-family="Times Roman,serif" font-size="14.00">text_path</text>
</g>
<!-- text_placement_info&#45;&gt;text_path -->
<g id="edge12" class="edge"><title>text_placement_info&#45;&gt;text_path</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M126.007,-557.868C99.1525,-542.905 69,-518.802 69,-486 69,-486 69,-486 69,-216 69,-188.913 65.5331,-158.201 62.492,-136.471"/>
<polygon fill="black" stroke="black" points="65.9132,-135.677 61.0058,-126.287 58.9866,-136.688 65.9132,-135.677"/>
<text text-anchor="middle" x="108.5" y="-346.9" font-family="Times Roman,serif" font-size="14.00">placements</text>
</g>
<!-- node_ -->
<g id="node4" class="node"><title>node_</title>
<ellipse fill="none" stroke="black" cx="621" cy="-756" rx="34.2406" ry="18"/>
<text text-anchor="middle" x="621" y="-751.9" font-family="Times Roman,serif" font-size="14.00">node</text>
</g>
<!-- text_processor -->
<g id="node6" class="node"><title>text_processor</title>
<ellipse fill="none" stroke="black" cx="369" cy="-396" rx="81.9961" ry="18"/>
<text text-anchor="middle" x="369" y="-391.9" font-family="Times Roman,serif" font-size="14.00">text_processor</text>
</g>
<!-- node_&#45;&gt;text_processor -->
<g id="edge2" class="edge"><title>node_&#45;&gt;text_processor</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M641.466,-741.272C661.17,-725.215 688,-697.556 688,-666 688,-666 688,-666 688,-486 688,-437.627 550.415,-413.974 455.959,-403.435"/>
<polygon fill="black" stroke="black" points="456.134,-399.933 445.814,-402.334 455.379,-406.892 456.134,-399.933"/>
<text text-anchor="middle" x="706.5" y="-571.9" font-family="Times Roman,serif" font-size="14.00">tree_</text>
</g>
<!-- text_node -->
<g id="node32" class="node"><title>text_node</title>
<ellipse fill="none" stroke="black" cx="600" cy="-486" rx="59.7599" ry="18"/>
<text text-anchor="middle" x="600" y="-481.9" font-family="Times Roman,serif" font-size="14.00">text_node</text>
</g>
<!-- node_&#45;&gt;text_node -->
<g id="edge38" class="edge"><title>node_&#45;&gt;text_node</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M621.64,-737.953C622.123,-723.392 622.745,-702.38 623,-684 624.004,-611.699 620.132,-593.283 608,-522 607.568,-519.46 607.073,-516.828 606.548,-514.197"/>
<polygon fill="black" stroke="black" points="609.934,-513.295 604.426,-504.244 603.088,-514.754 609.934,-513.295"/>
</g>
<!-- list_node -->
<g id="node34" class="node"><title>list_node</title>
<ellipse fill="none" stroke="black" cx="560" cy="-666" rx="54.2008" ry="18"/>
<text text-anchor="middle" x="560" y="-661.9" font-family="Times Roman,serif" font-size="14.00">list_node</text>
</g>
<!-- node_&#45;&gt;list_node -->
<g id="edge40" class="edge"><title>node_&#45;&gt;list_node</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M609.239,-738.647C600.267,-725.41 587.763,-706.962 577.602,-691.97"/>
<polygon fill="black" stroke="black" points="580.441,-689.921 571.933,-683.607 574.647,-693.848 580.441,-689.921"/>
</g>
<!-- format_node -->
<g id="node36" class="node"><title>format_node</title>
<ellipse fill="none" stroke="black" cx="502" cy="-576" rx="71.7694" ry="18"/>
<text text-anchor="middle" x="502" y="-571.9" font-family="Times Roman,serif" font-size="14.00">format_node</text>
</g>
<!-- node_&#45;&gt;format_node -->
<g id="edge42" class="edge"><title>node_&#45;&gt;format_node</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M590.428,-747.872C560.614,-738.135 516.934,-718.626 497,-684 483.032,-659.737 487.038,-627.084 492.815,-604.055"/>
<polygon fill="black" stroke="black" points="496.244,-604.787 495.536,-594.216 489.497,-602.921 496.244,-604.787"/>
</g>
<!-- text_processor&#45;&gt;Renderer -->
<g id="edge34" class="edge"><title>text_processor&#45;&gt;Renderer</title>
<path fill="none" stroke="red" d="M314.995,-382.308C284.546,-371.64 248.752,-353.535 229,-324 213.308,-300.537 212.917,-267.483 215.283,-244.138"/>
<polygon fill="red" stroke="red" points="218.762,-244.519 216.518,-234.164 211.815,-243.658 218.762,-244.519"/>
<text text-anchor="middle" x="259.5" y="-301.9" font-family="Times Roman,serif" font-size="14.00">called by</text>
</g>
<!-- processed_text -->
<g id="node18" class="node"><title>processed_text</title>
<ellipse fill="none" stroke="black" cx="382" cy="-306" rx="82.8866" ry="18"/>
<text text-anchor="middle" x="382" y="-301.9" font-family="Times Roman,serif" font-size="14.00">processed_text</text>
</g>
<!-- text_processor&#45;&gt;processed_text -->
<g id="edge18" class="edge"><title>text_processor&#45;&gt;processed_text</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M371.631,-377.787C373.428,-365.344 375.846,-348.605 377.895,-334.421"/>
<polygon fill="black" stroke="black" points="381.408,-334.583 379.373,-324.186 374.479,-333.583 381.408,-334.583"/>
<text text-anchor="middle" x="407" y="-346.9" font-family="Times Roman,serif" font-size="14.00">process()</text>
</g>
<!-- TextSymbolizer -->
<g id="node7" class="node"><title>TextSymbolizer</title>
<ellipse fill="none" stroke="black" cx="255" cy="-756" rx="84.7756" ry="18"/>
<text text-anchor="middle" x="255" y="-751.9" font-family="Times Roman,serif" font-size="14.00">TextSymbolizer</text>
</g>
<!-- TextSymbolizer&#45;&gt;text_placements -->
<g id="edge4" class="edge"><title>TextSymbolizer&#45;&gt;text_placements</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M255,-737.787C255,-725.344 255,-708.605 255,-694.421"/>
<polygon fill="black" stroke="black" points="258.5,-694.186 255,-684.186 251.5,-694.186 258.5,-694.186"/>
<text text-anchor="middle" x="323.5" y="-706.9" font-family="Times Roman,serif" font-size="14.00">placement_options_</text>
</g>
<!-- text_symbolizer_properties&#45;&gt;text_processor -->
<g id="edge16" class="edge"><title>text_symbolizer_properties&#45;&gt;text_processor</title>
<path fill="none" stroke="black" d="M347.464,-467.787C351.233,-455.222 356.317,-438.277 360.599,-424.005"/>
<polygon fill="black" stroke="black" points="364.023,-424.77 363.544,-414.186 357.318,-422.758 364.023,-424.77"/>
<text text-anchor="middle" x="390" y="-436.9" font-family="Times Roman,serif" font-size="14.00">processor</text>
</g>
<!-- text_path&#45;&gt;Renderer -->
<g id="edge22" class="edge"><title>text_path&#45;&gt;Renderer</title>
<path fill="none" stroke="red" d="M82.6849,-124.457C110.585,-143.056 156.131,-173.421 187.049,-194.033"/>
<polygon fill="red" stroke="red" points="185.485,-197.196 195.747,-199.831 189.368,-191.372 185.485,-197.196"/>
<text text-anchor="middle" x="190" y="-157.9" font-family="Times Roman,serif" font-size="14.00">used by</text>
</g>
<!-- processed_text&#45;&gt;Renderer -->
<g id="edge24" class="edge"><title>processed_text&#45;&gt;Renderer</title>
<path fill="none" stroke="red" d="M337.974,-290.733C323.299,-284.984 307.105,-277.902 293,-270 277.318,-261.215 261.124,-249.543 247.984,-239.299"/>
<polygon fill="red" stroke="red" points="249.834,-236.298 239.827,-232.817 245.479,-241.779 249.834,-236.298"/>
<text text-anchor="middle" x="327" y="-256.9" font-family="Times Roman,serif" font-size="14.00">owned by</text>
</g>
<!-- string_info -->
<g id="node20" class="node"><title>string_info</title>
<ellipse fill="none" stroke="black" cx="385" cy="-162" rx="61.8445" ry="18"/>
<text text-anchor="middle" x="385" y="-157.9" font-family="Times Roman,serif" font-size="14.00">string_info</text>
</g>
<!-- processed_text&#45;&gt;string_info -->
<g id="edge20" class="edge"><title>processed_text&#45;&gt;string_info</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M382.38,-287.762C382.892,-263.201 383.807,-219.247 384.409,-190.354"/>
<polygon fill="black" stroke="black" points="387.914,-190.16 384.623,-180.09 380.916,-190.015 387.914,-190.16"/>
<text text-anchor="middle" x="440" y="-256.9" font-family="Times Roman,serif" font-size="14.00">get_string_info()</text>
</g>
<!-- placement_finder -->
<g id="node26" class="node"><title>placement_finder</title>
<ellipse fill="none" stroke="black" cx="257" cy="-18" rx="93.8091" ry="18"/>
<text text-anchor="middle" x="257" y="-13.9" font-family="Times Roman,serif" font-size="14.00">placement_finder</text>
</g>
<!-- string_info&#45;&gt;placement_finder -->
<g id="edge32" class="edge"><title>string_info&#45;&gt;placement_finder</title>
<path fill="none" stroke="red" d="M391.337,-143.803C395.524,-128.385 398.65,-106.256 389,-90 373.987,-64.7104 346.622,-47.7353 320.685,-36.6545"/>
<polygon fill="red" stroke="red" points="321.803,-33.3312 311.22,-32.8342 319.183,-39.8225 321.803,-33.3312"/>
<text text-anchor="middle" x="422" y="-103.9" font-family="Times Roman,serif" font-size="14.00">used by</text>
</g>
<!-- text_symbolizer_helper&#45;&gt;placement_finder -->
<g id="edge28" class="edge"><title>text_symbolizer_helper&#45;&gt;placement_finder</title>
<path fill="none" stroke="red" d="M257,-89.7872C257,-77.3443 257,-60.6053 257,-46.4211"/>
<polygon fill="red" stroke="red" points="260.5,-46.1857 257,-36.1858 253.5,-46.1858 260.5,-46.1857"/>
<text text-anchor="middle" x="282.5" y="-58.9" font-family="Times Roman,serif" font-size="14.00">creates</text>
</g>
<!-- placement_finder&#45;&gt;text_path -->
<g id="edge30" class="edge"><title>placement_finder&#45;&gt;text_path</title>
<path fill="none" stroke="red" d="M220.029,-34.7207C185.996,-50.1122 135.554,-72.9254 100.01,-89.0004"/>
<polygon fill="red" stroke="red" points="98.53,-85.8285 90.8608,-93.1383 101.415,-92.2065 98.53,-85.8285"/>
<text text-anchor="middle" x="201.5" y="-58.9" font-family="Times Roman,serif" font-size="14.00">creates</text>
</g>
<!-- list_node&#45;&gt;text_node -->
<g id="edge44" class="edge"><title>list_node&#45;&gt;text_node</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M566.643,-647.87C571.723,-633.439 578.583,-612.642 583,-594 589.356,-567.176 593.972,-536.027 596.776,-514.095"/>
<polygon fill="black" stroke="black" points="600.255,-514.481 598.007,-504.127 593.308,-513.623 600.255,-514.481"/>
</g>
<!-- list_node&#45;&gt;format_node -->
<g id="edge46" class="edge"><title>list_node&#45;&gt;format_node</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M548.541,-648.219C540.175,-635.237 528.69,-617.415 519.222,-602.724"/>
<polygon fill="black" stroke="black" points="521.993,-600.562 513.634,-594.052 516.109,-604.354 521.993,-600.562"/>
</g>
<!-- format_node&#45;&gt;text_node -->
<g id="edge48" class="edge"><title>format_node&#45;&gt;text_node</title>
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M521.362,-558.219C536.318,-544.483 557.176,-525.328 573.652,-510.197"/>
<polygon fill="black" stroke="black" points="576.346,-512.475 581.344,-503.134 571.611,-507.32 576.346,-512.475"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -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*/)

View file

@ -24,16 +24,11 @@
#define MAPNIK_ATTRIBUTE_COLLECTOR_HPP
// mapnik
#include <mapnik/feature_layer_desc.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/path_expression_grammar.hpp>
#include <mapnik/parse_path.hpp>
// boost
#include <boost/utility.hpp>
#include <boost/variant.hpp>
#include <boost/concept_check.hpp>
// stl
#include <set>
#include <iostream>
@ -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<expression_ptr>::const_iterator it;
std::set<expression_ptr> 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<expression_ptr>::const_iterator it;
std::set<expression_ptr> 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);

View file

@ -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*/)

View file

@ -19,23 +19,34 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
// mapnik
#include <mapnik/grid/grid_renderer.hpp>
#include <iostream>
#ifndef CHAR_INFO_HPP
#define CHAR_INFO_HPP
#include <boost/shared_ptr.hpp>
namespace mapnik {
struct char_properties;
template <typename T>
void grid_renderer<T>::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<grid>::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

View file

@ -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<font_glyph>(*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<face_ptr> faces_;
std::map<unsigned, dimension_t> dimension_cache_;
std::map<unsigned, char_info> 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<glyph_t> 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<freetype_engine> &font_manager_, stroker & s);
box2d<double> 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<double> 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<double>(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<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((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<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((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;i<x_max;++i,++p)
{
for (j=y,q=0;j<y_max;++j,++q)
{
int gray = bitmap->buffer[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<freetype_engine> &font_manager_;
stroker & stroker_;
color fill_;
color halo_fill_;
double halo_radius_;
glyphs_t glyphs_;
double opacity_;
};
typedef face_manager<freetype_engine> face_manager_freetype;
}
#endif // MAPNIK_FONT_ENGINE_FREETYPE_HPP

View file

@ -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 <mapnik/enumeration.hpp>
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/symbolizer.hpp>
// boost
#include <boost/tuple/tuple.hpp>
namespace mapnik
{
typedef boost::tuple<double,double> 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

View file

@ -24,7 +24,6 @@
#define MAPNIK_GRID_HPP
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/grid/grid_view.hpp>

View file

@ -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*/)

View file

@ -27,8 +27,11 @@
#include <mapnik/global.hpp>
#include <ostream>
extern "C"
{
#include <stdio.h>
#include <jpeglib.h>
}

View file

@ -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

View file

@ -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<double> 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;

View file

@ -65,8 +65,8 @@ public:
virtual void add_box(box2d<double> 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);

View file

@ -45,8 +45,8 @@ public:
virtual void add_box(box2d<double> 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);

View file

@ -34,6 +34,7 @@
#include <vector>
#include <iostream>
#include <deque>
#include <algorithm>
namespace mapnik {

View file

@ -23,86 +23,31 @@
#ifndef MAPNIK_PLACEMENT_FINDER_HPP
#define MAPNIK_PLACEMENT_FINDER_HPP
// mapnik
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/text_placements.hpp>
// stl
#include <queue>
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<double> > envelopes;
//output
boost::ptr_vector<placement_element> 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<double, double> dimensions;
bool collect_extents;
box2d<double> 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<box2d<double> > additional_boxes;
};
template <typename DetectorT>
class placement_finder : boost::noncopyable
{
public:
placement_finder(DetectorT & detector);
placement_finder(DetectorT & detector, box2d<double> 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<double> 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 <typename T>
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 <typename T>
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<placement_element> get_placement_offset(placement & p,
const std::vector<vertex2d> & path_positions,
std::auto_ptr<text_path> get_placement_offset(const std::vector<vertex2d> & path_positions,
const std::vector<double> & 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<placement_element> & current_placement, const int & orientation);
bool test_placement(const std::auto_ptr<text_path> & 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<double> const& dimensions_;
string_info &info_;
text_symbolizer_properties &p;
text_placement_info &pi;
double string_width_;
double string_height_;
double first_line_space_;
vertical_alignment_e valign_;
horizontal_alignment_e halign_;
std::vector<unsigned> line_breaks_;
std::vector<std::pair<double, double> > line_sizes_;
};
}
#endif // MAPNIK_PLACEMENT_FINDER_HPP

View file

@ -258,7 +258,7 @@ T get(const boost::property_tree::ptree & node, const std::string & name, bool i
}
else
{
str = node.get_optional<std::string>(name );
str = node.get_optional<std::string>(name+".<xmltext>");
}
if ( str ) {
@ -289,7 +289,7 @@ inline color get(boost::property_tree::ptree const& node, std::string const& nam
}
else
{
str = node.get_optional<std::string>(name );
str = node.get_optional<std::string>(name+".<xmltext>");
}
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<std::string>(name);
str = node.get_optional<std::string>(name+".<xmltext>");
}
if ( ! str ) {
@ -348,7 +348,18 @@ T get_value(const boost::property_tree::ptree & node, const std::string & name)
{
try
{
return node.get_value<T>();
/* 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("<xmltext>").get_value<T>();
}
catch (boost::property_tree::ptree_bad_path)
{
/* If the XML parser did not find any non-empty data element the is no
<xmltext> 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<T> get_optional(const boost::property_tree::ptree & node, const
}
else
{
str = node.get_optional<std::string>(name);
str = node.get_optional<std::string>(name+".<xmltext>");
}
boost::optional<T> result;
@ -403,7 +414,7 @@ inline boost::optional<color> get_optional(const boost::property_tree::ptree & n
}
else
{
str = node.get_optional<std::string>(name);
str = node.get_optional<std::string>(name+".<xmltext>");
}
boost::optional<color> result;

View file

@ -33,7 +33,6 @@
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/expression_string.hpp>
@ -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<point_symbolizer,
line_symbolizer,
line_pattern_symbolizer,
@ -122,8 +116,7 @@ typedef boost::variant<point_symbolizer,
shield_symbolizer,
text_symbolizer,
building_symbolizer,
markers_symbolizer,
glyph_symbolizer> symbolizer;
markers_symbolizer> symbolizer;

View file

@ -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<text_placements_dummy>()));
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<double,double> const& get_shield_displacement() const;
position const& get_shield_displacement() const;
private:
bool unlock_image_;
bool no_text_;
boost::tuple<double,double> shield_displacement_;
position shield_displacement_;
};
}

View file

@ -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.

View file

@ -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_;
};
}

View file

@ -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 <mapnik/text_symbolizer.hpp>
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/text_processing.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <boost/shared_ptr.hpp>
namespace mapnik {
/** Helper object that does all the TextSymbolizer placment finding
* work except actually rendering the object. */
template <typename FaceManagerT, typename DetectorT>
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<double> dims_;
//Processing
processed_text text_;
/* Using list instead of vector, because we delete random elements and need iterators to stay valid. */
std::list<geometry_type*> geometries_to_process_;
std::list<geometry_type*>::iterator geo_itr_;
std::list<position> points_;
std::list<position>::iterator point_itr_;
double angle_;
string_info *info_;
bool placement_valid_;
bool point_placement_;
//Output
text_placement_info_ptr placement_;
};
template <typename FaceManagerT, typename DetectorT>
class shield_symbolizer_helper: public text_symbolizer_helper<FaceManagerT, DetectorT>
{
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<FaceManagerT, DetectorT>(sym, feature, prj_trans, width, height, scale_factor, t, font_manager, detector),
sym_(sym)
{
init_marker();
}
text_placement_info_ptr get_placement();
std::pair<int, int> 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<double> marker_ext_;
boost::optional<marker_ptr> 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<FaceManagerT, DetectorT>::geometries_to_process_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement;
using text_symbolizer_helper<FaceManagerT, DetectorT>::info_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::geo_itr_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::point_itr_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::points_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::writer_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::font_manager_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::feature_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::t_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::detector_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::dims_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::prj_trans_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_valid_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::point_placement_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::angle_;
};
} //namespace
#endif // SYMBOLIZER_HELPERS_HPP

View file

@ -23,6 +23,12 @@
#ifndef MAPNIK_TEXT_PATH_HPP
#define MAPNIK_TEXT_PATH_HPP
// mapnik
#include <mapnik/char_info.hpp>
//stl
#include <vector>
// boost
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
@ -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<character_info> characters_t;
typedef std::vector<char_info> 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<double, double> get_dimensions() const
{
return std::pair<double, double>(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_node> character_nodes_t;
character_nodes_t nodes_;
double starting_x;
double starting_y;
character_nodes_t nodes_;
int itr_;
std::pair<unsigned,unsigned> string_dimensions;
// std::pair<unsigned,unsigned> 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();

View file

@ -24,159 +24,257 @@
#define MAPNIK_TEXT_PLACEMENTS_HPP
// mapnik
#include <mapnik/config.hpp>
#include <mapnik/enumeration.hpp>
#include <mapnik/color.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/text_processing.hpp>
// stl
#include <vector>
#include <string>
#include <queue>
#include <set>
// boost
#include <boost/tuple/tuple.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>
#include <boost/property_tree/ptree.hpp>
namespace mapnik {
typedef boost::tuple<double,double> 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<double,double> position;
typedef std::pair<double,double> 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<std::string,font_set> 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<std::string,font_set> 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<expression_ptr> 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<freetype_engine> & 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<processed_expression> 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<freetype_engine> & 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<double> 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<box2d<double> > additional_boxes;
/* TODO */
std::queue< box2d<double> > envelopes;
/** Used to return all placements found. */
boost::ptr_vector<text_path> placements;
};
typedef boost::shared_ptr<text_placement_info> 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<expression_ptr> 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> 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

View file

@ -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 <mapnik/text_placements.hpp>
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<expression_ptr> get_all_expressions();
text_symbolizer_properties & add();
text_symbolizer_properties & get(unsigned i);
unsigned size() const;
private:
std::vector<text_symbolizer_properties> 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

View file

@ -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<directions_t> 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_;

View file

@ -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 <boost/property_tree/ptree.hpp>
#include <boost/optional.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/ctrans.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/enumeration.hpp>
#include <mapnik/filter_factory.hpp>
#include <list>
#include <vector>
#include <set>
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> 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<expression_ptr> &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<expression_ptr> &expressions) const;
void push_back(node_ptr n);
void set_children(std::vector<node_ptr> const& children);
std::vector<node_ptr> const& get_children() const;
void clear();
private:
std::vector<node_ptr> 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<expression_ptr> &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<std::string> face_name);
void set_text_size(boost::optional<unsigned> text_size);
void set_character_spacing(boost::optional<unsigned> character_spacing);
void set_line_spacing(boost::optional<unsigned> line_spacing);
void set_text_opacity(boost::optional<double> opacity);
void set_wrap_before(boost::optional<bool> wrap_before);
void set_wrap_char(boost::optional<unsigned> wrap_char);
void set_text_transform(boost::optional<text_transform_e> text_trans);
void set_fill(boost::optional<color> fill);
void set_halo_fill(boost::optional<color> halo_fill);
void set_halo_radius(boost::optional<double> radius);
private:
boost::optional<std::string> face_name_;
boost::optional<unsigned> text_size_;
boost::optional<unsigned> character_spacing_;
boost::optional<unsigned> line_spacing_;
boost::optional<double> text_opacity_;
boost::optional<bool> wrap_before_;
boost::optional<unsigned> wrap_char_;
boost::optional<text_transform_e> text_transform_;
boost::optional<color> fill_;
boost::optional<color> halo_fill_;
boost::optional<double> halo_radius_;
node_ptr child_;
};
} //namespace formating
} /* namespace mapnik*/
#endif

View file

@ -26,8 +26,6 @@
// mapnik
#include <mapnik/color.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/text_placements.hpp>
@ -38,6 +36,13 @@
// stl
#include <string>
// 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_;
};
}

View file

@ -31,6 +31,7 @@
#include <mapnik/font_set.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/map.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>

View file

@ -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 <mapnik/agg_renderer.hpp>
// 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 <typename T>
void agg_renderer<T>::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<T> 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<double> dim = ren.prepare_glyphs(path.get());
box2d<double> 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<image_32>::process(glyph_symbolizer const&,
Feature const&,
proj_transform const&);
}

View file

@ -27,6 +27,7 @@
#include <mapnik/agg_pattern_source.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
// agg
#include "agg_basics.h"

View file

@ -24,6 +24,7 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/line_symbolizer.hpp>
// agg
#include "agg_basics.h"

View file

@ -30,6 +30,7 @@
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/markers_placement.hpp>
#include <mapnik/arrow.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"

View file

@ -24,6 +24,7 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/polygon_symbolizer.hpp>
// agg
#include "agg_basics.h"

View file

@ -23,17 +23,13 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/expression_evaluator.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_u.h"
#include <mapnik/symbolizer_helpers.hpp>
// boost
#include <boost/make_shared.hpp>
@ -45,223 +41,26 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
shield_symbolizer_helper<face_manager<freetype_engine>,
label_collision_detector4> helper(
sym, feature, prj_trans,
width_, height_,
scale_factor_,
t_, font_manager_, *detector_);
text_renderer<T> 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,value_type>(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<double,6> const& m = sym.get_transform();
tr.load_from(&m[0]);
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<mapnik::marker_ptr> marker;
if ( !filename.empty() )
{
marker = marker_cache::instance()->find(filename, true);
}
else
{
marker.reset(boost::make_shared<mapnik::marker>());
}
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<double> 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<int, int> 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<T> 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<label_collision_detector4> 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<double> 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<double>(-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<path_type>(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<double> 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);
}
}
}

View file

@ -24,7 +24,7 @@
// mapnik
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/symbolizer_helpers.hpp>
namespace mapnik {
@ -33,147 +33,23 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
text_symbolizer_helper<face_manager<freetype_engine>,
label_collision_detector4> helper(
sym, feature, prj_trans,
width_, height_,
scale_factor_,
t_, font_manager_, *detector_);
text_renderer<T> ren(pixmap_, font_manager_, *(font_manager_.get_stroker()));
// Use a boost::ptr_vector here instread of std::vector?
std::vector<geometry_type*> geometries_to_process;
unsigned num_geom = feature.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() == 0) continue; // don't bother with empty geometries
if ((geom.type() == Polygon) && sym.get_minimum_path_length() > 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<double> 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<geometry_type*>(&geom));
}
if (!geometries_to_process.size() > 0)
return; // early return to avoid significant overhead of rendering setup
typedef coord_transform2<CoordTransform,geometry_type> 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,value_type>(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<T> 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<double> dims(0,0,width_,height_);
placement_finder<label_collision_detector4> 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,value_type>(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<path_type>(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);
}
}
}

View file

@ -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

View file

@ -36,6 +36,7 @@
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/segment.hpp>
#include <mapnik/symbolizer_helpers.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/warp.hpp>
#include <mapnik/config.hpp>
@ -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<freetype_engine> &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<CoordTransform,geometry_type> path_type;
shield_symbolizer_helper<face_manager<freetype_engine>,
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,value_type>(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<double,6> const& m = sym.get_transform();
tr.load_from(&m[0]);
std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature);
boost::optional<marker_ptr> marker;
if ( !filename.empty() )
{
marker = marker_cache::instance()->find(filename, true);
}
else
{
marker.reset(boost::make_shared<mapnik::marker>());
}
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<label_collision_detector4> 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<double> 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<double>(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<path_type>(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<double>(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<int, int> 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<double> 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<CoordTransform,geometry_type> path_type;
text_symbolizer_helper<face_manager<freetype_engine>, 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,value_type>(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<label_collision_detector4> finder(detector_);
metawriter_with_properties writer = sym.get_metawriter();
unsigned num_geom = feature.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() == 0) continue;// don't bother with empty geometries
while (!placement_found && placement_options->next_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,value_type>(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<path_type>(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_);
}
}
}

View file

@ -19,10 +19,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
// mapnik
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/text_placements.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/grid/grid.hpp>
// boost
#include <boost/algorithm/string.hpp>
@ -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<unsigned, dimension_t>::const_iterator itr;
//Check if char is already in cache
std::map<unsigned, char_info>::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<unsigned, dimension_t>(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<unsigned, char_info>(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 <typename T>
text_renderer<T>::text_renderer (pixmap_type & pixmap, face_manager<freetype_engine> &font_manager_, stroker & s)
: pixmap_(pixmap),
font_manager_(font_manager_),
stroker_(s)
{
}
template <typename T>
box2d<double> text_renderer<T>::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<double>(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
}
template <typename T>
void text_renderer<T>::render(double x0, double y0)
{
FT_Error error;
FT_Vector start;
unsigned height = pixmap_.height();
start.x = static_cast<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((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 <typename T>
void text_renderer<T>::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<FT_Pos>(x0 * (1 << 6));
start.y = static_cast<FT_Pos>((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<std::string,std::pair<int,std::string> > freetype_engine::name2file_;
template void text_renderer<image_32>::render(double, double);
template text_renderer<image_32>::text_renderer(image_32&, face_manager<freetype_engine>&, stroker&);
template box2d<double>text_renderer<image_32>::prepare_glyphs(text_path*);
template void text_renderer<grid>::render_id(int, double, double, double);
template text_renderer<grid>::text_renderer(grid&, face_manager<freetype_engine>&, stroker&);
template box2d<double>text_renderer<grid>::prepare_glyphs(text_path*);
}

View file

@ -1,149 +0,0 @@
#include <mapnik/glyph_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
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<double>(cdim.width)/2.0;
double cheight = static_cast<double>(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,value_type>(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,value_type>(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,value_type>(feature),
*expr
);
#ifdef MAPNIK_DEBUG
std::clog << "size_result=" << result.to_string() << "\n";
#endif
float size = static_cast<float>(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_type>(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,value_type>(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

View file

@ -28,6 +28,7 @@
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/placement_finder.hpp>
@ -35,10 +36,12 @@
#include <mapnik/font_set.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/map.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
// boost
#include <boost/utility.hpp>

View file

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View file

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/line_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View file

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>

View file

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>

View file

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/polygon_pattern_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View file

@ -27,6 +27,7 @@
#include <mapnik/grid/grid_pixfmt.hpp>
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/polygon_symbolizer.hpp>
// agg
#include "agg_rasterizer_scanline_aa.h"

View file

@ -28,8 +28,8 @@
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/symbolizer_helpers.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
@ -44,200 +44,36 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
shield_symbolizer_helper<face_manager<freetype_engine>,
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<T> 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,value_type>(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<double,6> 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<mapnik::marker_ptr> marker;
if ( !filename.empty() )
{
marker = marker_cache::instance()->find(filename, true);
}
else
{
marker.reset(boost::make_shared<mapnik::marker>());
}
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<int, int> 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<T> 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<label_collision_detector4> 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<double> 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<double> 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<path_type>(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<double> 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<grid>::process(shield_symbolizer const&,

View file

@ -23,8 +23,7 @@
// mapnik
#include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/symbolizer_helpers.hpp>
namespace mapnik {
@ -33,120 +32,29 @@ void grid_renderer<T>::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
text_symbolizer_helper<face_manager<freetype_engine>,
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,value_type>(feature),*name_expr);
UnicodeString text = result.to_unicode();
if ( sym.get_text_transform() == UPPERCASE)
text_renderer<T> 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<T> 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<double> dims(0,0,width_,height_);
placement_finder<label_collision_detector4> finder(detector_,dims);
string_info info(text);
faces->get_string_info(info);
unsigned num_geom = feature.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature.get_geometry(i);
if (geom.num_points() == 0) continue; // don't bother with empty geometries
while (!placement_found && placement_options->next_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,value_type>(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<path_type>(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<grid>::process(text_symbolizer const&,

View file

@ -29,6 +29,7 @@
#include <boost/utility.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <libxml/parser.h>
#include <libxml/tree.h>
@ -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("<xmltext>", ptree()));
it->second.put_value(trimmed);
}
break;
case XML_COMMENT_NODE:
{
ptree::iterator it = pt.push_back(

View file

@ -48,6 +48,10 @@
#include <mapnik/metawriter_factory.hpp>
#include <mapnik/text_placements_simple.hpp>
#include <mapnik/text_placements_list.hpp>
#include <mapnik/text_processing.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/rule.hpp>
// boost
#include <boost/optional.hpp>
@ -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<std::string>(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<std::string> placement_type = get_opt_attr<std::string>(sym, "placement-type");
if (placement_type) {
if (*placement_type == "simple") {
placement_finder = text_placements_ptr(
new text_placements_simple(
get_attr<std::string>(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<std::string> old_name = get_opt_attr<std::string>(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<std::string>(sym, "TextSymbolizer");
if (name.empty()) throw config_error(std::string("TextSymbolizer needs a non-empty text"));
}
optional<std::string> face_name =
get_opt_attr<std::string>(sym, "face-name");
optional<std::string> fontset_name =
get_opt_attr<std::string>(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<std::string> orientation = get_opt_attr<std::string>(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<std::string,font_set>::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<label_placement_e>(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<vertical_alignment_e>(sym, "vertical-alignment", default_vertical_alignment);
text_symbol.set_vertical_alignment(valign);
// halo fill and radius
optional<color> halo_fill = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill)
{
text_symbol.set_halo_fill( * halo_fill );
}
optional<double> halo_radius =
get_opt_attr<double>(sym, "halo-radius");
if (halo_radius)
{
text_symbol.set_halo_radius(*halo_radius);
}
// text ratio and wrap width
optional<unsigned> text_ratio =
get_opt_attr<unsigned>(sym, "text-ratio");
if (text_ratio)
{
text_symbol.set_text_ratio(*text_ratio);
}
optional<unsigned> wrap_width =
get_opt_attr<unsigned>(sym, "wrap-width");
if (wrap_width)
{
text_symbol.set_wrap_width(*wrap_width);
}
optional<boolean> wrap_before =
get_opt_attr<boolean>(sym, "wrap-before");
if (wrap_before)
{
text_symbol.set_wrap_before(*wrap_before);
}
// character used to break long strings
optional<std::string> wrap_char =
get_opt_attr<std::string>(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<text_transform_e>(sym, "text-transform", NONE);
text_symbol.set_text_transform(tconvert);
// spacing between text lines
optional<unsigned> line_spacing = get_opt_attr<unsigned>(sym, "line-spacing");
if (line_spacing)
{
text_symbol.set_line_spacing(*line_spacing);
}
// tolerance between label spacing along line
optional<unsigned> label_position_tolerance = get_opt_attr<unsigned>(sym, "label-position-tolerance");
if (label_position_tolerance)
{
text_symbol.set_label_position_tolerance(*label_position_tolerance);
}
// spacing between characters in text
optional<unsigned> character_spacing = get_opt_attr<unsigned>(sym, "character-spacing");
if (character_spacing)
{
text_symbol.set_character_spacing(*character_spacing);
}
// spacing between repeated labels on lines
optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
if (spacing)
{
text_symbol.set_label_spacing(*spacing);
}
// minimum distance between labels
optional<unsigned> min_distance = get_opt_attr<unsigned>(sym, "minimum-distance");
if (min_distance)
{
text_symbol.set_minimum_distance(*min_distance);
}
// minimum distance from edge of the map
optional<unsigned> min_padding = get_opt_attr<unsigned>(sym, "minimum-padding");
if (min_padding)
{
text_symbol.set_minimum_padding(*min_padding);
}
// minimum path length
optional<unsigned> min_path_length = get_opt_attr<unsigned>(sym, "minimum-path-length");
if (min_path_length)
{
text_symbol.set_minimum_path_length(*min_path_length);
}
// do not render labels around edges
optional<boolean> avoid_edges =
get_opt_attr<boolean>(sym, "avoid-edges");
if (avoid_edges)
{
text_symbol.set_avoid_edges( * avoid_edges);
}
// allow_overlap
optional<boolean> allow_overlap =
get_opt_attr<boolean>(sym, "allow-overlap");
if (allow_overlap)
{
text_symbol.set_allow_overlap( * allow_overlap );
}
// opacity
optional<double> opacity =
get_opt_attr<double>(sym, "opacity");
if (opacity)
{
text_symbol.set_text_opacity( * opacity );
}
// max_char_angle_delta
optional<double> max_char_angle_delta =
get_opt_attr<double>(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<horizontal_alignment_e>(sym, "horizontal-alignment", H_AUTO);
text_symbol.set_horizontal_alignment(halign);
// justify alignment
justify_alignment_e jalign = get_attr<justify_alignment_e>(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<boolean> no_text =
get_opt_attr<boolean>(sym, "no-text");
std::string name;
optional<std::string> old_name = get_opt_attr<std::string>(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<std::string>(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<std::string> placement_type = get_opt_attr<std::string>(sym, "placement-type");
if (placement_type) {
if (*placement_type == "simple") {
placement_finder = text_placements_ptr(
new text_placements_simple(
get_attr<std::string>(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<std::string> face_name =
get_opt_attr<std::string>(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<std::string> fontset_name =
get_opt_attr<std::string>(sym, "fontset-name");
shield_symbolizer shield_symbol = shield_symbolizer(placement_finder);
/* Symbolizer specific attributes. */
optional<std::string> transform_wkt = get_opt_attr<std::string>(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<double,6> 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<double> opacity = get_opt_attr<double>(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<double> text_opacity =
get_opt_attr<double>(sym, "text-opacity");
if (text_opacity)
{
shield_symbol.set_text_opacity( * text_opacity );
}
// unlock_image
optional<boolean> unlock_image =
get_opt_attr<boolean>(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<std::string>(sym, "file");
optional<std::string> base = get_opt_attr<std::string>(sym, "base");
optional<std::string> transform_wkt = get_opt_attr<std::string>(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<std::string,font_set>::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<label_placement_e>(sym, "placement", POINT_PLACEMENT);
shield_symbol.set_label_placement( placement );
// don't render shields around edges
optional<boolean> avoid_edges =
get_opt_attr<boolean>(sym, "avoid-edges");
if (avoid_edges)
{
shield_symbol.set_avoid_edges( *avoid_edges);
}
// halo fill and radius
optional<color> halo_fill = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill)
{
shield_symbol.set_halo_fill( * halo_fill );
}
optional<double> halo_radius =
get_opt_attr<double>(sym, "halo-radius");
if (halo_radius)
{
shield_symbol.set_halo_radius(*halo_radius);
}
// minimum distance between labels
optional<unsigned> min_distance = get_opt_attr<unsigned>(sym, "minimum-distance");
if (min_distance)
{
shield_symbol.set_minimum_distance(*min_distance);
}
// minimum distance from edge of the map
optional<unsigned> min_padding = get_opt_attr<unsigned>(sym, "minimum-padding");
if (min_padding)
{
shield_symbol.set_minimum_padding(*min_padding);
}
// spacing between repeated labels on lines
optional<unsigned> spacing = get_opt_attr<unsigned>(sym, "spacing");
if (spacing)
{
shield_symbol.set_label_spacing(*spacing);
}
// allow_overlap
optional<boolean> allow_overlap =
get_opt_attr<boolean>(sym, "allow-overlap");
if (allow_overlap)
{
shield_symbol.set_allow_overlap( * allow_overlap );
}
// vertical alignment
vertical_alignment_e valign = get_attr<vertical_alignment_e>(sym, "vertical-alignment", V_MIDDLE);
shield_symbol.set_vertical_alignment(valign);
// horizontal alignment
horizontal_alignment_e halign = get_attr<horizontal_alignment_e>(sym, "horizontal-alignment", H_MIDDLE);
shield_symbol.set_horizontal_alignment(halign);
// justify alignment
justify_alignment_e jalign = get_attr<justify_alignment_e>(sym, "justify-alignment", J_MIDDLE);
shield_symbol.set_justify_alignment(jalign);
optional<unsigned> wrap_width =
get_opt_attr<unsigned>(sym, "wrap-width");
if (wrap_width)
{
shield_symbol.set_wrap_width(*wrap_width);
}
optional<boolean> wrap_before =
get_opt_attr<boolean>(sym, "wrap-before");
if (wrap_before)
{
shield_symbol.set_wrap_before(*wrap_before);
}
// character used to break long strings
optional<std::string> wrap_char =
get_opt_attr<std::string>(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<text_transform_e>(sym, "text-transform", NONE);
shield_symbol.set_text_transform(tconvert);
// spacing between text lines
optional<unsigned> line_spacing = get_opt_attr<unsigned>(sym, "line-spacing");
if (line_spacing)
{
shield_symbol.set_line_spacing(*line_spacing);
}
// spacing between characters in text
optional<unsigned> character_spacing = get_opt_attr<unsigned>(sym, "character-spacing");
if (character_spacing)
{
shield_symbol.set_character_spacing(*character_spacing);
}
// opacity
optional<double> opacity =
get_opt_attr<double>(sym, "opacity");
if (opacity)
{
shield_symbol.set_opacity( * opacity );
}
// text-opacity
optional<double> text_opacity =
get_opt_attr<double>(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<double,6> matrix;
tr.store_to(&matrix[0]);
shield_symbol.set_transform(matrix);
}
// unlock_image
optional<boolean> unlock_image =
get_opt_attr<boolean>(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<std::string>(sym, "face-name");
std::string _char = get_attr<std::string>(sym, "char");
glyph_symbolizer glyph_sym = glyph_symbolizer(
face_name,
parse_expression(_char, "utf8")
);
//
// parse and set optional attrs.
//
// angle
optional<std::string> angle =
get_opt_attr<std::string>(sym, "angle");
if (angle)
glyph_sym.set_angle(parse_expression(*angle, "utf8"));
angle_mode_e angle_mode =
get_attr<angle_mode_e>(sym, "angle-mode", TRIGONOMETRIC);
glyph_sym.set_angle_mode(angle_mode);
// value
optional<std::string> value =
get_opt_attr<std::string>(sym, "value");
if (value)
glyph_sym.set_value(parse_expression(*value, "utf8"));
// size
std::string size =
get_attr<std::string>(sym, "size");
glyph_sym.set_size(parse_expression(size, "utf8"));
// color
optional<std::string> _color =
get_opt_attr<std::string>(sym, "color");
if (_color)
glyph_sym.set_color(parse_expression(*_color, "utf8"));
// halo_fill
optional<color> halo_fill = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill)
glyph_sym.set_halo_fill(*halo_fill);
// halo_radius
optional<double> halo_radius = get_opt_attr<double>(
sym,
"halo-radius");
if (halo_radius)
glyph_sym.set_halo_radius(*halo_radius);
// allow_overlap
optional<boolean> allow_overlap = get_opt_attr<boolean>(
sym,
"allow-overlap"
);
if (allow_overlap)
glyph_sym.set_allow_overlap(*allow_overlap);
// avoid_edges
optional<boolean> avoid_edges = get_opt_attr<boolean>(
sym,
"avoid-edges"
);
if (avoid_edges)
glyph_sym.set_avoid_edges(*avoid_edges);
// displacement
optional<double> dx = get_opt_attr<double>(sym, "dx");
optional<double> dy = get_opt_attr<double>(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!="<xmlcomment>" && tag.first!="<xmlattr>" )
{
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 )
{

View file

@ -23,7 +23,7 @@
// Mapnik
#include <mapnik/metawriter.hpp>
#include <mapnik/metawriter_json.hpp>
#include <mapnik/placement_finder.hpp>
#include <mapnik/text_placements.hpp>
// Boost
#include <boost/foreach.hpp>
@ -175,8 +175,8 @@ void metawriter_json_stream::add_box(box2d<double> 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<placement_element &>(p.placements[n]);
text_path & current_placement = const_cast<text_path &>(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<double>(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);

View file

@ -72,9 +72,9 @@ metawriter_inmem::add_box(box2d<double> 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) {

View file

@ -26,6 +26,7 @@
#include <mapnik/placement_finder.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/text_path.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/fastmath.hpp>
// 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<typename T>
std::pair<double, double> get_position_at_distance(double target_distance, T & shape_path)
@ -151,22 +98,26 @@ double get_total_distance(T & shape_path)
}
template <typename DetectorT>
placement_finder<DetectorT>::placement_finder(DetectorT & detector)
placement_finder<DetectorT>::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 <typename DetectorT>
placement_finder<DetectorT>::placement_finder(DetectorT & detector, box2d<double> const& extent)
placement_finder<DetectorT>::placement_finder(text_placement_info &placement_info, string_info &info, DetectorT & detector, box2d<double> 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 <typename DetectorT>
template <typename T>
void placement_finder<DetectorT>::find_point_placements(placement & p, text_placement_info_ptr po, T & shape_path)
void placement_finder<DetectorT>::find_point_placements(T & shape_path)
{
unsigned cmd;
double new_x = 0.0;
@ -182,15 +133,15 @@ void placement_finder<DetectorT>::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<int> (floor(total_distance / p.label_spacing));
num_labels = static_cast<int> (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<DetectorT>::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<DetectorT>::find_point_placements(placement & p, text_plac
}
template <typename DetectorT>
void placement_finder<DetectorT>::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<DetectorT>::init_string_size()
{
double x, y;
std::auto_ptr<placement_element> current_placement(new placement_element);
std::pair<double, double> 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 <typename DetectorT>
void placement_finder<DetectorT>::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<int> line_breaks;
std::vector<double> 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 <typename DetectorT>
void placement_finder<DetectorT>::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 <typename DetectorT>
void placement_finder<DetectorT>::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 <typename DetectorT>
void placement_finder<DetectorT>::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<text_path> 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<double> > 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<double> 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<double> 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<double> 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<DetectorT>::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<double> box, p.additional_boxes)
BOOST_FOREACH(box2d<double> box, pi.additional_boxes)
{
box2d<double> pt(box.minx() + current_placement->starting_x,
box.miny() + current_placement->starting_y,
@ -502,18 +478,19 @@ void placement_finder<DetectorT>::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 <typename DetectorT>
template <typename PathT>
void placement_finder<DetectorT>::find_line_placements(placement & p, text_placement_info_ptr po, PathT & shape_path)
void placement_finder<DetectorT>::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<DetectorT>::find_line_placements(placement & p, text_place
return;
double distance = 0.0;
std::pair<double, double> 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<int> (floor(total_distance / (p.label_spacing + string_width)));
num_labels = static_cast<int> (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<DetectorT>::find_line_placements(placement & p, text_place
{
//Record details for the start of the string placement
int orientation = 0;
std::auto_ptr<placement_element> current_placement = get_placement_offset(p, path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir));
std::auto_ptr<text_path> 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<DetectorT>::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<DetectorT>::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<DetectorT>::find_line_placements(placement & p, text_place
}
template <typename DetectorT>
std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offset(placement & p, const std::vector<vertex2d> &path_positions, const std::vector<double> &path_distances, int &orientation, unsigned index, double distance)
std::auto_ptr<text_path> placement_finder<DetectorT>::get_placement_offset(const std::vector<vertex2d> &path_positions, const std::vector<double> &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_element> placement_finder<DetectorT>::get_placement_offs
distance += path_distances[index];
}
if (index <= 1 && distance < 0) //We've gone off the start, fail out
return std::auto_ptr<placement_element>(NULL);
return std::auto_ptr<text_path>(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_element> placement_finder<DetectorT>::get_placement_offs
index++;
}
if (index >= path_distances.size())
return std::auto_ptr<placement_element>(NULL);
return std::auto_ptr<text_path>(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<placement_element> current_placement(new placement_element);
std::auto_ptr<text_path> 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_element> placement_finder<DetectorT>::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<placement_element>(NULL);
return std::auto_ptr<text_path>(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_element> placement_finder<DetectorT>::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<placement_element>(NULL);
return std::auto_ptr<text_path>(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_element> placement_finder<DetectorT>::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<placement_element>(NULL);
return std::auto_ptr<text_path>(NULL);
}
new_x = path_positions[index].x;
new_y = path_positions[index].y;
@ -815,7 +783,7 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
fabs(angle_delta) > p.max_char_angle_delta)
{
//std::clog << "FAIL: Too Bendy!" << std::endl;
return std::auto_ptr<placement_element>(NULL);
return std::auto_ptr<text_path>(NULL);
}
double render_angle = angle;
@ -826,19 +794,20 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::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_element> placement_finder<DetectorT>::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<placement_element>(NULL);
return std::auto_ptr<text_path>(NULL);
}
}
@ -871,57 +840,50 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
}
template <typename DetectorT>
bool placement_finder<DetectorT>::test_placement(placement & p, const std::auto_ptr<placement_element> & current_placement, const int & orientation)
bool placement_finder<DetectorT>::test_placement(const std::auto_ptr<text_path> & current_placement, const int & orientation)
{
std::pair<double, double> 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<double> 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<DetectorT>::test_placement(placement & p, const std::auto_
status = false;
break;
}
if (p.minimum_padding > 0)
if (p.minimum_padding > 0)
{
box2d<double> 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<double> 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<DetectorT>::find_line_circle_intersection(
}
template <typename DetectorT>
void placement_finder<DetectorT>::update_detector(placement & p)
void placement_finder<DetectorT>::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<double> e = p.envelopes.front();
detector_.insert(e, p.info.get_string());
p.envelopes.pop();
box2d<double> 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<CoordTransform,geometry_type> PathType;
typedef label_collision_detector4 DetectorType;
template class placement_finder<DetectorType>;
template void placement_finder<DetectorType>::find_point_placements<PathType> (placement&, text_placement_info_ptr po, PathType & );
template void placement_finder<DetectorType>::find_line_placements<PathType> (placement&, text_placement_info_ptr po, PathType & );
template void placement_finder<DetectorType>::find_point_placements<PathType>(PathType &);
template void placement_finder<DetectorType>::find_line_placements<PathType>(PathType &);
} // namespace

View file

@ -28,6 +28,8 @@
#include <mapnik/expression_string.hpp>
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/metawriter_factory.hpp>
#include <mapnik/text_placements_simple.hpp>
#include <mapnik/text_placements_list.hpp>
// boost
#include <boost/algorithm/string.hpp>
@ -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(), "<no default>", 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("<no default>", 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("<xmltext>", 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<text_placements_simple *>(p.get());
text_placements_list *list = dynamic_cast<text_placements_list *>(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(), "<no default>",
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));
}
}
}

View file

@ -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<double,double>(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<double,double>(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<double,double> const& shield_symbolizer::get_shield_displacement() const
position const& shield_symbolizer::get_shield_displacement() const
{
return shield_displacement_;
}

View file

@ -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 <mapnik/svg_renderer.hpp>
namespace mapnik
{
template <typename T>
void svg_renderer<T>::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
// nothing yet.
}
template void svg_renderer<std::ostream_iterator<char> >::process(glyph_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
}

View file

@ -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

351
src/symbolizer_helpers.cpp Normal file
View file

@ -0,0 +1,351 @@
#include <mapnik/symbolizer_helpers.hpp>
namespace mapnik {
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_placement()
{
if (!placement_valid_) return text_placement_info_ptr();
if (point_placement_)
return get_point_placement();
else
return get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::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<DetectorT> finder(*placement_, *info_, detector_, dims_);
typedef coord_transform2<CoordTransform,geometry_type> 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<geometry_type*>::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 <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::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<DetectorT> 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<position>::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 <typename FaceManagerT, typename DetectorT>
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries()
{
unsigned num_geom = feature_.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature_.get_geometry(i);
// don't bother with empty geometries
if (geom.num_points() == 0) continue;
if ((geom.type() == Polygon) && sym_.get_minimum_path_length() > 0)
{
// TODO - find less costly method than fetching full envelope
box2d<double> 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<geometry_type*>(&geom));
}
geo_itr_ = geometries_to_process_.begin();
}
template <typename FaceManagerT, typename DetectorT>
void text_symbolizer_helper<FaceManagerT, DetectorT>::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<geometry_type*>::const_iterator itr = geometries_to_process_.begin();
std::list<geometry_type*>::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 <typename FaceManagerT, typename DetectorT>
bool text_symbolizer_helper<FaceManagerT, DetectorT>::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, value_type>(feature_),
*(placement_->properties.orientation)).to_double();
} else {
angle_ = 0.0;
}
return true;
}
/*****************************************************************************/
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_placement()
{
if (!placement_valid_ || !marker_) return text_placement_info_ptr();
if (point_placement_)
return get_point_placement();
else
return get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::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<DetectorT> 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<position>::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 <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::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<double>(-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<FaceManagerT, DetectorT>::get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
void shield_symbolizer_helper<FaceManagerT, DetectorT>::init_marker()
{
std::string filename = path_processor_type::evaluate(*sym_.get_filename(), this->feature_);
boost::array<double,6> 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 <typename FaceManagerT, typename DetectorT>
std::pair<int, int> shield_symbolizer_helper<FaceManagerT, DetectorT>::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 <typename FaceManagerT, typename DetectorT>
marker& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker() const
{
return **marker_;
}
template <typename FaceManagerT, typename DetectorT>
agg::trans_affine const& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_transform() const
{
return transform_;
}
template class text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
template class shield_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
} //namespace

View file

@ -22,11 +22,16 @@
#include <mapnik/text_placements.hpp>
#include <mapnik/text_placements_simple.hpp>
#include <mapnik/text_placements_list.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/text_processing.hpp>
#include <mapnik/ptree_helpers.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/optional.hpp>
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<std::string,font_set> const & fontsets)
{
optional<label_placement_e> placement_ = get_opt_attr<label_placement_e>(sym, "placement");
if (placement_) label_placement = *placement_;
optional<vertical_alignment_e> valign_ = get_opt_attr<vertical_alignment_e>(sym, "vertical-alignment");
if (valign_) valign = *valign_;
optional<unsigned> text_ratio_ = get_opt_attr<unsigned>(sym, "text-ratio");
if (text_ratio_) text_ratio = *text_ratio_;
optional<unsigned> wrap_width_ = get_opt_attr<unsigned>(sym, "wrap-width");
if (wrap_width_) wrap_width = *wrap_width_;
optional<unsigned> label_position_tolerance_ = get_opt_attr<unsigned>(sym, "label-position-tolerance");
if (label_position_tolerance_) label_position_tolerance = *label_position_tolerance_;
optional<unsigned> spacing_ = get_opt_attr<unsigned>(sym, "spacing");
if (spacing_) label_spacing = *spacing_;
optional<unsigned> minimum_distance_ = get_opt_attr<unsigned>(sym, "minimum-distance");
if (minimum_distance_) minimum_distance = *minimum_distance_;
optional<unsigned> min_padding_ = get_opt_attr<unsigned>(sym, "minimum-padding");
if (min_padding_) minimum_padding = *min_padding_;
optional<unsigned> min_path_length_ = get_opt_attr<unsigned>(sym, "minimum-path-length");
if (min_path_length_) minimum_path_length = *min_path_length_;
optional<boolean> avoid_edges_ = get_opt_attr<boolean>(sym, "avoid-edges");
if (avoid_edges_) avoid_edges = *avoid_edges_;
optional<boolean> allow_overlap_ = get_opt_attr<boolean>(sym, "allow-overlap");
if (allow_overlap_) allow_overlap = *allow_overlap_;
optional<horizontal_alignment_e> halign_ = get_opt_attr<horizontal_alignment_e>(sym, "horizontal-alignment");
if (halign_) halign = *halign_;
optional<justify_alignment_e> jalign_ = get_opt_attr<justify_alignment_e>(sym, "justify-alignment");
if (jalign_) jalign = *jalign_;
/* Attributes needing special care */
optional<std::string> orientation_ = get_opt_attr<std::string>(sym, "orientation");
if (orientation_) orientation = parse_expression(*orientation_, "utf8");
optional<double> dx = get_opt_attr<double>(sym, "dx");
if (dx) displacement.first = *dx;
optional<double> dy = get_opt_attr<double>(sym, "dy");
if (dy) displacement.second = *dy;
optional<double> max_char_angle_delta_ = get_opt_attr<double>(sym, "max-char-angle-delta");
if (max_char_angle_delta_) max_char_angle_delta=(*max_char_angle_delta_)*(M_PI/180);
optional<std::string> name_ = get_opt_attr<std::string>(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<expression_ptr> text_symbolizer_properties::get_all_expressions() const
{
std::set<expression_ptr> 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<std::string,font_set> const & fontsets)
{
optional<double> text_size_ = get_opt_attr<double>(sym, "size");
if (text_size_) text_size = *text_size_;
optional<double> character_spacing_ = get_opt_attr<double>(sym, "character-spacing");
if (character_spacing_) character_spacing = *character_spacing_;
optional<color> fill_ = get_opt_attr<color>(sym, "fill");
if (fill_) fill = *fill_;
optional<color> halo_fill_ = get_opt_attr<color>(sym, "halo-fill");
if (halo_fill_) halo_fill = *halo_fill_;
optional<double> halo_radius_ = get_opt_attr<double>(sym, "halo-radius");
if (halo_radius_) halo_radius = *halo_radius_;
optional<boolean> wrap_before_ = get_opt_attr<boolean>(sym, "wrap-before");
if (wrap_before_) wrap_before = *wrap_before_;
optional<text_transform_e> tconvert_ = get_opt_attr<text_transform_e>(sym, "text-transform");
if (tconvert_) text_transform = *tconvert_;
optional<double> line_spacing_ = get_opt_attr<double>(sym, "line-spacing");
if (line_spacing_) line_spacing = *line_spacing_;
optional<double> opacity_ = get_opt_attr<double>(sym, "opacity");
if (opacity_) text_opacity = *opacity_;
optional<std::string> wrap_char_ = get_opt_attr<std::string>(sym, "wrap-character");
if (wrap_char_ && (*wrap_char_).size() > 0) wrap_char = ((*wrap_char_)[0]);
optional<std::string> face_name_ = get_opt_attr<std::string>(sym, "face-name");
if (face_name_)
{
face_name = *face_name_;
}
optional<std::string> fontset_name_ = get_opt_attr<std::string>(sym, "fontset-name");
if (fontset_name_) {
std::map<std::string,font_set>::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<expression_ptr> text_placements::get_all_expressions()
{
std::set<expression_ptr> 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<expression_ptr> text_placements_list::get_all_expressions()
{
std::set<expression_ptr> result, tmp;
tmp = properties.get_all_expressions();
result.insert(tmp.begin(), tmp.end());
result.insert(properties.orientation);
std::vector<text_symbolizer_properties>::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

399
src/text_processing.cpp Normal file
View file

@ -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 <mapnik/text_processing.hpp>
#include <mapnik/text_placements.hpp>
#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/ptree_helpers.hpp>
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/shared_ptr.hpp>
#include <stack>
#include <vector>
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 == "<xmltext>") {
n = text_node::from_xml(itr->second);
} else if (itr->first == "Format") {
n = format_node::from_xml(itr->second);
} else if (itr->first != "<xmlcomment>" && itr->first != "<xmlattr>" && 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<expression_ptr> &expressions) const
{
//Do nothing by default
}
/************************************************************/
void list_node::to_xml(boost::property_tree::ptree &xml) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::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<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::const_iterator end = children_.end();
for (;itr != end; itr++)
{
(*itr)->apply(p, feature, output);
}
}
void list_node::add_expressions(std::set<expression_ptr> &expressions) const
{
std::vector<node_ptr>::const_iterator itr = children_.begin();
std::vector<node_ptr>::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<node_ptr> const& children)
{
children_ = children;
}
std::vector<node_ptr> 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(
"<xmltext>", 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,value_type>(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<expression_ptr> &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<std::string>(xml, "face-name"));
/*TODO: Fontset is problematic. We don't have the fontsets pointer here... */
n->set_text_size(get_opt_attr<unsigned>(xml, "size"));
n->set_character_spacing(get_opt_attr<unsigned>(xml, "character-spacing"));
n->set_line_spacing(get_opt_attr<unsigned>(xml, "line-spacing"));
n->set_text_opacity(get_opt_attr<double>(xml, "opactity"));
boost::optional<boolean> wrap = get_opt_attr<boolean>(xml, "wrap-before");
boost::optional<bool> wrap_before;
if (wrap) wrap_before = *wrap;
n->set_wrap_before(wrap_before);
n->set_wrap_char(get_opt_attr<unsigned>(xml, "wrap-character"));
n->set_text_transform(get_opt_attr<text_transform_e>(xml, "text-transform"));
n->set_fill(get_opt_attr<color>(xml, "fill"));
n->set_halo_fill(get_opt_attr<color>(xml, "halo-fill"));
n->set_halo_radius(get_opt_attr<double>(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<std::string> face_name)
{
face_name_ = face_name;
}
void format_node::set_text_size(optional<unsigned> text_size)
{
text_size_ = text_size;
}
void format_node::set_character_spacing(optional<unsigned> character_spacing)
{
character_spacing_ = character_spacing;
}
void format_node::set_line_spacing(optional<unsigned> line_spacing)
{
line_spacing_ = line_spacing;
}
void format_node::set_text_opacity(optional<double> text_opacity)
{
text_opacity_ = text_opacity;
}
void format_node::set_wrap_before(optional<bool> wrap_before)
{
wrap_before_ = wrap_before;
}
void format_node::set_wrap_char(optional<unsigned> wrap_char)
{
wrap_char_ = wrap_char;
}
void format_node::set_text_transform(optional<text_transform_e> text_transform)
{
text_transform_ = text_transform;
}
void format_node::set_fill(optional<color> c)
{
fill_ = c;
}
void format_node::set_halo_fill(optional<color> c)
{
halo_fill_ = c;
}
void format_node::set_halo_radius(optional<double> 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<freetype_engine> & 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 */

View file

@ -26,6 +26,7 @@
#include <mapnik/text_symbolizer.hpp>
// boost
#include <boost/scoped_ptr.hpp>
#include <mapnik/text_processing.hpp>
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

View file

@ -14,4 +14,4 @@
<PointSymbolizer file="../data/images/also.png"/>
</Rule>
</Style>
</Map>
</Map>

View file

@ -1,8 +0,0 @@
<Map minimum-version="0.7.2">
<Style name="arrows">
<Rule>
<GlyphSymbolizer size="10" char="'&#237;'" value="[value]" angle="[azimuth]+90" face-name="DejaVu Sans Condensed" allow-overlap="1" avoid-edges="0" halo-fill="rgba(0%,0%,0%,.1)" halo-radius="1" angle-mode="azimuth">
</GlyphSymbolizer>
</Rule>
</Style>
</Map>

View file

@ -2,7 +2,7 @@
<Map background-color="#b5d0d0" srs="+init=epsg:4326" minimum-version="0.7.2">
<Style name="1">
<Rule>
<ShieldSymbolizer file="../images/dummy.png" size="10" transform="scale(5) translate(15, 15) rotate(20) skewX(20) skewY(5)" dy="-5" dx="-5" opacity=".5" text-opacity=".3" face-name="DejaVu Sans Book" halo-radius="1" shield-dx="10" shield-dy="10" no-text="false" allow-overlap="true" avoid-edges="false">[label]</ShieldSymbolizer>
<ShieldSymbolizer file="../images/dummy.png" size="10" transform="scale(5) translate(15, 15) rotate(20) skewX(20) skewY(5)" dy="-5" dx="-5" opacity=".5" text-opacity=".3" face-name="DejaVu Sans Book" halo-radius="1" shield-dx="10" shield-dy="10" allow-overlap="true" avoid-edges="false">[label]</ShieldSymbolizer>
</Rule>
<Rule>
<PointSymbolizer allow-overlap="true"/>
@ -13,7 +13,7 @@
<LineSymbolizer/>
</Rule>
<Rule>
<ShieldSymbolizer file="../svg/ellipses.svg" size="10" opacity=".5" text-opacity=".3" spacing="50" placement="line" allow-overlap="true" face-name="DejaVu Sans Book" no-text="false" line-spacing="10">[label]</ShieldSymbolizer>
<ShieldSymbolizer file="../svg/ellipses.svg" size="10" opacity=".5" text-opacity=".3" spacing="50" placement="line" allow-overlap="true" face-name="DejaVu Sans Book" line-spacing="10">[label]</ShieldSymbolizer>
</Rule>
</Style>
@ -35,4 +35,4 @@
</Datasource>
</Layer>
</Map>
</Map>

View file

@ -33,4 +33,4 @@
</Datasource>
</Layer>
</Map>
</Map>

View file

@ -22,4 +22,4 @@
<Parameter name="type">shape</Parameter>
</Datasource>
</Layer>
</Map>
</Map>

View file

@ -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

View file

@ -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]

View file

@ -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'))

View file

@ -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)

View file

@ -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)

5
tests/visual_tests/clean.sh Executable file
View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map>
<Map background-color="white" srs="+proj=latlong +datum=WGS84">
<Layer name="layer" srs="+proj=latlong +datum=WGS84">
<StyleName>My Style</StyleName>
<Datasource>
<Parameter name="type">shape</Parameter>
<Parameter name="file">points.shp</Parameter>
</Datasource>
</Layer>
<Style name="My Style">
<Rule>
<PointSymbolizer/>
<!-- Basic test -->
<TextSymbolizer face-name="DejaVu Sans Book" size="16" placement="point" dx="0" dy="5">
[name]+'&#10;'<Format face-name="DejaVu Sans Oblique" size="9">'('+[name]+')'</Format></TextSymbolizer>
</Rule>
</Style>
</Map>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map>
<Map background-color="white" srs="+proj=latlong +datum=WGS84">
<Layer name="layer" srs="+proj=latlong +datum=WGS84">
<StyleName>My Style</StyleName>
<Datasource>
<Parameter name="type">shape</Parameter>
<Parameter name="file">points.shp</Parameter>
</Datasource>
</Layer>
<Style name="My Style">
<Rule>
<PointSymbolizer/>
<!-- Nested formats -->
<TextSymbolizer face-name="DejaVu Sans Book" size="16" placement="point" dx="0" dy="5">
[name]+'&#10;'<Format face-name="DejaVu Sans Oblique"><Format size="9">'('+[name]+')'</Format></Format></TextSymbolizer>
</Rule>
</Style>
</Map>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map>
<Map background-color="white" srs="+proj=latlong +datum=WGS84">
<Layer name="layer" srs="+proj=latlong +datum=WGS84">
<StyleName>My Style</StyleName>
<Datasource>
<Parameter name="type">shape</Parameter>
<Parameter name="file">points.shp</Parameter>
</Datasource>
</Layer>
<Style name="My Style">
<Rule>
<PointSymbolizer/>
<!-- Empty formats-->
<TextSymbolizer face-name="DejaVu Sans Book" size="16" placement="point" dx="0" dy="5">
[name]+'&#10;'<Format face-name="DejaVu Sans Oblique" size="9"></Format></TextSymbolizer>
</Rule>
</Style>
</Map>

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map>
<Map background-color="white" srs="+proj=latlong +datum=WGS84">
<Layer name="layer" srs="+proj=latlong +datum=WGS84">
<StyleName>My Style</StyleName>
<Datasource>
<Parameter name="type">shape</Parameter>
<Parameter name="file">points.shp</Parameter>
</Datasource>
</Layer>
<Style name="My Style">
<Rule>
<PointSymbolizer/>
<!-- Empty text -->
<TextSymbolizer face-name="DejaVu Sans Book" size="16" placement="point" dx="0" dy="5" />
</Rule>
</Style>
</Map>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map>
<Map background-color="white" srs="+proj=latlong +datum=WGS84">
<Layer name="layer" srs="+proj=latlong +datum=WGS84">
<StyleName>My Style</StyleName>
<Datasource>
<!--
<Parameter name="type">osm</Parameter>
<Parameter name="file">points.osm</Parameter>-->
<Parameter name="type">shape</Parameter>
<Parameter name="file">points.shp</Parameter>
</Datasource>
</Layer>
<Style name="My Style">
<Rule>
<PointSymbolizer/>
<TextSymbolizer face-name="DejaVu Sans Book" size="16" placement="point" dy="8" fill="blue" placement-type="list">[name]
<Placement size="10" dy="-8" fill="red"/>
<Placement fill="green">'S'+[nr]</Placement>
<Placement fill="orange" dy="8">[nr]</Placement>
</TextSymbolizer>
</Rule>
</Style>
</Map>

Some files were not shown because too many files have changed in this diff Show more