avoid storing evaluated text properties in text_symbolizer_properties (all style related strucures must be read-only)
make process(..) method const
This commit is contained in:
parent
c6ed108a72
commit
74e872c48b
9 changed files with 109 additions and 128 deletions
|
@ -39,7 +39,7 @@ using DetectorType = label_collision_detector4;
|
|||
|
||||
using pixel_position_list = std::list<pixel_position>;
|
||||
|
||||
/** Helper object that does some of the GroupSymbolizer placement finding work. */
|
||||
// Helper object that does some of the GroupSymbolizer placement finding work.
|
||||
class group_symbolizer_helper : public base_symbolizer_helper
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -85,6 +85,7 @@ private:
|
|||
DetectorType & detector_;
|
||||
box2d<double> const& extent_;
|
||||
text_placement_info const& info_;
|
||||
evaluated_text_properties_ptr text_props_;
|
||||
layout_container layouts_;
|
||||
|
||||
double scale_factor_;
|
||||
|
|
|
@ -59,7 +59,7 @@ bool placement_finder::find_line_placements(T & path, bool points)
|
|||
}
|
||||
else
|
||||
{
|
||||
if ((pp.length() < info_.properties.minimum_path_length * scale_factor_)
|
||||
if ((pp.length() < text_props_->minimum_path_length * scale_factor_)
|
||||
||
|
||||
(pp.length() <= 0.001) // Clipping removed whole geometry
|
||||
||
|
||||
|
@ -89,14 +89,13 @@ bool placement_finder::find_line_placements(T & path, bool points)
|
|||
|
||||
do
|
||||
{
|
||||
tolerance_iterator tolerance_offset(info_.properties.label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign
|
||||
tolerance_iterator tolerance_offset(text_props_->label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign
|
||||
while (tolerance_offset.next())
|
||||
{
|
||||
vertex_cache::scoped_state state(pp);
|
||||
if (pp.move(tolerance_offset.get())
|
||||
&& (
|
||||
(points && find_point_placement(pp.current_position()))
|
||||
|| (!points && single_line_placement(pp, info_.properties.upright))))
|
||||
&& ((points && find_point_placement(pp.current_position()))
|
||||
|| (!points && single_line_placement(pp, text_props_->upright))))
|
||||
{
|
||||
success = true;
|
||||
break;
|
||||
|
|
|
@ -97,6 +97,7 @@ protected:
|
|||
// Use point placement. Otherwise line placement is used.
|
||||
mutable bool point_placement_;
|
||||
text_placement_info_ptr placement_;
|
||||
evaluated_text_properties_ptr text_props_;
|
||||
};
|
||||
|
||||
// Helper object that does all the TextSymbolizer placement finding
|
||||
|
|
|
@ -49,34 +49,41 @@ namespace detail {
|
|||
|
||||
struct evaluated_format_properties
|
||||
{
|
||||
evaluated_format_properties() :
|
||||
face_name(),
|
||||
text_size(0.0),
|
||||
character_spacing(0.0),
|
||||
line_spacing(0.0),
|
||||
text_opacity(1.0),
|
||||
halo_opacity(1.0),
|
||||
text_transform(NONE),
|
||||
fill(0,0,0),
|
||||
halo_fill(0,0,0),
|
||||
halo_radius(0.0),
|
||||
font_feature_settings(std::make_shared<mapnik::font_feature_settings>()) {}
|
||||
std::string face_name;
|
||||
std::string face_name = "";
|
||||
boost::optional<font_set> fontset;
|
||||
double text_size;
|
||||
double character_spacing;
|
||||
double line_spacing;
|
||||
double text_opacity;
|
||||
double halo_opacity;
|
||||
text_transform_e text_transform;
|
||||
color fill;
|
||||
color halo_fill;
|
||||
double halo_radius;
|
||||
font_feature_settings_ptr font_feature_settings;
|
||||
double text_size = 0.0;
|
||||
double character_spacing = 0.0;
|
||||
double line_spacing = 0.0;
|
||||
double text_opacity = 1.0;
|
||||
double halo_opacity = 1.0;
|
||||
text_transform_e text_transform = NONE;
|
||||
color fill = color(0,0,0);
|
||||
color halo_fill = color(255,255,255);
|
||||
double halo_radius = 0.0;
|
||||
font_feature_settings_ptr font_feature_settings = std::make_shared<mapnik::font_feature_settings>();
|
||||
};
|
||||
|
||||
struct evaluated_text_properties
|
||||
{
|
||||
label_placement_e label_placement = POINT_PLACEMENT;
|
||||
double label_spacing = 0.0; // distance between repeated labels on a single geometry
|
||||
double label_position_tolerance = 0.0; // distance the label can be moved on the line to fit, if 0 the default is used
|
||||
bool avoid_edges = false;
|
||||
double margin = 0.0;
|
||||
double repeat_distance = 0.0;
|
||||
double minimum_distance = 0.0;
|
||||
double minimum_padding = 0.0;
|
||||
double minimum_path_length = 0.0;
|
||||
double max_char_angle_delta = 22.5 * M_PI/180.0;
|
||||
bool allow_overlap = false;
|
||||
bool largest_bbox_only = true; // Only consider geometry with largest bbox (polygons)
|
||||
text_upright_e upright = UPRIGHT_AUTO;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using evaluated_text_properties_ptr = std::unique_ptr<detail::evaluated_text_properties>;
|
||||
|
||||
enum directions_e
|
||||
{
|
||||
NORTH,
|
||||
|
@ -179,8 +186,8 @@ struct MAPNIK_DECL text_symbolizer_properties
|
|||
|
||||
// Takes a feature and produces formatted text as output.
|
||||
// The output object has to be created by the caller and passed in for thread safety.
|
||||
void process(text_layout &output, feature_impl const& feature, attributes const& vars);
|
||||
void evaluate_text_properties(feature_impl const& feature, attributes const& attrs);
|
||||
void process(text_layout & output, feature_impl const& feature, attributes const& vars) const;
|
||||
evaluated_text_properties_ptr evaluate_text_properties(feature_impl const& feature, attributes const& attrs) const;
|
||||
// Sets new format tree.
|
||||
void set_format_tree(formatting::node_ptr tree);
|
||||
// Get a list of all expressions used in any placement.
|
||||
|
@ -188,23 +195,6 @@ struct MAPNIK_DECL text_symbolizer_properties
|
|||
void add_expressions(expression_set & output) const;
|
||||
|
||||
// Per symbolizer options
|
||||
label_placement_e label_placement;
|
||||
// distance between repeated labels on a single geometry
|
||||
double label_spacing;
|
||||
// distance the label can be moved on the line to fit, if 0 the default is used
|
||||
double label_position_tolerance;
|
||||
bool avoid_edges;
|
||||
double margin;
|
||||
double repeat_distance;
|
||||
double minimum_distance;
|
||||
double minimum_padding;
|
||||
double minimum_path_length;
|
||||
double max_char_angle_delta;
|
||||
bool allow_overlap;
|
||||
// Only consider geometry with largest bbox (polygons)
|
||||
bool largest_bbox_only;
|
||||
text_upright_e upright;
|
||||
|
||||
// Expressions
|
||||
text_properties_expressions expressions;
|
||||
// Default values for text layouts
|
||||
|
|
|
@ -95,7 +95,7 @@ bool group_symbolizer_helper::find_line_placements(T & path)
|
|||
pp.forward(spacing/2.0);
|
||||
do
|
||||
{
|
||||
tolerance_iterator tolerance_offset(placement_->properties.label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign
|
||||
tolerance_iterator tolerance_offset(text_props_->label_position_tolerance * scale_factor_, spacing); //TODO: Handle halign
|
||||
while (tolerance_offset.next())
|
||||
{
|
||||
vertex_cache::scoped_state state(pp);
|
||||
|
@ -146,16 +146,16 @@ bool group_symbolizer_helper::collision(box2d<double> const& box, value_unicode_
|
|||
{
|
||||
if (!detector_.extent().intersects(box)
|
||||
||
|
||||
(placement_->properties.avoid_edges && !query_extent_.contains(box))
|
||||
(text_props_->avoid_edges && !query_extent_.contains(box))
|
||||
||
|
||||
(placement_->properties.minimum_padding > 0 &&
|
||||
!query_extent_.contains(box + (scale_factor_ * placement_->properties.minimum_padding)))
|
||||
(text_props_->minimum_padding > 0 &&
|
||||
!query_extent_.contains(box + (scale_factor_ * text_props_->minimum_padding)))
|
||||
||
|
||||
(!placement_->properties.allow_overlap &&
|
||||
((repeat_key.length() == 0 && !detector_.has_placement(box, placement_->properties.margin * scale_factor_))
|
||||
(!text_props_->allow_overlap &&
|
||||
((repeat_key.length() == 0 && !detector_.has_placement(box, text_props_->margin * scale_factor_))
|
||||
||
|
||||
(repeat_key.length() > 0 && !detector_.has_placement(box, placement_->properties.margin * scale_factor_,
|
||||
repeat_key, placement_->properties.repeat_distance * scale_factor_))))
|
||||
(repeat_key.length() > 0 && !detector_.has_placement(box, text_props_->margin * scale_factor_,
|
||||
repeat_key, text_props_->repeat_distance * scale_factor_))))
|
||||
)
|
||||
{
|
||||
return true;
|
||||
|
@ -166,10 +166,10 @@ bool group_symbolizer_helper::collision(box2d<double> const& box, value_unicode_
|
|||
double group_symbolizer_helper::get_spacing(double path_length) const
|
||||
{
|
||||
int num_labels = 1;
|
||||
if (placement_->properties.label_spacing > 0)
|
||||
if (text_props_->label_spacing > 0)
|
||||
{
|
||||
num_labels = static_cast<int>(std::floor(
|
||||
path_length / (placement_->properties.label_spacing * scale_factor_)));
|
||||
path_length / (text_props_->label_spacing * scale_factor_)));
|
||||
}
|
||||
if (num_labels <= 0)
|
||||
{
|
||||
|
|
|
@ -51,13 +51,14 @@ placement_finder::placement_finder(feature_impl const& feature,
|
|||
attr_(attr),
|
||||
detector_(detector),
|
||||
extent_(extent),
|
||||
info_(placement_info),
|
||||
scale_factor_(scale_factor),
|
||||
font_manager_(font_manager),
|
||||
placements_(),
|
||||
has_marker_(false),
|
||||
marker_(),
|
||||
marker_box_() {}
|
||||
info_(placement_info),
|
||||
text_props_(info_.properties.evaluate_text_properties(feature_,attr_)),
|
||||
scale_factor_(scale_factor),
|
||||
font_manager_(font_manager),
|
||||
placements_(),
|
||||
has_marker_(false),
|
||||
marker_(),
|
||||
marker_box_() {}
|
||||
|
||||
bool placement_finder::next_position()
|
||||
{
|
||||
|
@ -66,6 +67,7 @@ bool placement_finder::next_position()
|
|||
text_layout_ptr layout = std::make_shared<text_layout>(font_manager_, scale_factor_, info_.properties.layout_defaults);
|
||||
layout->evaluate_properties(feature_, attr_);
|
||||
move_dx_ = layout->displacement().x;
|
||||
text_props_ = info_.properties.evaluate_text_properties(feature_,attr_); // this call is needed (text-bug1533) ??
|
||||
info_.properties.process(*layout, feature_, attr_);
|
||||
layouts_.clear(); // FIXME !!!!
|
||||
layouts_.add(layout);
|
||||
|
@ -231,8 +233,8 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or
|
|||
// Only calculate new angle at the start of each cluster!
|
||||
angle = normalize_angle(off_pp.angle(sign * layout.cluster_width(current_cluster)));
|
||||
rot.init(angle);
|
||||
if ((info_.properties.max_char_angle_delta > 0) && (last_cluster_angle != 999) &&
|
||||
std::fabs(normalize_angle(angle-last_cluster_angle)) > info_.properties.max_char_angle_delta)
|
||||
if ((text_props_->max_char_angle_delta > 0) && (last_cluster_angle != 999) &&
|
||||
std::fabs(normalize_angle(angle-last_cluster_angle)) > text_props_->max_char_angle_delta)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -307,10 +309,10 @@ double placement_finder::normalize_angle(double angle)
|
|||
double placement_finder::get_spacing(double path_length, double layout_width) const
|
||||
{
|
||||
int num_labels = 1;
|
||||
if (info_.properties.label_spacing > 0)
|
||||
if (text_props_->label_spacing > 0)
|
||||
{
|
||||
num_labels = static_cast<int>(std::floor(
|
||||
path_length / (info_.properties.label_spacing * scale_factor_ + layout_width)));
|
||||
path_length / (text_props_->label_spacing * scale_factor_ + layout_width)));
|
||||
}
|
||||
if (num_labels <= 0)
|
||||
{
|
||||
|
@ -324,25 +326,25 @@ bool placement_finder::collision(const box2d<double> &box, const value_unicode_s
|
|||
double margin, repeat_distance;
|
||||
if (line_placement)
|
||||
{
|
||||
margin = info_.properties.margin * scale_factor_;
|
||||
repeat_distance = (info_.properties.repeat_distance != 0 ? info_.properties.repeat_distance : info_.properties.minimum_distance) * scale_factor_;
|
||||
margin = text_props_->margin * scale_factor_;
|
||||
repeat_distance = (text_props_->repeat_distance != 0 ? text_props_->repeat_distance : text_props_->minimum_distance) * scale_factor_;
|
||||
}
|
||||
else
|
||||
{
|
||||
margin = (info_.properties.margin != 0 ? info_.properties.margin : info_.properties.minimum_distance) * scale_factor_;
|
||||
repeat_distance = info_.properties.repeat_distance * scale_factor_;
|
||||
margin = (text_props_->margin != 0 ? text_props_->margin : text_props_->minimum_distance) * scale_factor_;
|
||||
repeat_distance = text_props_->repeat_distance * scale_factor_;
|
||||
}
|
||||
return !detector_.extent().intersects(box)
|
||||
||
|
||||
(info_.properties.avoid_edges && !extent_.contains(box))
|
||||
||
|
||||
(info_.properties.minimum_padding > 0 &&
|
||||
!extent_.contains(box + (scale_factor_ * info_.properties.minimum_padding)))
|
||||
||
|
||||
(!info_.properties.allow_overlap &&
|
||||
((repeat_key.length() == 0 && !detector_.has_placement(box, margin))
|
||||
||
|
||||
(repeat_key.length() > 0 && !detector_.has_placement(box, margin, repeat_key, repeat_distance))));
|
||||
||
|
||||
(text_props_->avoid_edges && !extent_.contains(box))
|
||||
||
|
||||
(text_props_->minimum_padding > 0 &&
|
||||
!extent_.contains(box + (scale_factor_ * text_props_->minimum_padding)))
|
||||
||
|
||||
(!text_props_->allow_overlap &&
|
||||
((repeat_key.length() == 0 && !detector_.has_placement(box, margin))
|
||||
||
|
||||
(repeat_key.length() > 0 && !detector_.has_placement(box, margin, repeat_key, repeat_distance))));
|
||||
}
|
||||
|
||||
void placement_finder::set_marker(marker_info_ptr m, box2d<double> box, bool marker_unlocked, pixel_position const& marker_displacement)
|
||||
|
@ -370,14 +372,14 @@ box2d<double> placement_finder::get_bbox(text_layout const& layout, glyph_info c
|
|||
{
|
||||
/*
|
||||
|
||||
(0/ymax) (width/ymax)
|
||||
***************
|
||||
* *
|
||||
(0/0)* *
|
||||
* *
|
||||
***************
|
||||
(0/ymin) (width/ymin)
|
||||
Add glyph offset in y direction, but not in x direction (as we use the full cluster width anyways)!
|
||||
(0/ymax) (width/ymax)
|
||||
***************
|
||||
* *
|
||||
(0/0)* *
|
||||
* *
|
||||
***************
|
||||
(0/ymin) (width/ymin)
|
||||
Add glyph offset in y direction, but not in x direction (as we use the full cluster width anyways)!
|
||||
*/
|
||||
double width = layout.cluster_width(glyph.char_index);
|
||||
if (glyph.advance() <= 0) width = -width;
|
||||
|
|
|
@ -58,9 +58,9 @@ base_symbolizer_helper::base_symbolizer_helper(
|
|||
dims_(0, 0, width, height),
|
||||
query_extent_(query_extent),
|
||||
scale_factor_(scale_factor),
|
||||
placement_(get<text_placements_ptr>(sym_, keys::text_placements_)->get_placement_info(scale_factor))
|
||||
placement_(get<text_placements_ptr>(sym_, keys::text_placements_)->get_placement_info(scale_factor)),
|
||||
text_props_(placement_->properties.evaluate_text_properties(feature_,vars_))
|
||||
{
|
||||
placement_->properties.evaluate_text_properties(feature_, vars_);
|
||||
initialize_geometries();
|
||||
if (!geometries_to_process_.size()) return; // FIXME - bad practise
|
||||
initialize_points();
|
||||
|
@ -78,8 +78,8 @@ struct largest_bbox_first
|
|||
|
||||
void base_symbolizer_helper::initialize_geometries() const
|
||||
{
|
||||
bool largest_box_only = placement_->properties.largest_bbox_only;
|
||||
double minimum_path_length = placement_->properties.minimum_path_length;
|
||||
bool largest_box_only = text_props_->largest_bbox_only;
|
||||
double minimum_path_length = text_props_->minimum_path_length;
|
||||
for ( auto const& geom : feature_.paths())
|
||||
{
|
||||
// don't bother with empty geometries
|
||||
|
@ -111,7 +111,7 @@ void base_symbolizer_helper::initialize_geometries() const
|
|||
|
||||
void base_symbolizer_helper::initialize_points() const
|
||||
{
|
||||
label_placement_enum how_placed = placement_->properties.label_placement;
|
||||
label_placement_enum how_placed = text_props_->label_placement;
|
||||
if (how_placed == LINE_PLACEMENT)
|
||||
{
|
||||
point_placement_ = false;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#include <mapnik/config_error.hpp>
|
||||
#include <mapnik/text/properties_util.hpp>
|
||||
#include <mapnik/boolean.hpp>
|
||||
|
||||
#include <mapnik/make_unique.hpp>
|
||||
// boost
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
|
@ -41,51 +41,39 @@ namespace mapnik
|
|||
using boost::optional;
|
||||
|
||||
text_symbolizer_properties::text_symbolizer_properties()
|
||||
: label_placement(POINT_PLACEMENT),
|
||||
label_spacing(0.0),
|
||||
label_position_tolerance(0.0),
|
||||
avoid_edges(false),
|
||||
margin(0.0),
|
||||
repeat_distance(0.0),
|
||||
minimum_distance(0.0),
|
||||
minimum_padding(0.0),
|
||||
minimum_path_length(0.0),
|
||||
max_char_angle_delta(22.5 * M_PI/180.0),
|
||||
allow_overlap(false),
|
||||
largest_bbox_only(true),
|
||||
upright(UPRIGHT_AUTO),
|
||||
layout_defaults(),
|
||||
: layout_defaults(),
|
||||
format_defaults(),
|
||||
tree_() {}
|
||||
|
||||
|
||||
void text_symbolizer_properties::evaluate_text_properties(feature_impl const& feature, attributes const& attrs) //const
|
||||
evaluated_text_properties_ptr text_symbolizer_properties::evaluate_text_properties(feature_impl const& feature, attributes const& attrs) const
|
||||
{
|
||||
label_placement = util::apply_visitor(extract_value<label_placement_enum>(feature,attrs), expressions.label_placement);
|
||||
label_spacing = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_spacing);
|
||||
label_position_tolerance = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_position_tolerance);
|
||||
avoid_edges = util::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.avoid_edges);
|
||||
margin = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.margin);
|
||||
repeat_distance = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.repeat_distance);
|
||||
minimum_distance = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_distance);
|
||||
minimum_padding = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_padding);
|
||||
minimum_path_length = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_path_length);
|
||||
max_char_angle_delta = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.max_char_angle_delta) * M_PI/180;
|
||||
allow_overlap = util::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.allow_overlap);
|
||||
largest_bbox_only = util::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.largest_bbox_only);
|
||||
upright = util::apply_visitor(extract_value<text_upright_enum>(feature,attrs), expressions.upright);
|
||||
// evaluate text properties
|
||||
evaluated_text_properties_ptr prop = std::make_unique<detail::evaluated_text_properties>();
|
||||
prop->label_placement = util::apply_visitor(extract_value<label_placement_enum>(feature,attrs), expressions.label_placement);
|
||||
prop->label_spacing = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_spacing);
|
||||
prop->label_position_tolerance = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_position_tolerance);
|
||||
prop->avoid_edges = util::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.avoid_edges);
|
||||
prop->margin = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.margin);
|
||||
prop->repeat_distance = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.repeat_distance);
|
||||
prop->minimum_distance = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_distance);
|
||||
prop->minimum_padding = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_padding);
|
||||
prop->minimum_path_length = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_path_length);
|
||||
prop->max_char_angle_delta = util::apply_visitor(extract_value<value_double>(feature,attrs), expressions.max_char_angle_delta) * M_PI/180;
|
||||
prop->allow_overlap = util::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.allow_overlap);
|
||||
prop->largest_bbox_only = util::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.largest_bbox_only);
|
||||
prop->upright = util::apply_visitor(extract_value<text_upright_enum>(feature,attrs), expressions.upright);
|
||||
return prop;
|
||||
}
|
||||
|
||||
void text_symbolizer_properties::process(text_layout & output, feature_impl const& feature, attributes const& attrs) //const
|
||||
void text_symbolizer_properties::process(text_layout & output, feature_impl const& feature, attributes const& attrs) const
|
||||
{
|
||||
output.clear();
|
||||
|
||||
if (tree_)
|
||||
{
|
||||
evaluate_text_properties(feature, attrs);
|
||||
//evaluate format properties
|
||||
evaluated_format_properties_ptr format = std::make_shared<detail::evaluated_format_properties>();
|
||||
|
||||
format->text_size = util::apply_visitor(extract_value<value_double>(feature,attrs), format_defaults.text_size);
|
||||
format->character_spacing = util::apply_visitor(extract_value<value_double>(feature,attrs), format_defaults.character_spacing);
|
||||
format->line_spacing = util::apply_visitor(extract_value<value_double>(feature,attrs), format_defaults.line_spacing);
|
||||
|
|
Loading…
Add table
Reference in a new issue