Merge branch 'textplacement-merge' into feature-text-merge

Conflicts:
	docs/textrendering.gv
	include/mapnik/building_symbolizer.hpp
	include/mapnik/placement_finder.hpp
	include/mapnik/symbolizer_helpers.hpp
	include/mapnik/text_placements.hpp
	include/mapnik/text_placements_list.hpp
	include/mapnik/text_placements_simple.hpp
	include/mapnik/text_processing.hpp
	plugins/input/shape/shape_utils.hpp
	src/agg/process_shield_symbolizer.cpp
	src/agg/process_text_symbolizer.cpp
	src/cairo_renderer.cpp
	src/grid/process_shield_symbolizer.cpp
	src/grid/process_text_symbolizer.cpp
	src/load_map.cpp
	src/placement_finder.cpp
	src/shield_symbolizer.cpp
	src/text_placements.cpp
	src/text_processing.cpp
	src/text_symbolizer.cpp
	tests/visual_tests/clean.sh
	tests/visual_tests/test.py

Merge herm/textplacement-merge
This commit is contained in:
Artem Pavlenko 2012-01-31 11:09:55 +00:00
commit b90de4874c
45 changed files with 1605 additions and 1296 deletions

View file

@ -60,6 +60,7 @@ 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();
@ -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();

View file

@ -37,6 +37,7 @@ using mapnik::path_expression_ptr;
using mapnik::guess_type;
using mapnik::expression_ptr;
using mapnik::parse_path;
using mapnik::position;
namespace {
@ -44,8 +45,8 @@ using namespace boost::python;
tuple get_shield_displacement(const shield_symbolizer& s)
{
boost::tuple<double,double> pos = s.get_shield_displacement();
return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos));
position const& pos = s.get_shield_displacement();
return boost::python::make_tuple(pos.first, pos.second);
}
void set_shield_displacement(shield_symbolizer & s, boost::python::tuple arg)
@ -55,8 +56,8 @@ void set_shield_displacement(shield_symbolizer & s, boost::python::tuple arg)
tuple get_text_displacement(const shield_symbolizer& t)
{
boost::tuple<double,double> pos = t.get_displacement();
return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos));
position const& pos = t.get_displacement();
return boost::python::make_tuple(pos.first, pos.second);
}
void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg)
@ -223,9 +224,6 @@ void export_shield_symbolizer()
.add_property("wrap_before",
&shield_symbolizer::get_wrap_before,
&shield_symbolizer::set_wrap_before)
.add_property("no_text",
&shield_symbolizer::get_no_text,
&shield_symbolizer::set_no_text)
.add_property("unlock_image",
&shield_symbolizer::get_unlock_image,
&shield_symbolizer::set_unlock_image)

View file

@ -0,0 +1,115 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
#include <boost/python.hpp>
#include <mapnik/text_placements.hpp>
#include "mapnik_enumeration.hpp"
#include <mapnik/expression_string.hpp>
using namespace mapnik;
//using mapnik::color;
//using mapnik::text_symbolizer;
//using mapnik::expr_node;
//using mapnik::expression_ptr;
//using mapnik::to_expression_string;
namespace {
using namespace boost::python;
tuple get_displacement(text_symbolizer_properties const& t)
{
return boost::python::make_tuple(t.displacement.first, t.displacement.second);
}
void set_displacement(text_symbolizer_properties &t, boost::python::tuple arg)
{
double x = extract<double>(arg[0]);
double y = extract<double>(arg[0]);
t.displacement = std::make_pair(x, y);
}
}
void export_text_placement()
{
using namespace boost::python;
enumeration_<label_placement_e>("label_placement")
.value("LINE_PLACEMENT",LINE_PLACEMENT)
.value("POINT_PLACEMENT",POINT_PLACEMENT)
.value("VERTEX_PLACEMENT",VERTEX_PLACEMENT)
.value("INTERIOR_PLACEMENT",INTERIOR_PLACEMENT)
;
enumeration_<vertical_alignment_e>("vertical_alignment")
.value("TOP",V_TOP)
.value("MIDDLE",V_MIDDLE)
.value("BOTTOM",V_BOTTOM)
.value("AUTO",V_AUTO)
;
enumeration_<horizontal_alignment_e>("horizontal_alignment")
.value("LEFT",H_LEFT)
.value("MIDDLE",H_MIDDLE)
.value("RIGHT",H_RIGHT)
.value("AUTO",H_AUTO)
;
enumeration_<justify_alignment_e>("justify_alignment")
.value("LEFT",J_LEFT)
.value("MIDDLE",J_MIDDLE)
.value("RIGHT",J_RIGHT)
;
enumeration_<text_transform_e>("text_transform")
.value("NONE",NONE)
.value("UPPERCASE",UPPERCASE)
.value("LOWERCASE",LOWERCASE)
.value("CAPITALIZE",CAPITALIZE)
;
class_<text_symbolizer_properties>("TextSymbolizerProperties")
.def_readwrite("orientation", &text_symbolizer_properties::orientation)
.add_property("displacement",
&get_displacement,
&set_displacement)
.def_readwrite("label_placement", &text_symbolizer_properties::label_placement)
.def_readwrite("horizontal_alignment", &text_symbolizer_properties::halign)
.def_readwrite("justify_alignment", &text_symbolizer_properties::jalign)
.def_readwrite("vertical_alignment", &text_symbolizer_properties::valign)
.def_readwrite("label_spacing", &text_symbolizer_properties::label_spacing)
.def_readwrite("label_position_tolerance", &text_symbolizer_properties::label_position_tolerance)
.def_readwrite("avoid_edges", &text_symbolizer_properties::avoid_edges)
.def_readwrite("minimum_distance", &text_symbolizer_properties::minimum_distance)
.def_readwrite("minimum_padding", &text_symbolizer_properties::minimum_padding)
.def_readwrite("minimum_path_length", &text_symbolizer_properties::minimum_path_length)
.def_readwrite("maximum_angle_char_delta", &text_symbolizer_properties::max_char_angle_delta)
.def_readwrite("force_odd_labels", &text_symbolizer_properties::force_odd_labels)
.def_readwrite("allow_overlap", &text_symbolizer_properties::allow_overlap)
.def_readwrite("text_ratio", &text_symbolizer_properties::text_ratio)
.def_readwrite("wrap_width", &text_symbolizer_properties::wrap_width)
/* TODO: text_processor */
/* from_xml, to_xml operate on mapnik's internal XML tree and don't make sense in python.*/
;
}

View file

@ -39,8 +39,8 @@ using namespace boost::python;
tuple get_text_displacement(const text_symbolizer& t)
{
position pos = t.get_displacement();
return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos));
mapnik::position const& pos = t.get_displacement();
return boost::python::make_tuple(pos.first, pos.second);
}
void set_text_displacement(text_symbolizer & t, boost::python::tuple arg)

View file

