Merge remote-tracking branch 'herm/textplacement-merge'
|
@ -649,7 +649,6 @@ __all__ = [
|
|||
'FontEngine',
|
||||
'FontSet',
|
||||
'Geometry2d',
|
||||
'GlyphSymbolizer',
|
||||
'Image',
|
||||
'ImageView',
|
||||
'Grid',
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
;
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>())
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
|
|
115
bindings/python/mapnik_text_placement.cpp
Normal 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.*/
|
||||
;
|
||||
}
|
|
@ -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
|
@ -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
After Width: | Height: | Size: 115 KiB |
226
docs/textrendering.svg
Normal 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->text_symbolizer_helper -->
|
||||
<g id="edge26" class="edge"><title>Renderer->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->text_placement_info -->
|
||||
<g id="edge8" class="edge"><title>text_placements->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->text_symbolizer_properties -->
|
||||
<g id="edge6" class="edge"><title>text_placements->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->Renderer -->
|
||||
<g id="edge36" class="edge"><title>text_placement_info->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->text_placement_info -->
|
||||
<g id="edge14" class="edge"><title>text_placement_info->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->text_symbolizer_properties -->
|
||||
<g id="edge10" class="edge"><title>text_placement_info->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->text_path -->
|
||||
<g id="edge12" class="edge"><title>text_placement_info->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_->text_processor -->
|
||||
<g id="edge2" class="edge"><title>node_->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_->text_node -->
|
||||
<g id="edge38" class="edge"><title>node_->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_->list_node -->
|
||||
<g id="edge40" class="edge"><title>node_->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_->format_node -->
|
||||
<g id="edge42" class="edge"><title>node_->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->Renderer -->
|
||||
<g id="edge34" class="edge"><title>text_processor->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->processed_text -->
|
||||
<g id="edge18" class="edge"><title>text_processor->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->text_placements -->
|
||||
<g id="edge4" class="edge"><title>TextSymbolizer->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->text_processor -->
|
||||
<g id="edge16" class="edge"><title>text_symbolizer_properties->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->Renderer -->
|
||||
<g id="edge22" class="edge"><title>text_path->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->Renderer -->
|
||||
<g id="edge24" class="edge"><title>processed_text->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->string_info -->
|
||||
<g id="edge20" class="edge"><title>processed_text->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->placement_finder -->
|
||||
<g id="edge32" class="edge"><title>string_info->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->placement_finder -->
|
||||
<g id="edge28" class="edge"><title>text_symbolizer_helper->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->text_path -->
|
||||
<g id="edge30" class="edge"><title>placement_finder->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->text_node -->
|
||||
<g id="edge44" class="edge"><title>list_node->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->format_node -->
|
||||
<g id="edge46" class="edge"><title>list_node->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->text_node -->
|
||||
<g id="edge48" class="edge"><title>format_node->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 |
|
@ -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*/)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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*/)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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>
|
||||
|
|
|
@ -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*/)
|
||||
|
|
|
@ -27,8 +27,11 @@
|
|||
|
||||
#include <mapnik/global.hpp>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <stdio.h>
|
||||
#include <jpeglib.h>
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mapnik {
|
||||
|
||||
|
|
|
@ -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 π
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
169
include/mapnik/symbolizer_helpers.hpp
Normal 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
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
64
include/mapnik/text_placements_list.hpp
Normal 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
|
|
@ -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_;
|
||||
|
|
179
include/mapnik/text_processing.hpp
Normal 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
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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&);
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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*);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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&,
|
||||
|
|
|
@ -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&,
|
||||
|
|
|
@ -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(
|
||||
|
|
693
src/load_map.cpp
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
270
src/save_map.cpp
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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_;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -14,4 +14,4 @@
|
|||
<PointSymbolizer file="../data/images/also.png"/>
|
||||
</Rule>
|
||||
</Style>
|
||||
</Map>
|
||||
</Map>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<Map minimum-version="0.7.2">
|
||||
<Style name="arrows">
|
||||
<Rule>
|
||||
<GlyphSymbolizer size="10" char="'í'" 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>
|
|
@ -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>
|
||||
|
|
|
@ -33,4 +33,4 @@
|
|||
</Datasource>
|
||||
</Layer>
|
||||
|
||||
</Map>
|
||||
</Map>
|
||||
|
|
|
@ -22,4 +22,4 @@
|
|||
<Parameter name="type">shape</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
</Map>
|
||||
</Map>
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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]
|
||||
|
|
@ -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'))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
BIN
tests/visual_tests/formating-1-500-reference.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
22
tests/visual_tests/formating-1.xml
Normal 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]+' '<Format face-name="DejaVu Sans Oblique" size="9">'('+[name]+')'</Format></TextSymbolizer>
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
</Map>
|
BIN
tests/visual_tests/formating-2-500-reference.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
22
tests/visual_tests/formating-2.xml
Normal 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]+' '<Format face-name="DejaVu Sans Oblique"><Format size="9">'('+[name]+')'</Format></Format></TextSymbolizer>
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
</Map>
|
BIN
tests/visual_tests/formating-3-500-reference.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
22
tests/visual_tests/formating-3.xml
Normal 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]+' '<Format face-name="DejaVu Sans Oblique" size="9"></Format></TextSymbolizer>
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
</Map>
|
BIN
tests/visual_tests/formating-4-500-reference.png
Normal file
After Width: | Height: | Size: 533 B |
21
tests/visual_tests/formating-4.xml
Normal 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>
|
BIN
tests/visual_tests/list-100-reference.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
tests/visual_tests/list-150-reference.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
tests/visual_tests/list-200-reference.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
tests/visual_tests/list-250-reference.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
tests/visual_tests/list-300-reference.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
tests/visual_tests/list-400-reference.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
tests/visual_tests/list-600-reference.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
tests/visual_tests/list-800-reference.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
27
tests/visual_tests/list.xml
Normal 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>
|