@ -1,13 +1,14 @@
#process with: dot textrendering.gv -Tpng > textrendering.png
/* 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]
# Red: function is called
/* 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]
Renderer
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]
@ -16,9 +17,24 @@ digraph textrendering {
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()"]
processed_text -> string_info [label="get_string_info()", style=dashed]
text_path -> Renderer [color=red, label="used by"]
Renderer -> text_placement_info [color=red, label="init()"]
Renderer -> processed_text [color=red, label="initializes"]
processed_text -> Renderer [color=red, label="owned by"]
Renderer -> text_symbolizer_helper [color=red, label="creates"]
text_symbolizer_helper -> placement_finder [color=red, label="creates"]
placement_finder -> text_path [color=red, label="creates"]
string_info -> placement_finder [color=red, label="used by"]
text_processor -> Renderer [color=red, label="called by"]
text_placement_info -> Renderer [color=red, label="used by"]
node_[label="node"]
node_ -> text_node [style=dashed]
node_ -> list_node [style=dashed]
node_ -> format_node [style=dashed]
list_node -> text_node [style=dashed]
list_node -> format_node [style=dashed]
format_node -> text_node [style=dashed]
{ rank=same; text_path text_symbolizer_helper }
{ rank=same; node_ TextSymbolizer}
}

BIN
docs/textrendering.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

226
docs/textrendering.svg Normal file
View file

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

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -36,20 +36,17 @@ public:
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
/** 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(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(T & path);
//Find placement, automatically select point or line placement
void find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans);
void update_detector();
void clear();
@ -65,14 +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(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(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
@ -87,7 +84,7 @@ private:
void find_line_breaks();
void init_string_size();
void init_alignment();
void adjust_position(placement_element *current_placement, double label_x, double label_y);
void adjust_position(text_path *current_placement, double label_x, double label_y);
///General Internals
DetectorT & detector_;

View file

@ -50,15 +50,12 @@ struct MAPNIK_DECL shield_symbolizer : public text_symbolizer,
bool get_unlock_image() const; // image is not locked to the text placement
void set_unlock_image(bool unlock_image);
bool get_no_text() const; // do no render text
void set_no_text(bool unlock_image);
void set_shield_displacement(double shield_dx,double shield_dy);
boost::tuple<double,double> const& get_shield_displacement() const;
position const& get_shield_displacement() const;
private:
bool unlock_image_;
bool no_text_;
boost::tuple<double,double> shield_displacement_;
position shield_displacement_;
};
}

View file

@ -23,163 +23,147 @@
#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>
#include <boost/foreach.hpp>
namespace mapnik {
struct greater_bbox_comp
{
bool operator() (geometry_type const* g0, geometry_type const* g1) const
{
box2d<double> b0 = g0->envelope();
box2d<double> b1 = g1->envelope();
return b0.width()*b0.height() > b1.width()*b1.height();
}
};
/** 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(unsigned width,
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) :
width_(width),
height_(height),
scale_factor_(scale_factor),
t_(t),
font_manager_(font_manager),
detector_(detector),
text_()
{
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();
text_placement_info_ptr get_placement(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
private:
bool initialize_geometries(std::vector<geometry_type*> & geometries_to_process,
text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans);
unsigned width_;
unsigned height_;
double scale_factor_;
//Input
text_symbolizer const& sym_;
Feature const& feature_;
proj_transform const& prj_trans_;
CoordTransform const &t_;
FaceManagerT &font_manager_;
DetectorT &detector_;
boost::shared_ptr<processed_text> text_; /*TODO: Use shared pointers for text placement so we don't need to keep a reference here! */
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>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_placement(
text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
class shield_symbolizer_helper: public text_symbolizer_helper<FaceManagerT, DetectorT>
{
unsigned num_geom = feature.num_geometries();
if (!num_geom) return text_placement_info_ptr(); //Nothing to do
std::vector<geometry_type*> geometries_to_process;
if (!initialize_geometries(geometries_to_process,sym, feature, prj_trans))
return text_placement_info_ptr();
text_ = boost::shared_ptr<processed_text>(new processed_text(font_manager_, scale_factor_));
metawriter_with_properties writer = sym.get_metawriter();
box2d<double> dims(0, 0, width_, height_);
text_placement_info_ptr placement = sym.get_placement_options()->get_placement_info();
placement->init(scale_factor_, width_, height_);
if (writer.first)
placement->collect_extents = true;
while (placement->next())
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)
{
text_processor &processor = placement->properties.processor;
text_symbolizer_properties const& p = placement->properties;
/* TODO: Simplify this. */
text_->clear();
processor.process(*text_, feature);
string_info &info = text_->get_string_info();
/* END TODO */
// text rotation
double angle = 0.0;
if (p.orientation)
{
angle = boost::apply_visitor(evaluate<Feature,value_type>(feature),*(p.orientation)).to_double();
}
placement_finder<DetectorT> finder(*placement, info, detector_, dims);
BOOST_FOREACH( geometry_type * geom, geometries_to_process )
{
finder.find_placement(angle, *geom, t_, prj_trans);
//if (!placement->placements.size())
// continue;
//if (writer.first) writer.first->add_text(*placement, font_manager_, feature, t_, writer.second);
//return placement;
}
return placement;
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
bool text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries(
std::vector<geometry_type*> & geometries_to_process,
text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
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));
init_marker();
}
std::sort(geometries_to_process.begin(), geometries_to_process.end(), greater_bbox_comp());
if (!geometries_to_process.size() > 0)
{
// early return to avoid significant overhead of rendering setup
return false;
}
return true;
}
}
text_placement_info_ptr get_placement();
std::pair<int, int> get_marker_position(text_path &p);
marker &get_marker() const;
agg::trans_affine const& get_transform() const;
protected:
text_placement_info_ptr get_point_placement();
text_placement_info_ptr get_line_placement();
void init_marker();
shield_symbolizer const& sym_;
box2d<double> marker_ext_;
boost::optional<marker_ptr> marker_;
agg::trans_affine transform_;
int marker_w_;
int marker_h_;
int marker_x_;
int marker_y_;
// F***ing templates...
// http://womble.decadent.org.uk/c++/template-faq.html#base-lookup
using text_symbolizer_helper<FaceManagerT, DetectorT>::geometries_to_process_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement;
using text_symbolizer_helper<FaceManagerT, DetectorT>::info_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::geo_itr_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::point_itr_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::points_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::writer_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::font_manager_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::feature_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::t_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::detector_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::dims_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::prj_trans_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::placement_valid_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::point_placement_;
using text_symbolizer_helper<FaceManagerT, DetectorT>::angle_;
};
} //namespace
#endif // SYMBOLIZER_HELPERS_HPP

View file

@ -46,9 +46,8 @@ namespace mapnik {
class text_placements;
typedef text_path placement_element;
typedef boost::tuple<double,double> position;
typedef std::pair<double,double> position;
typedef std::pair<double,double> dimension_type;
enum label_placement_enum {
POINT_PLACEMENT,
@ -97,7 +96,7 @@ struct text_symbolizer_properties
{
text_symbolizer_properties();
/** Load all values and also the ```processor``` object from XML ptree. */
void set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets);
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;
@ -136,7 +135,8 @@ 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);
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!
@ -160,7 +160,7 @@ public:
/* TODO: Don't know what this is used for. */
bool has_dimensions;
/* TODO: Don't know what this is used for. */
std::pair<double, double> dimensions;
dimension_type dimensions;
/** Set scale factor. */
void set_scale_factor(double factor) { scale_factor = factor; }
/** Get scale factor. */
@ -177,10 +177,16 @@ public:
//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;
/* TODO */
boost::ptr_vector<placement_element> placements;
/** Used to return all placements found. */
boost::ptr_vector<text_path> placements;
};
typedef boost::shared_ptr<text_placement_info> text_placement_info_ptr;
@ -206,7 +212,9 @@ public:
* return text_placement_info_ptr(new text_placement_info_XXX(this));
* }
*/
virtual text_placement_info_ptr get_placement_info() const =0;
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.
*/
@ -229,7 +237,8 @@ class text_placements_info_dummy;
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;
};
@ -237,8 +246,10 @@ public:
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), 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();
private:
unsigned state;

View file

@ -33,7 +33,8 @@ class text_placements_list: public text_placements
{
public:
text_placements_list();
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;
virtual std::set<expression_ptr> get_all_expressions();
text_symbolizer_properties & add();
text_symbolizer_properties & get(unsigned i);
@ -48,8 +49,10 @@ private:
class text_placement_info_list : public text_placement_info
{
public:
text_placement_info_list(text_placements_list const* parent) :
text_placement_info(parent), state(0), parent_(parent) {}
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;

View file

@ -49,9 +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 const& get_positions() const;
std::string get_positions();
private:
std::string positions_;
std::vector<directions_t> direction_;
@ -64,8 +65,12 @@ 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();

View file

@ -46,14 +46,14 @@ enum text_transform
CAPITALIZE,
text_transform_MAX
};
DEFINE_ENUM( text_transform_e, text_transform );
struct char_properties
{
char_properties();
/** Construct object from XML. */
void set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets);
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;
@ -79,7 +79,8 @@ public:
UnicodeString str;
};
class processed_text
class processed_text : boost::noncopyable
{
public:
processed_text(face_manager<freetype_engine> & font_manager, double scale_factor);
@ -88,8 +89,8 @@ public:
unsigned empty() const { return expr_list_.empty(); }
void clear();
typedef std::list<processed_expression> expression_list;
expression_list::const_iterator begin();
expression_list::const_iterator end();
expression_list::const_iterator begin() const;
expression_list::const_iterator end() const;
string_info &get_string_info();
private:
expression_list expr_list_;
@ -98,7 +99,86 @@ private:
string_info info_;
};
class abstract_token;
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
/** Stores formating information and uses this to produce formated text for a given feature. */
class text_processor
@ -113,22 +193,21 @@ public:
/** 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);
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);
/** Add a new formating token. */
void push_back(abstract_token *token);
/** Sets new format tree. */
void set_format_tree(formating::node_ptr tree);
/** Get format tree. */
formating::node_ptr get_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;
/** Default values for char_properties. */
char_properties defaults;
protected:
void from_xml_recursive(boost::property_tree::ptree const& pt, std::map<std::string,font_set> const &fontsets);
private:
std::list<abstract_token *> list_;
bool clear_on_write; //Clear list once
formating::node_ptr tree_;
};
} /* namespace */
} /* namespace mapnik*/
#endif

View file

@ -23,17 +23,13 @@
#include <mapnik/agg_renderer.hpp>
#include <mapnik/agg_rasterizer.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
#include <mapnik/expression_evaluator.hpp>
#include "agg_basics.h"
#include "agg_rendering_buffer.h"
#include "agg_scanline_u.h"
#include <mapnik/symbolizer_helpers.hpp>
// boost
#include <boost/make_shared.hpp>
@ -45,227 +41,28 @@ void agg_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
#if 0
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, text, 0);
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);
}
}
#endif
}

View file

@ -33,23 +33,24 @@ void agg_renderer<T>::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
/* This could also be a member of the renderer class, but I would have
to check if any of the variables changes and notify the helper.
It could be done at a later point, but for now keep the code simple.
*/
text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4> helper(width_, height_, scale_factor_, t_, font_manager_, *detector_);
text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans);
if (!placement) return;
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()));
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
double x = placement->placements[ii].starting_x;
double y = placement->placements[ii].starting_y;
ren.prepare_glyphs(&(placement->placements[ii]));
ren.render(x, y);
text_placement_info_ptr placement;
while ((placement = helper.get_placement())) {
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
double x = placement->placements[ii].starting_x;
double y = placement->placements[ii].starting_y;
ren.prepare_glyphs(&(placement->placements[ii]));
ren.render(x, y);
}
}
}

View file

@ -145,6 +145,7 @@ source = Split(
memory_datasource.cpp
stroke.cpp
symbolizer.cpp
symbolizer_helpers.cpp
arrow.cpp
unicode.cpp
markers_symbolizer.cpp

View file

@ -1051,207 +1051,26 @@ void cairo_renderer_base::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
#if 0
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();
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, text, 0);
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_);
}
}
#endif
}
void cairo_renderer_base::process(line_pattern_symbolizer const& sym,
@ -1435,17 +1254,15 @@ void cairo_renderer_base::process(text_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4> helper(detector_.extent().width(), detector_.extent().height(), 1.0 /*scale_factor*/, t_, font_manager_, detector_);
text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans);
if (!placement) return;
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_);
cairo_context context(context_);
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
context.add_text(placement->placements[ii], face_manager_, font_manager_);
text_placement_info_ptr placement;
while ((placement = helper.get_placement())) {
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
context.add_text(placement->placements[ii], face_manager_, font_manager_);
}
}
}

View file

@ -28,8 +28,8 @@
#include <mapnik/grid/grid_pixel.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/symbolizer_helpers.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/svg/svg_renderer.hpp>
#include <mapnik/svg/svg_path_adapter.hpp>
@ -44,201 +44,36 @@ void grid_renderer<T>::process(shield_symbolizer const& sym,
Feature const& feature,
proj_transform const& prj_trans)
{
#if 0
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, text, 0);
// 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);
#endif
}
template void grid_renderer<grid>::process(shield_symbolizer const&,

View file

@ -23,8 +23,6 @@
// 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 {
@ -36,23 +34,26 @@ void grid_renderer<T>::process(text_symbolizer const& sym,
{
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_);
text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans);
if (!placement) return;
bool placement_found = false;
text_renderer<T> ren(pixmap_, font_manager_, *(font_manager_.get_stroker()));
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
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);
text_placement_info_ptr placement;
while ((placement = helper.get_placement())) {
placement_found = true;
for (unsigned int ii = 0; ii < placement->placements.size(); ++ii)
{
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);
}
}
pixmap_.add_feature(feature);
if (placement_found) pixmap_.add_feature(feature);
}

View file

@ -1281,8 +1281,7 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
placement_finder = text_placements_ptr(new text_placements_dummy());
}
text_symbolizer text_symbol = text_symbolizer(placement_finder);
placement_finder->properties.set_values_from_xml(sym, fontsets_);
placement_finder->properties.from_xml(sym, fontsets_);
if (strict_) ensure_font_face(placement_finder->properties.processor.defaults.face_name);
if (list) {
ptree::const_iterator symIter = sym.begin();
@ -1296,10 +1295,12 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym )
}
ensure_attrs(symIter->second, "TextSymbolizer/Placement", s_common.str());
text_symbolizer_properties & p = list->add();
p.set_values_from_xml(symIter->second, fontsets_);
p.from_xml(symIter->second, fontsets_);
if (strict_) ensure_font_face(p.processor.defaults.face_name);
}
}
text_symbolizer text_symbol = text_symbolizer(placement_finder);
parse_metawriter_in_symbolizer(text_symbol, sym);
rule.append(text_symbol);
}
@ -1312,26 +1313,64 @@ 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;
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";
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.str());
ensure_attrs(sym, "ShieldSymbolizer", s_symbolizer);
try
{
shield_symbolizer shield_symbol = shield_symbolizer();
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());
}
placement_finder->properties.from_xml(sym, fontsets_);
if (strict_) ensure_font_face(placement_finder->properties.processor.defaults.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.processor.defaults.face_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)
{
@ -1378,14 +1417,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
shield_symbol.set_unlock_image( * unlock_image );
}
// no text
optional<boolean> no_text =
get_opt_attr<boolean>(sym, "no-text");
if (no_text)
{
shield_symbol.set_no_text( * no_text );
}
parse_metawriter_in_symbolizer(shield_symbol, sym);
std::string image_file = get_attr<std::string>(sym, "file");
@ -1418,8 +1449,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym )
std::clog << "### WARNING: " << msg << endl;
}
}
text_placements_ptr placement_finder = shield_symbol.get_placement_options();
placement_finder->properties.set_values_from_xml(sym, fontsets_);
rule.append(shield_symbol);
}
catch (const config_error & ex)

View file

@ -193,7 +193,7 @@ void metawriter_json_stream::add_text(text_placement_info const& p,
*/
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; /* Part of text is inside rendering region */
bool straight = true;

View file

@ -103,6 +103,7 @@ placement_finder<DetectorT>::placement_finder(text_placement_info &placement_inf
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>
@ -111,6 +112,7 @@ placement_finder<DetectorT>::placement_finder(text_placement_info &placement_inf
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>
@ -289,9 +291,9 @@ void placement_finder<DetectorT>::init_alignment()
{
valign_ = p.valign;
if (valign_ == V_AUTO) {
if (p.displacement.get<1>() > 0.0)
if (p.displacement.second > 0.0)
valign_ = V_BOTTOM;
else if (p.displacement.get<1>() < 0.0)
else if (p.displacement.second < 0.0)
valign_ = V_TOP;
else
valign_ = V_MIDDLE;
@ -299,9 +301,9 @@ void placement_finder<DetectorT>::init_alignment()
halign_ = p.halign;
if (halign_ == H_AUTO) {
if (p.displacement.get<0>() > 0.0)
if (p.displacement.first > 0.0)
halign_ = H_RIGHT;
else if (p.displacement.get<0>() < 0.0)
else if (p.displacement.first < 0.0)
halign_ = H_LEFT;
else
halign_ = H_MIDDLE;
@ -310,7 +312,7 @@ void placement_finder<DetectorT>::init_alignment()
template <typename DetectorT>
void placement_finder<DetectorT>::adjust_position(placement_element *current_placement, double label_x, double label_y)
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
@ -331,8 +333,8 @@ void placement_finder<DetectorT>::adjust_position(placement_element *current_pla
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 += pi.get_scale_factor() * boost::tuples::get<0>(p.displacement);
current_placement->starting_y += pi.get_scale_factor() * boost::tuples::get<1>(p.displacement);
current_placement->starting_x += pi.get_scale_factor() * p.displacement.first;
current_placement->starting_y += pi.get_scale_factor() * p.displacement.second;
}
@ -348,7 +350,7 @@ void placement_finder<DetectorT>::find_point_placement(double label_x, double la
double sina = std::sin(rad);
double x, y;
std::auto_ptr<placement_element> current_placement(new placement_element);
std::auto_ptr<text_path> current_placement(new text_path);
adjust_position(current_placement.get(), label_x, label_y);
@ -456,12 +458,10 @@ void placement_finder<DetectorT>::find_point_placement(double label_x, double la
x += cwidth; // move position to next character
}
#if 0
//TODO
// 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,
@ -474,7 +474,6 @@ void placement_finder<DetectorT>::find_point_placement(double label_x, double la
c_envelopes.push(pt);
}
}
#endif
// since there was no early exit, add the character envelopes to the placements' envelopes
while( !c_envelopes.empty() )
@ -535,7 +534,7 @@ void placement_finder<DetectorT>::find_line_placements(PathT & shape_path)
double distance = 0.0;
double displacement = boost::tuples::get<1>(p.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
@ -595,7 +594,7 @@ void placement_finder<DetectorT>::find_line_placements(PathT & shape_path)
{
//Record details for the start of the string placement
int orientation = 0;
std::auto_ptr<placement_element> current_placement = get_placement_offset(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)
@ -657,7 +656,7 @@ void placement_finder<DetectorT>::find_line_placements(PathT & shape_path)
}
template <typename DetectorT>
std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offset(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)
@ -666,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])
@ -675,13 +674,13 @@ 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 old_x = path_positions[index-1].x;
double old_y = path_positions[index-1].y;
@ -695,7 +694,7 @@ 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;
@ -719,7 +718,7 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
//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;
@ -747,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;
@ -784,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;
@ -833,7 +832,7 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
{
//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);
}
}
@ -841,7 +840,7 @@ std::auto_ptr<placement_element> placement_finder<DetectorT>::get_placement_offs
}
template <typename DetectorT>
bool placement_finder<DetectorT>::test_placement(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)
{
//Create and test envelopes
bool status = true;
@ -996,53 +995,6 @@ void placement_finder<DetectorT>::clear()
detector_.clear();
}
template <typename DetectorT>
void placement_finder<DetectorT>::find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans)
{
double label_x=0.0;
double label_y=0.0;
double z=0.0;
if (p.label_placement == POINT_PLACEMENT ||
p.label_placement == VERTEX_PLACEMENT ||
p.label_placement == INTERIOR_PLACEMENT)
{
unsigned iterations = 1;
if (p.label_placement == VERTEX_PLACEMENT)
{
iterations = geom.num_points();
geom.rewind(0);
}
for(unsigned jj = 0; jj < iterations; jj++) {
switch (p.label_placement)
{
case POINT_PLACEMENT:
geom.label_position(&label_x, &label_y);
break;
case INTERIOR_PLACEMENT:
geom.label_interior_position(&label_x, &label_y);
break;
case VERTEX_PLACEMENT:
geom.vertex(&label_x, &label_y);
break;
case LINE_PLACEMENT:
case label_placement_enum_MAX:
/*not handled here*/
break;
}
prj_trans.backward(label_x, label_y, z);
t.forward(&label_x, &label_y);
find_point_placement(label_x, label_y, angle);
}
update_detector();
} else if (p.label_placement == LINE_PLACEMENT && geom.num_points() > 1)
{
typedef coord_transform2<CoordTransform,geometry_type> path_type;
path_type path(t, geom, prj_trans);
find_line_placements<path_type>(path);
}
}
typedef coord_transform2<CoordTransform,geometry_type> PathType;
typedef label_collision_detector4 DetectorType;

View file

@ -189,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
@ -199,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);
}
}

View file

@ -38,8 +38,7 @@ shield_symbolizer::shield_symbolizer(text_placements_ptr placements)
: text_symbolizer(placements),
symbolizer_with_image(),
unlock_image_(false),
no_text_(false),
shield_displacement_(boost::make_tuple<double,double>(0,0))
shield_displacement_(0,0)
{
}
@ -52,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)
{
}
@ -65,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)
{
}
@ -80,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_;
}

351
src/symbolizer_helpers.cpp Normal file
View file

@ -0,0 +1,351 @@
#include <mapnik/symbolizer_helpers.hpp>
namespace mapnik {
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_placement()
{
if (!placement_valid_) return text_placement_info_ptr();
if (point_placement_)
return get_point_placement();
else
return get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_line_placement()
{
while (geometries_to_process_.size())
{
if (geo_itr_ == geometries_to_process_.end())
{
//Just processed the last geometry. Try next placement.
if (!next_placement()) return text_placement_info_ptr(); //No more placements
//Start again from begin of list
geo_itr_ = geometries_to_process_.begin();
continue; //Reexecute size check
}
//TODO: Avoid calling constructor repeatedly
placement_finder<DetectorT> finder(*placement_, *info_, detector_, dims_);
typedef coord_transform2<CoordTransform,geometry_type> path_type;
path_type path(t_, **geo_itr_, prj_trans_);
finder.find_line_placements(path);
//Keep reference to current object so we can delete it.
std::list<geometry_type*>::iterator current_object = geo_itr_;
geo_itr_++;
if (placement_->placements.size())
{
//Found a placement
geometries_to_process_.erase(current_object);
if (writer_.first) writer_.first->add_text(
*placement_, font_manager_,
feature_, t_, writer_.second);
return placement_;
}
//No placement for this geometry. Keep it in geometries_to_process_ for next try.
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr text_symbolizer_helper<FaceManagerT, DetectorT>::get_point_placement()
{
while (points_.size())
{
if (point_itr_ == points_.end())
{
//Just processed the last point. Try next placement.
if (!next_placement()) return text_placement_info_ptr(); //No more placements
//Start again from begin of list
point_itr_ = points_.begin();
continue; //Reexecute size check
}
placement_finder<DetectorT> finder(*placement_, *info_, detector_, dims_);
finder.find_point_placement(point_itr_->first, point_itr_->second, angle_);
//Keep reference to current object so we can delete it.
std::list<position>::iterator current_object = point_itr_;
point_itr_++;
if (placement_->placements.size())
{
//Found a placement
points_.erase(current_object);
if (writer_.first) writer_.first->add_text(
*placement_, font_manager_,
feature_, t_, writer_.second);
finder.update_detector();
return placement_;
}
//No placement for this point. Keep it in points_ for next try.
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_geometries()
{
unsigned num_geom = feature_.num_geometries();
for (unsigned i=0; i<num_geom; ++i)
{
geometry_type const& geom = feature_.get_geometry(i);
// don't bother with empty geometries
if (geom.num_points() == 0) continue;
if ((geom.type() == Polygon) && sym_.get_minimum_path_length() > 0)
{
// TODO - find less costly method than fetching full envelope
box2d<double> gbox = t_.forward(geom.envelope(), prj_trans_);
if (gbox.width() < sym_.get_minimum_path_length())
{
continue;
}
}
// TODO - calculate length here as well
geometries_to_process_.push_back(const_cast<geometry_type*>(&geom));
}
geo_itr_ = geometries_to_process_.begin();
}
template <typename FaceManagerT, typename DetectorT>
void text_symbolizer_helper<FaceManagerT, DetectorT>::initialize_points()
{
label_placement_enum how_placed = placement_->properties.label_placement;
if (how_placed == LINE_PLACEMENT) {
point_placement_ = false;
return;
} else {
point_placement_ = true;
}
double label_x=0.0;
double label_y=0.0;
double z=0.0;
std::list<geometry_type*>::const_iterator itr = geometries_to_process_.begin();
std::list<geometry_type*>::const_iterator end = geometries_to_process_.end();
for (; itr != end; itr++)
{
geometry_type const& geom = **itr;
if (how_placed == VERTEX_PLACEMENT)
{
geom.rewind(0);
for(unsigned i = 0; i < geom.num_points(); i++)
{
geom.vertex(&label_x, &label_y);
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(std::make_pair(label_x, label_y));
}
} else {
if (how_placed == POINT_PLACEMENT)
{
geom.label_position(&label_x, &label_y);
} else if (how_placed == INTERIOR_PLACEMENT)
{
geom.label_interior_position(&label_x, &label_y);
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "ERROR: Unknown placement type in initialize_points();\n";
#endif
}
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(std::make_pair(label_x, label_y));
}
}
point_itr_ = points_.begin();
}
template <typename FaceManagerT, typename DetectorT>
bool text_symbolizer_helper<FaceManagerT, DetectorT>::next_placement()
{
if (!placement_->next()) {
placement_valid_ = false;
return false;
}
placement_->properties.processor.process(text_, feature_);
info_ = &(text_.get_string_info());
if (placement_->properties.orientation)
{
angle_ = boost::apply_visitor(
evaluate<Feature, value_type>(feature_),
*(placement_->properties.orientation)).to_double();
} else {
angle_ = 0.0;
}
return true;
}
/*****************************************************************************/
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_placement()
{
if (!placement_valid_ || !marker_) return text_placement_info_ptr();
if (point_placement_)
return get_point_placement();
else
return get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_point_placement()
{
position const& shield_pos = sym_.get_shield_displacement();
while (points_.size())
{
if (point_itr_ == points_.end())
{
//Just processed the last point. Try next placement.
if (!next_placement()) return text_placement_info_ptr(); //No more placements
//Start again from begin of list
point_itr_ = points_.begin();
continue; //Reexecute size check
}
position const& pos = placement_->properties.displacement;
double label_x = point_itr_->first + shield_pos.first;
double label_y = point_itr_->second + shield_pos.second;
placement_finder<DetectorT> finder(*placement_, *info_, detector_, dims_);
finder.find_point_placement(label_x, label_y, angle_);
//Keep reference to current object so we can delete it.
std::list<position>::iterator current_object = point_itr_;
point_itr_++;
if (!placement_->placements.size())
{
//No placement for this point. Keep it in points_ for next try.
continue;
}
//Found a label placement but not necessarily also a marker placement
// check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies
double x = floor(placement_->placements[0].starting_x);
double y = floor(placement_->placements[0].starting_y);
if (!sym_.get_unlock_image())
{
// center image at text center position
// remove displacement from image label
double lx = x - pos.first;
double ly = y - pos.second;
marker_x_ = int(floor(lx - (0.5 * marker_w_))) + 1;
marker_y_ = int(floor(ly - (0.5 * marker_h_))) + 1;
marker_ext_.re_center(lx, ly);
}
else
{ // center image at reference location
marker_x_ = int(floor(label_x - 0.5 * marker_w_));
marker_y_ = int(floor(label_y - 0.5 * marker_h_));
marker_ext_.re_center(label_x, label_y);
}
if (placement_->properties.allow_overlap || detector_.has_placement(marker_ext_))
{
detector_.insert(marker_ext_);
finder.update_detector();
if (writer_.first) {
writer_.first->add_box(marker_ext_, feature_, t_, writer_.second);
writer_.first->add_text(*placement_, font_manager_, feature_, t_, writer_.second);
}
points_.erase(current_object);
return placement_;
}
}
return text_placement_info_ptr();
}
template <typename FaceManagerT, typename DetectorT>
text_placement_info_ptr shield_symbolizer_helper<FaceManagerT, DetectorT>::get_line_placement()
{
position const& pos = placement_->properties.displacement;
placement_->additional_boxes.push_back(
/*TODO: I'm not sure this is correct. It's what the old code did, but
I think transfroms can make the marker non-centered.
*/
box2d<double>(-0.5 * marker_ext_.width() - pos.first,
-0.5 * marker_ext_.height() - pos.second,
0.5 * marker_ext_.width() - pos.first,
0.5 * marker_ext_.height() - pos.second));
return text_symbolizer_helper<FaceManagerT, DetectorT>::get_line_placement();
}
template <typename FaceManagerT, typename DetectorT>
void shield_symbolizer_helper<FaceManagerT, DetectorT>::init_marker()
{
std::string filename = path_processor_type::evaluate(*sym_.get_filename(), this->feature_);
boost::array<double,6> const& m = sym_.get_transform();
transform_.load_from(&m[0]);
marker_.reset();
if (!filename.empty())
{
marker_ = marker_cache::instance()->find(filename, true);
}
if (!marker_) {
marker_w_ = 0;
marker_h_ = 0;
marker_ext_.init(0, 0, 0, 0);
return;
}
marker_w_ = (*marker_)->width();
marker_h_ = (*marker_)->height();
double px0 = - 0.5 * marker_w_;
double py0 = - 0.5 * marker_h_;
double px1 = 0.5 * marker_w_;
double py1 = 0.5 * marker_h_;
double px2 = px1;
double py2 = py0;
double px3 = px0;
double py3 = py1;
transform_.transform(&px0,&py0);
transform_.transform(&px1,&py1);
transform_.transform(&px2,&py2);
transform_.transform(&px3,&py3);
marker_ext_.init(px0, py0, px1, py1);
marker_ext_.expand_to_include(px2, py2);
marker_ext_.expand_to_include(px3, py3);
}
template <typename FaceManagerT, typename DetectorT>
std::pair<int, int> shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker_position(text_path &p)
{
position const& pos = placement_->properties.displacement;
if (placement_->properties.label_placement == LINE_PLACEMENT) {
double x = floor(p.starting_x);
double y = floor(p.starting_y);
double lx = x - pos.first;
double ly = y - pos.second;
int px = int(floor(lx - (0.5*marker_w_))) + 1;
int py = int(floor(ly - (0.5*marker_h_))) + 1;
marker_ext_.re_center(lx, ly);
// detector_->insert(label_ext); //TODO: Is this done by placement_finder?
if (writer_.first) writer_.first->add_box(marker_ext_, feature_, t_, writer_.second);
return std::make_pair(px, py);
} else {
return std::make_pair(marker_x_, marker_y_);
}
}
template <typename FaceManagerT, typename DetectorT>
marker& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_marker() const
{
return **marker_;
}
template <typename FaceManagerT, typename DetectorT>
agg::trans_affine const& shield_symbolizer_helper<FaceManagerT, DetectorT>::get_transform() const
{
return transform_;
}
template class text_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
template class shield_symbolizer_helper<face_manager<freetype_engine>, label_collision_detector4>;
} //namespace

View file

@ -63,7 +63,7 @@ text_symbolizer_properties::text_symbolizer_properties() :
}
void text_symbolizer_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets)
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_;
@ -95,9 +95,9 @@ void text_symbolizer_properties::set_values_from_xml(boost::property_tree::ptree
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.get<0>() = *dx;
if (dx) displacement.first = *dx;
optional<double> dy = get_opt_attr<double>(sym, "dy");
if (dy) displacement.get<1>() = *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);
processor.from_xml(sym, fontsets);
@ -118,13 +118,13 @@ void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, bool
}
}
if (displacement.get<0>() != dfl.displacement.get<0>() || explicit_defaults)
if (displacement.first != dfl.displacement.first || explicit_defaults)
{
set_attr(node, "dx", displacement.get<0>());
set_attr(node, "dx", displacement.first);
}
if (displacement.get<1>() != dfl.displacement.get<1>() || explicit_defaults)
if (displacement.second != dfl.displacement.second || explicit_defaults)
{
set_attr(node, "dy", displacement.get<1>());
set_attr(node, "dy", displacement.second);
}
if (label_placement != dfl.label_placement || explicit_defaults)
{
@ -204,7 +204,7 @@ char_properties::char_properties() :
}
void char_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map<std::string,font_set> const & fontsets)
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_;
@ -328,11 +328,13 @@ std::set<expression_ptr> text_placements::get_all_expressions()
/************************************************************************/
text_placement_info::text_placement_info(text_placements const* parent):
properties(parent->properties),
scale_factor(1),
has_dimensions(false),
collect_extents(false)
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)
{
}
@ -344,17 +346,11 @@ bool text_placement_info_dummy::next()
return true;
}
text_placement_info_ptr text_placements_dummy::get_placement_info() const
text_placement_info_ptr text_placements_dummy::get_placement_info(
double scale_factor, dimension_type dim, bool has_dimensions) const
{
return text_placement_info_ptr(new text_placement_info_dummy(this));
}
void text_placement_info::init(double scale_factor_,
unsigned w, unsigned h, bool has_dimensions_)
{
scale_factor = scale_factor_;
dimensions = std::make_pair(w, h);
has_dimensions = has_dimensions_;
return text_placement_info_ptr(new text_placement_info_dummy(
this, scale_factor, dim, has_dimensions));
}
/************************************************************************/
@ -389,36 +385,28 @@ bool text_placement_info_simple::next_position_only()
displacement = pdisp;
break;
case NORTH:
displacement = boost::make_tuple(0, -abs(pdisp.get<1>()));
displacement = std::make_pair(0, -abs(pdisp.second));
break;
case EAST:
displacement = boost::make_tuple(abs(pdisp.get<0>()), 0);
displacement = std::make_pair(abs(pdisp.first), 0);
break;
case SOUTH:
displacement = boost::make_tuple(0, abs(pdisp.get<1>()));
displacement = std::make_pair(0, abs(pdisp.second));
break;
case WEST:
displacement = boost::make_tuple(-abs(pdisp.get<0>()), 0);
displacement = std::make_pair(-abs(pdisp.first), 0);
break;
case NORTHEAST:
displacement = boost::make_tuple(
abs(pdisp.get<0>()),
-abs(pdisp.get<1>()));
displacement = std::make_pair(abs(pdisp.first), -abs(pdisp.second));
break;
case SOUTHEAST:
displacement = boost::make_tuple(
abs(pdisp.get<0>()),
abs(pdisp.get<1>()));
displacement = std::make_pair(abs(pdisp.first), abs(pdisp.second));
break;
case NORTHWEST:
displacement = boost::make_tuple(
-abs(pdisp.get<0>()),
-abs(pdisp.get<1>()));
displacement = std::make_pair(-abs(pdisp.first), -abs(pdisp.second));
break;
case SOUTHWEST:
displacement = boost::make_tuple(
-abs(pdisp.get<0>()),
abs(pdisp.get<1>()));
displacement = std::make_pair(-abs(pdisp.first), abs(pdisp.second));
break;
default:
std::cerr << "WARNING: Unknown placement\n";
@ -427,22 +415,24 @@ 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));
}
/** 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
* is always the one given in the TextSymbolizer's parameters.
* First all directions are tried, then font size is reduced
* and all directions are tried again. The process ends when a placement is
* found or the last fontsize is tried without success.
* Example: N,S,15,10,8 (tries placement above, then below and if
* that fails it tries the additional font sizes 15, 10 and 8.
*/
* [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
* is always the one given in the TextSymbolizer's parameters.
* First all directions are tried, then font size is reduced
* and all directions are tried again. The process ends when a placement is
* found or the last fontsize is tried without success.
* Example: N,S,15,10,8 (tries placement above, then below and if
* that fails it tries the additional font sizes 15, 10 and 8.
*/
void text_placements_simple::set_positions(std::string positions)
{
positions_ = positions;
@ -460,16 +450,16 @@ void text_placements_simple::set_positions(std::string positions)
("NW", NORTHWEST)
("SW", SOUTHWEST)
("X" , EXACT_POSITION)
;
;
}
} direction_name;
std::string::iterator first = positions.begin(), last = positions.end();
qi::phrase_parse(first, last,
(direction_name[push_back(phoenix::ref(direction_), _1)] % ',') >> *(',' >> qi::float_[push_back(phoenix::ref(text_sizes_), _1)]),
space
);
(direction_name[push_back(phoenix::ref(direction_), _1)] % ',') >> *(',' >> qi::float_[push_back(phoenix::ref(text_sizes_), _1)]),
space
);
if (first != last) {
std::cerr << "WARNING: Could not parse text_placement_simple placement string ('" << positions << "').\n";
}
@ -483,16 +473,15 @@ text_placements_simple::text_placements_simple()
set_positions("X");
}
std::string const& text_placements_simple::get_positions() const
{
return positions_;
}
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_
}
/***************************************************************************/
@ -526,9 +515,11 @@ text_symbolizer_properties & text_placements_list::get(unsigned i)
/***************************************************************************/
text_placement_info_ptr text_placements_list::get_placement_info() const
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));
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)

View file

@ -25,371 +25,378 @@
#include <mapnik/feature.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/ptree_helpers.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;
class abstract_token
{
public:
virtual ~abstract_token() {}
virtual ptree *to_xml(ptree *node) = 0;
};
namespace formating {
class abstract_formating_token : public abstract_token
void node::to_xml(boost::property_tree::ptree &xml) const
{
public:
virtual void apply(char_properties &p, Feature const& feature) = 0;
};
//TODO: Should this throw a config_error?
#ifdef MAPNIK_DEBUG
std::cerr << "Error: Trying to write unsupported node type to XML.\n";
#endif
}
class abstract_text_token : public abstract_token
node_ptr node::from_xml(boost::property_tree::ptree const& xml)
{
public:
virtual UnicodeString to_string(Feature const& feature) = 0;
};
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();
}
}
class end_format_token : public abstract_token
void node::add_expressions(std::set<expression_ptr> &expressions) const
{
public:
end_format_token() {}
ptree *to_xml(ptree *node);
};
class expression_token: public abstract_text_token
{
public:
expression_token(expression_ptr text);
UnicodeString to_string(Feature const& feature);
ptree *to_xml(ptree *node);
void set_expression(expression_ptr text);
expression_ptr get_expression();
private:
expression_ptr text_;
};
class fixed_formating_token : public abstract_formating_token
{
public:
fixed_formating_token();
virtual void apply(char_properties &p, Feature const& feature);
ptree* to_xml(ptree *node);
void from_xml(ptree const& node);
void set_face_name(optional<std::string> face_name);
void set_text_size(optional<unsigned> text_size);
void set_character_spacing(optional<unsigned> character_spacing);
void set_line_spacing(optional<unsigned> line_spacing);
void set_text_opacity(optional<double> opacity);
void set_wrap_before(optional<boolean> wrap_before);
void set_wrap_char(optional<unsigned> wrap_char);
void set_text_transform(optional<text_transform_e> text_trans);
void set_fill(optional<color> fill);
void set_halo_fill(optional<color> halo_fill);
void set_halo_radius(optional<double> radius);
private:
boost::optional<std::string> face_name_;
// font_set fontset;
boost::optional<unsigned> text_size_;
boost::optional<unsigned> character_spacing_;
boost::optional<unsigned> line_spacing_;
boost::optional<double> text_opacity_;
boost::optional<boolean> 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_;
};
//Do nothing by default
}
/************************************************************/
expression_token::expression_token(expression_ptr text):
text_(text)
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 expression_token::set_expression(expression_ptr text)
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_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 expression_token::get_expression()
expression_ptr text_node::get_text() const
{
return text_;
}
UnicodeString expression_token::to_string(const Feature &feature)
{
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(feature), *text_);
return result.to_unicode();
}
ptree *expression_token::to_xml(ptree *node)
{
ptree &new_node = node->push_back(ptree::value_type(
"<xmltext>", ptree()))->second;
new_node.put_value(to_expression_string(*text_));
return &new_node;
}
/************************************************************/
fixed_formating_token::fixed_formating_token():
fill_()
format_node::format_node():
node(),
fill_(),
child_()
{
}
void fixed_formating_token::apply(char_properties &p, const Feature &feature)
void format_node::to_xml(ptree &xml) const
{
if (face_name_) p.face_name = *face_name_;
if (text_size_) p.text_size = *text_size_;
if (character_spacing_) p.character_spacing = *character_spacing_;
if (line_spacing_) p.line_spacing = *line_spacing_;
if (text_opacity_) p.text_opacity = *text_opacity_;
if (wrap_before_) p.wrap_before = *wrap_before_;
if (wrap_char_) p.wrap_char = *wrap_char_;
if (text_transform_) p.text_transform = *text_transform_;
if (fill_) p.fill = *fill_;
if (halo_fill_) p.halo_fill = *halo_fill_;
if (halo_radius_) p.halo_radius = *halo_radius_;
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);
}
ptree *fixed_formating_token::to_xml(ptree *node)
{
ptree &new_node = node->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_);
return &new_node;
}
void fixed_formating_token::from_xml(ptree const& node)
node_ptr format_node::from_xml(ptree const& xml)
{
set_face_name(get_opt_attr<std::string>(node, "face-name"));
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... */
set_text_size(get_opt_attr<unsigned>(node, "size"));
set_character_spacing(get_opt_attr<unsigned>(node, "character-spacing"));
set_line_spacing(get_opt_attr<unsigned>(node, "line-spacing"));
set_text_opacity(get_opt_attr<double>(node, "opactity"));
set_wrap_before(get_opt_attr<boolean>(node, "wrap-before"));
set_wrap_char(get_opt_attr<unsigned>(node, "wrap-character"));
set_text_transform(get_opt_attr<text_transform_e>(node, "text-transform"));
set_fill(get_opt_attr<color>(node, "fill"));
set_halo_fill(get_opt_attr<color>(node, "halo-fill"));
set_halo_radius(get_opt_attr<double>(node, "halo-radius"));
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 fixed_formating_token::set_face_name(optional<std::string> face_name)
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 fixed_formating_token::set_text_size(optional<unsigned> text_size)
void format_node::set_text_size(optional<unsigned> text_size)
{
text_size_ = text_size;
}
void fixed_formating_token::set_character_spacing(optional<unsigned> character_spacing)
void format_node::set_character_spacing(optional<unsigned> character_spacing)
{
character_spacing_ = character_spacing;
}
void fixed_formating_token::set_line_spacing(optional<unsigned> line_spacing)
void format_node::set_line_spacing(optional<unsigned> line_spacing)
{
line_spacing_ = line_spacing;
}
void fixed_formating_token::set_text_opacity(optional<double> text_opacity)
void format_node::set_text_opacity(optional<double> text_opacity)
{
text_opacity_ = text_opacity;
}
void fixed_formating_token::set_wrap_before(optional<boolean> wrap_before)
void format_node::set_wrap_before(optional<bool> wrap_before)
{
wrap_before_ = wrap_before;
}
void fixed_formating_token::set_wrap_char(optional<unsigned> wrap_char)
void format_node::set_wrap_char(optional<unsigned> wrap_char)
{
wrap_char_ = wrap_char;
}
void fixed_formating_token::set_text_transform(optional<text_transform_e> text_transform)
void format_node::set_text_transform(optional<text_transform_e> text_transform)
{
text_transform_ = text_transform;
}
void fixed_formating_token::set_fill(optional<color> c)
void format_node::set_fill(optional<color> c)
{
fill_ = c;
}
void fixed_formating_token::set_halo_fill(optional<color> c)
void format_node::set_halo_fill(optional<color> c)
{
halo_fill_ = c;
}
void fixed_formating_token::set_halo_radius(optional<double> radius)
void format_node::set_halo_radius(optional<double> radius)
{
halo_radius_ = radius;
}
/************************************************************/
ptree *end_format_token::to_xml(ptree *node)
{
return 0;
}
} //namespace formating
/************************************************************/
text_processor::text_processor():
list_(), clear_on_write(false)
tree_()
{
}
void text_processor::push_back(abstract_token *token)
void text_processor::set_format_tree(formating::node_ptr tree)
{
if (clear_on_write) list_.clear();
clear_on_write = false;
list_.push_back(token);
tree_ = tree;
}
formating::node_ptr text_processor::get_format_tree() const
{
return tree_;
}
void text_processor::from_xml(const boost::property_tree::ptree &pt, std::map<std::string,font_set> const &fontsets)
{
clear_on_write = true;
defaults.set_values_from_xml(pt, fontsets);
from_xml_recursive(pt, fontsets);
defaults.from_xml(pt, fontsets);
formating::node_ptr n = formating::node::from_xml(pt);
if (n) set_format_tree(n);
}
void text_processor::from_xml_recursive(const boost::property_tree::ptree &pt, std::map<std::string,font_set> const &fontsets)
{
ptree::const_iterator itr = pt.begin();
ptree::const_iterator end = pt.end();
for (; itr != end; ++itr) {
if (itr->first == "<xmltext>") {
std::string data = itr->second.data();
boost::trim(data);
if (data.empty()) continue;
expression_token *token = new expression_token(parse_expression(data, "utf8"));
push_back(token);
} else if (itr->first == "Format") {
fixed_formating_token *token = new fixed_formating_token();
token->from_xml(itr->second);
push_back(token);
from_xml_recursive(itr->second, fontsets); /* Parse children, making a list out of a tree. */
push_back(new end_format_token());
} else if (itr->first != "<xmlcomment>" && itr->first != "<xmlattr>" && itr->first != "Placement") {
std::cerr << "Unknown item" << itr->first;
}
}
}
void text_processor::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_processor const& dfl) const
{
defaults.to_xml(node, explicit_defaults, dfl.defaults);
std::list<abstract_token *>::const_iterator itr = list_.begin();
std::list<abstract_token *>::const_iterator end = list_.end();
std::stack<ptree *> nodes;
ptree *current_node = &node;
for (; itr != end; ++itr) {
abstract_token *token = *itr;
ptree *new_node = token->to_xml(current_node);
if (dynamic_cast<abstract_formating_token *>(token)) {
nodes.push(current_node);
current_node = new_node;
} else if (dynamic_cast<end_format_token *>(token)) {
current_node = nodes.top();
nodes.pop();
}
}
if (tree_) tree_->to_xml(node);
}
void text_processor::process(processed_text &output, Feature const& feature)
void text_processor::process(processed_text &output, Feature const& feature) const
{
std::list<abstract_token *>::const_iterator itr = list_.begin();
std::list<abstract_token *>::const_iterator end = list_.end();
std::stack<char_properties> formats;
formats.push(defaults);
for (; itr != end; ++itr) {
abstract_text_token *text = dynamic_cast<abstract_text_token *>(*itr);
abstract_formating_token *format = dynamic_cast<abstract_formating_token *>(*itr);;
end_format_token *end = dynamic_cast<end_format_token *>(*itr);;
if (text) {
UnicodeString text_str = text->to_string(feature);
char_properties const& p = formats.top();
/* TODO: Make a class out of text_transform which does the work! */
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_expression(p, text_str));
} else {
output.clear();
if (tree_) {
tree_->apply(defaults, feature, output);
} else {
#ifdef MAPNIK_DEBUG
std::cerr << "Warning: Empty expression.\n";
std::cerr << "Warning: text_processor can't produce text: No formating tree!\n";
#endif
}
} else if (format) {
char_properties next_properties = formats.top();
format->apply(next_properties, feature);
formats.push(next_properties);
} else if (end) {
/* Always keep at least the defaults_ on stack. */
if (formats.size() > 1) {
formats.pop();
} else {
std::cerr << "Warning: Internal mapnik error. More elements popped than pushed in text_processor::process()\n";
output.clear();
return;
}
}
}
if (formats.size() != 1) {
std::cerr << "Warning: Internal mapnik error. Less elements popped than pushed in text_processor::process()\n";
}
}
std::set<expression_ptr> text_processor::get_all_expressions() const
{
std::set<expression_ptr> result;
std::list<abstract_token *>::const_iterator itr = list_.begin();
std::list<abstract_token *>::const_iterator end = list_.end();
for (; itr != end; ++itr) {
expression_token *text = dynamic_cast<expression_token *>(*itr);
if (text) result.insert(text->get_expression());
}
if (tree_) tree_->add_expressions(result);
return result;
}
void text_processor::set_old_style_expression(expression_ptr expr)
{
list_.push_back(new expression_token(expr));
tree_ = formating::node_ptr(new formating::text_node(expr));
}
/************************************************************/
@ -399,12 +406,12 @@ void processed_text::push_back(processed_expression const& exp)
expr_list_.push_back(exp);
}
processed_text::expression_list::const_iterator processed_text::begin()
processed_text::expression_list::const_iterator processed_text::begin() const
{
return expr_list_.begin();
}
processed_text::expression_list::const_iterator processed_text::end()
processed_text::expression_list::const_iterator processed_text::end() const
{
return expr_list_.end();
}
@ -424,7 +431,7 @@ void processed_text::clear()
string_info &processed_text::get_string_info()
{
//info_.clear(); TODO: if this function is called twice invalid results are returned, so clear string_info first
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)

View file

@ -342,7 +342,7 @@ label_placement_e text_symbolizer::get_label_placement() const
void text_symbolizer::set_displacement(double x, double y)
{
placement_options_->properties.displacement = boost::make_tuple(x,y);
placement_options_->properties.displacement = std::make_pair(x,y);
}
void text_symbolizer::set_displacement(position const& p)

View file

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

View file

@ -24,7 +24,6 @@ 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)
@ -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)

View file

@ -74,6 +74,7 @@ def test_linesymbolizer_pickle():
# TextSymbolizer pickling
def test_textsymbolizer_pickle():
raise Todo("text_symbolizer pickling currently disabled")
ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black'))
eq_(str(ts.name), str(mapnik.Expression('[Field_Name]')))
@ -81,7 +82,6 @@ def test_textsymbolizer_pickle():
eq_(ts.text_size, 8)
eq_(ts.fill, mapnik.Color('black'))
raise Todo("text_symbolizer pickling currently disabled")
ts2 = pickle.loads(pickle.dumps(ts,pickle.HIGHEST_PROTOCOL))
eq_(ts.name, ts2.name)

View file

@ -63,8 +63,9 @@ def main():
# 3 * '-v' gets us debugging information from nose
argv.append('-v')
argv.append('-v')
argv.extend(['-w','./tests/python_tests'])
dirname = os.path.dirname(sys.argv[0])
argv.extend(['-w', dirname+'/python_tests'])
if not nose.run(argv=argv, plugins=[TodoPlugin(), Doctest()]):
sys.exit(1)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

View file

@ -0,0 +1,19 @@
<?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>
<ShieldSymbolizer face-name="DejaVu Sans Book" size="16" placement="point" file="../data/svg/rect.svg" dx="0" dy="-5">'x'</ShieldSymbolizer>
</Rule>
</Style>
</Map>

View file

@ -11,7 +11,9 @@ dirname = os.path.dirname(sys.argv[0])
widths = [ 800, 600, 400, 300, 250, 200, 150, 100]
filenames = ["list", "simple"]
filenames_one_width = ["simple-E", "simple-NE", "simple-NW", "simple-N",
"simple-SE", "simple-SW", "simple-S", "simple-W", "formating"]
"simple-SE", "simple-SW", "simple-S", "simple-W",
"formating-1", "formating-2", "formating-3", "formating-4",
"shieldsymbolizer-1"]
def render(filename, width):
print "Rendering style \"%s\" with width %d" % (filename, width)
@ -22,12 +24,16 @@ def render(filename, width):
mapnik.render_to_file(m, '%s-%d-agg.png' % (filename, width))
return m
if len(sys.argv) > 1:
filenames = []
filenames_one_width = sys.argv[1:]
for filename in filenames:
for width in widths:
m = render(filename, width)
mapnik.save_map(m, "%s-out.xml" % filename)
for filename in filenames_one_width:
render(filename, 500)
m = render(filename, 500)
mapnik.save_map(m, "%s-out.xml" % filename)

View file

@ -196,7 +196,6 @@
line_spacing CDATA "0"
min_distance CDATA "0"
name CDATA #IMPLIED
no_text (true|false) "false"
placement (point|line|vertex) "point"
size CDATA "10"
spacing CDATA "0"