Add repeat-distance to text symbolizer properties.

Update collision detector to handle minimum-distance and repeat-distance seperately.
Update placement_finder to use repeat-distance.
Update group symbolizer to handle minimum-distance and repeat-distance.
This commit is contained in:
jhollinger2 2014-08-03 16:10:21 -04:00
parent 9f741c21b5
commit 0aad860a75
10 changed files with 72 additions and 50 deletions

View file

@ -424,6 +424,7 @@ void export_text_placement()
.def_readwrite("label_position_tolerance", &text_symbolizer_properties::label_position_tolerance) .def_readwrite("label_position_tolerance", &text_symbolizer_properties::label_position_tolerance)
.def_readwrite("avoid_edges", &text_symbolizer_properties::avoid_edges) .def_readwrite("avoid_edges", &text_symbolizer_properties::avoid_edges)
.def_readwrite("minimum_distance", &text_symbolizer_properties::minimum_distance) .def_readwrite("minimum_distance", &text_symbolizer_properties::minimum_distance)
.def_readwrite("repeat_distance", &text_symbolizer_properties::repeat_distance)
.def_readwrite("minimum_padding", &text_symbolizer_properties::minimum_padding) .def_readwrite("minimum_padding", &text_symbolizer_properties::minimum_padding)
.def_readwrite("minimum_path_length", &text_symbolizer_properties::minimum_path_length) .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("maximum_angle_char_delta", &text_symbolizer_properties::max_char_angle_delta)

View file

@ -158,29 +158,47 @@ public:
return true; return true;
} }
bool has_placement(box2d<double> const& box, mapnik::value_unicode_string const& text, double distance) bool has_placement(box2d<double> const& box, double minimum_distance)
{ {
box2d<double> bigger_box(box.minx() - distance, box.miny() - distance, box.maxx() + distance, box.maxy() + distance); box2d<double> const& minimum_box = (minimum_distance > 0
tree_t::query_iterator itr = tree_.query_in_box(bigger_box); ? box2d<double>(box.minx() - minimum_distance, box.miny() - minimum_distance,
box.maxx() + minimum_distance, box.maxy() + minimum_distance)
: box);
tree_t::query_iterator itr = tree_.query_in_box(minimum_box);
tree_t::query_iterator end = tree_.query_end(); tree_t::query_iterator end = tree_.query_end();
for (;itr != end; ++itr) for (;itr != end; ++itr)
{ {
if (itr->box.intersects(box) || (text == itr->text && itr->box.intersects(bigger_box))) if (itr->box.intersects(minimum_box))
{
return false; return false;
}
} }
return true; return true;
} }
bool has_point_placement(box2d<double> const& box, double distance) bool has_placement(box2d<double> const& box, double minimum_distance, mapnik::value_unicode_string const& text, double repeat_distance)
{ {
box2d<double> bigger_box(box.minx() - distance, box.miny() - distance, box.maxx() + distance, box.maxy() + distance); box2d<double> const& minimum_box = (minimum_distance > 0
tree_t::query_iterator itr = tree_.query_in_box(bigger_box); ? box2d<double>(box.minx() - minimum_distance, box.miny() - minimum_distance,
box.maxx() + minimum_distance, box.maxy() + minimum_distance)
: box);
box2d<double> const& repeat_box = (repeat_distance > 0
? box2d<double>(box.minx() - repeat_distance, box.miny() - repeat_distance,
box.maxx() + repeat_distance, box.maxy() + repeat_distance)
: box);
tree_t::query_iterator itr = tree_.query_in_box(repeat_distance > minimum_distance ? repeat_box : minimum_box);
tree_t::query_iterator end = tree_.query_end(); tree_t::query_iterator end = tree_.query_end();
for ( ;itr != end; ++itr) for ( ;itr != end; ++itr)
{ {
if (itr->box.intersects(bigger_box)) return false; if (itr->box.intersects(minimum_box) || (text == itr->text && itr->box.intersects(repeat_box)))
{
return false;
}
} }
return true; return true;

View file

@ -297,38 +297,28 @@ void render_group_symbolizer(group_symbolizer const& sym,
common.scale_factor_, common.t_, common.scale_factor_, common.t_,
*common.detector_, clipping_extent); *common.detector_, clipping_extent);
// determine if we should be tracking repeat distance
bool check_repeat = (helper.get_properties().minimum_distance > 0);
for (size_t i = 0; i < matches.size(); ++i) for (size_t i = 0; i < matches.size(); ++i)
{ {
if (check_repeat) group_rule_ptr match_rule = matches[i].first;
feature_ptr match_feature = matches[i].second;
value_unicode_string rpt_key_value = "";
// get repeat key from matched group rule
expression_ptr rpt_key_expr = match_rule->get_repeat_key();
// if no repeat key was defined, use default from group symbolizer
if (!rpt_key_expr)
{ {
group_rule_ptr match_rule = matches[i].first; rpt_key_expr = get<expression_ptr>(sym, keys::repeat_key);
feature_ptr match_feature = matches[i].second;
value_unicode_string rpt_key_value = "";
// get repeat key from matched group rule
expression_ptr rpt_key_expr = match_rule->get_repeat_key();
// if no repeat key was defined, use default from group symbolizer
if (!rpt_key_expr)
{
rpt_key_expr = get<expression_ptr>(sym, keys::repeat_key);
}
// evalute the repeat key with the matched sub feature if we have one
if (rpt_key_expr)
{
rpt_key_value = boost::apply_visitor(evaluate<Feature,value_type,attributes>(*match_feature,common.vars_),
*rpt_key_expr).to_unicode();
}
helper.add_box_element(layout_manager.offset_box_at(i), rpt_key_value);
} }
else
// evalute the repeat key with the matched sub feature if we have one
if (rpt_key_expr)
{ {
helper.add_box_element(layout_manager.offset_box_at(i)); rpt_key_value = boost::apply_visitor(evaluate<Feature,value_type,attributes>(*match_feature,common.vars_),
*rpt_key_expr).to_unicode();
} }
helper.add_box_element(layout_manager.offset_box_at(i), rpt_key_value);
} }
pixel_position_list positions = helper.get(); pixel_position_list positions = helper.get();

View file

@ -72,7 +72,7 @@ private:
// Adjusts user defined spacing to place an integer number of labels. // Adjusts user defined spacing to place an integer number of labels.
double get_spacing(double path_length, double layout_width) const; double get_spacing(double path_length, double layout_width) const;
// Checks for collision. // Checks for collision.
bool collision(box2d<double> const& box) const; bool collision(box2d<double> const& box, const value_unicode_string &repeat_key = "") const;
// Adds marker to glyph_positions and to collision detector. Returns false if there is a collision. // Adds marker to glyph_positions and to collision detector. Returns false if there is a collision.
bool add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const; bool add_marker(glyph_positions_ptr glyphs, pixel_position const& pos) const;
// Maps upright==auto, left_only and right_only to left,right to simplify processing. // Maps upright==auto, left_only and right_only to left,right to simplify processing.

View file

@ -128,6 +128,7 @@ struct text_properties_expressions
symbolizer_base::value_type label_spacing = 0.0; symbolizer_base::value_type label_spacing = 0.0;
symbolizer_base::value_type label_position_tolerance = 0.0; symbolizer_base::value_type label_position_tolerance = 0.0;
symbolizer_base::value_type avoid_edges = false; symbolizer_base::value_type avoid_edges = false;
symbolizer_base::value_type repeat_distance = 0.0;
symbolizer_base::value_type minimum_distance = 0.0; symbolizer_base::value_type minimum_distance = 0.0;
symbolizer_base::value_type minimum_padding = 0.0; symbolizer_base::value_type minimum_padding = 0.0;
symbolizer_base::value_type minimum_path_length = 0.0; symbolizer_base::value_type minimum_path_length = 0.0;
@ -170,6 +171,7 @@ struct MAPNIK_DECL text_symbolizer_properties
// distance the label can be moved on the line to fit, if 0 the default is used // distance the label can be moved on the line to fit, if 0 the default is used
double label_position_tolerance; double label_position_tolerance;
bool avoid_edges; bool avoid_edges;
double repeat_distance;
double minimum_distance; double minimum_distance;
double minimum_padding; double minimum_padding;
double minimum_path_length; double minimum_path_length;

View file

@ -164,9 +164,10 @@ bool group_symbolizer_helper::collision(const box2d<double> &box, const value_un
!query_extent_.contains(box + (scale_factor_ * placement_->properties.minimum_padding))) !query_extent_.contains(box + (scale_factor_ * placement_->properties.minimum_padding)))
|| ||
(!placement_->properties.allow_overlap && (!placement_->properties.allow_overlap &&
((repeat_key.length() == 0 && !detector_.has_point_placement(box, placement_->properties.minimum_distance * scale_factor_)) ((repeat_key.length() == 0 && !detector_.has_placement(box, placement_->properties.minimum_distance * scale_factor_))
|| ||
(repeat_key.length() > 0 && !detector_.has_placement(box, repeat_key, placement_->properties.minimum_distance * scale_factor_)))) (repeat_key.length() > 0 && !detector_.has_placement(box, placement_->properties.minimum_distance * scale_factor_,
repeat_key, placement_->properties.repeat_distance * scale_factor_))))
) )
{ {
return true; return true;

View file

@ -120,8 +120,8 @@ bool placement_finder::find_point_placement(pixel_position const& pos)
box2d<double> bbox = layout.bounds(); box2d<double> bbox = layout.bounds();
bbox.re_center(layout_center.x, layout_center.y); bbox.re_center(layout_center.x, layout_center.y);
// For point placements it is faster to just check the bounding box. /* For point placements it is faster to just check the bounding box. */
if (collision(bbox)) return false; if (collision(bbox, layouts_.text())) return false;
if (layout.num_lines()) bboxes.push_back(std::move(bbox)); if (layout.num_lines()) bboxes.push_back(std::move(bbox));
@ -309,7 +309,7 @@ bool placement_finder::single_line_placement(vertex_cache &pp, text_upright_e or
cluster_offset.y -= rot.sin * glyph.advance(); cluster_offset.y -= rot.sin * glyph.advance();
box2d<double> bbox = get_bbox(layout, glyph, pos, rot); box2d<double> bbox = get_bbox(layout, glyph, pos, rot);
if (collision(bbox)) return false; if (collision(bbox, layouts_.text())) return false;
bboxes.push_back(std::move(bbox)); bboxes.push_back(std::move(bbox));
glyphs->push_back(glyph, pos, rot); glyphs->push_back(glyph, pos, rot);
} }
@ -376,7 +376,7 @@ double placement_finder::get_spacing(double path_length, double layout_width) co
return path_length / num_labels; return path_length / num_labels;
} }
bool placement_finder::collision(const box2d<double> &box) const bool placement_finder::collision(const box2d<double> &box, const value_unicode_string &repeat_key) const
{ {
if (!detector_.extent().intersects(box) if (!detector_.extent().intersects(box)
|| ||
@ -386,7 +386,10 @@ bool placement_finder::collision(const box2d<double> &box) const
!extent_.contains(box + (scale_factor_ * info_.properties.minimum_padding))) !extent_.contains(box + (scale_factor_ * info_.properties.minimum_padding)))
|| ||
(!info_.properties.allow_overlap && (!info_.properties.allow_overlap &&
!detector_.has_point_placement(box, info_.properties.minimum_distance * scale_factor_)) ((repeat_key.length() == 0 && !detector_.has_placement(box, info_.properties.minimum_distance * scale_factor_))
||
(repeat_key.length() > 0 && !detector_.has_placement(box, info_.properties.minimum_distance * scale_factor_,
repeat_key, info_.properties.repeat_distance * scale_factor_))))
) )
{ {
return true; return true;
@ -410,7 +413,7 @@ bool placement_finder::add_marker(glyph_positions_ptr glyphs, pixel_position con
box2d<double> bbox = marker_box_; box2d<double> bbox = marker_box_;
bbox.move(real_pos.x, real_pos.y); bbox.move(real_pos.x, real_pos.y);
glyphs->set_marker(marker_, real_pos); glyphs->set_marker(marker_, real_pos);
if (collision(bbox)) return false; if (collision(bbox, layouts_.text())) return false;
detector_.insert(bbox); detector_.insert(bbox);
return true; return true;
} }

View file

@ -44,6 +44,7 @@ text_symbolizer_properties::text_symbolizer_properties()
label_spacing(0.0), label_spacing(0.0),
label_position_tolerance(0.0), label_position_tolerance(0.0),
avoid_edges(false), avoid_edges(false),
repeat_distance(0.0),
minimum_distance(0.0), minimum_distance(0.0),
minimum_padding(0.0), minimum_padding(0.0),
minimum_path_length(0.0), minimum_path_length(0.0),
@ -62,6 +63,7 @@ void text_symbolizer_properties::evaluate_text_properties(feature_impl const& fe
label_spacing = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_spacing); label_spacing = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_spacing);
label_position_tolerance = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_position_tolerance); label_position_tolerance = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.label_position_tolerance);
avoid_edges = boost::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.avoid_edges); avoid_edges = boost::apply_visitor(extract_value<value_bool>(feature,attrs), expressions.avoid_edges);
repeat_distance = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.repeat_distance);
minimum_distance = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_distance); minimum_distance = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_distance);
minimum_padding = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_padding); minimum_padding = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_padding);
minimum_path_length = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_path_length); minimum_path_length = boost::apply_visitor(extract_value<value_double>(feature,attrs), expressions.minimum_path_length);
@ -117,6 +119,7 @@ void text_symbolizer_properties::text_properties_from_xml(xml_node const& node)
set_property_from_xml<label_placement_e>(expressions.label_placement, "placement", node); set_property_from_xml<label_placement_e>(expressions.label_placement, "placement", node);
set_property_from_xml<value_double>(expressions.label_spacing, "spacing", node); set_property_from_xml<value_double>(expressions.label_spacing, "spacing", node);
set_property_from_xml<value_double>(expressions.label_position_tolerance, "label-position-tolerance", node); set_property_from_xml<value_double>(expressions.label_position_tolerance, "label-position-tolerance", node);
set_property_from_xml<value_double>(expressions.repeat_distance, "repeat-distance", node);
set_property_from_xml<value_double>(expressions.minimum_distance, "minimum-distance", node); set_property_from_xml<value_double>(expressions.minimum_distance, "minimum-distance", node);
set_property_from_xml<value_double>(expressions.minimum_padding, "minimum-padding", node); set_property_from_xml<value_double>(expressions.minimum_padding, "minimum-padding", node);
set_property_from_xml<value_double>(expressions.minimum_path_length, "minimum-path-length", node); set_property_from_xml<value_double>(expressions.minimum_path_length, "minimum-path-length", node);
@ -159,6 +162,10 @@ void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node,
{ {
serialize_property("spacing", expressions.label_spacing, node); serialize_property("spacing", expressions.label_spacing, node);
} }
if (!(expressions.repeat_distance == dfl.expressions.repeat_distance) || explicit_defaults)
{
serialize_property("repeat-distance", expressions.repeat_distance, node);
}
if (!(expressions.minimum_distance == dfl.expressions.minimum_distance) || explicit_defaults) if (!(expressions.minimum_distance == dfl.expressions.minimum_distance) || explicit_defaults)
{ {
serialize_property("minimum-distance", expressions.minimum_distance, node); serialize_property("minimum-distance", expressions.minimum_distance, node);

View file

@ -17,7 +17,7 @@ wkt,name1,ref1,name2,ref2
<Style name="alpha"> <Style name="alpha">
<Rule> <Rule>
<LineSymbolizer stroke-width="0.2" stroke="#ff0000"/> <LineSymbolizer stroke-width="0.2" stroke="#ff0000"/>
<GroupSymbolizer start-column="1" num-columns="2" placement="line" avoid-edges="false" minimum-distance="50" spacing="100" repeat-key="[name%]+'-'+[ref%]"> <GroupSymbolizer start-column="1" num-columns="2" placement="line" avoid-edges="false" repeat-distance="50" spacing="100" repeat-key="[name%]+'-'+[ref%]">
<PairLayout item-margin="1"/> <PairLayout item-margin="1"/>
<GroupRule> <GroupRule>
<ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="16" placement="point"> <ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="16" placement="point">
@ -43,7 +43,7 @@ wkt,name1,ref1,name2,ref2
<Style name="beta"> <Style name="beta">
<Rule> <Rule>
<LineSymbolizer stroke-width="0.2" stroke="#ff0000"/> <LineSymbolizer stroke-width="0.2" stroke="#ff0000"/>
<GroupSymbolizer start-column="1" num-columns="2" placement="line" avoid-edges="false" minimum-distance="40" spacing="80" repeat-key="[name%]+'-'+[ref%]"> <GroupSymbolizer start-column="1" num-columns="2" placement="line" avoid-edges="false" repeat-distance="40" spacing="80" repeat-key="[name%]+'-'+[ref%]">
<PairLayout item-margin="1"/> <PairLayout item-margin="1"/>
<GroupRule> <GroupRule>
<ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="16" placement="point"> <ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="16" placement="point">
@ -69,7 +69,7 @@ wkt,name1,ref1,name2,ref2
<Style name="gamma"> <Style name="gamma">
<Rule> <Rule>
<LineSymbolizer stroke-width="0.2" stroke="#ff0000"/> <LineSymbolizer stroke-width="0.2" stroke="#ff0000"/>
<GroupSymbolizer start-column="1" num-columns="2" placement="line" avoid-edges="false" minimum-distance="30" spacing="60" repeat-key="[name%]+'-'+[ref%]"> <GroupSymbolizer start-column="1" num-columns="2" placement="line" avoid-edges="false" repeat-distance="30" spacing="60" repeat-key="[name%]+'-'+[ref%]">
<PairLayout item-margin="1"/> <PairLayout item-margin="1"/>
<GroupRule> <GroupRule>
<ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="16" placement="point"> <ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="16" placement="point">

View file

@ -22,10 +22,10 @@ wkt,key1,key2
<Style name="alpha"> <Style name="alpha">
<Rule> <Rule>
<PointSymbolizer ignore-placement="true"/> <PointSymbolizer ignore-placement="true"/>
<GroupSymbolizer start-column="1" num-columns="1" placement="point" avoid-edges="false" minimum-distance="1" spacing="1" repeat-key="[key%]"> <GroupSymbolizer start-column="1" num-columns="1" placement="point" avoid-edges="false" repeat-key="[key%]">
<PairLayout item-margin="1"/> <PairLayout item-margin="1"/>
<GroupRule> <GroupRule>
<ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="10" placement="point" dy="0" fill="#ff0000">[key%]</ShieldSymbolizer> <ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="10" minimum-distance="1" placement="point" dy="0" fill="#ff0000">[key%]</ShieldSymbolizer>
</GroupRule> </GroupRule>
</GroupSymbolizer> </GroupSymbolizer>
</Rule> </Rule>
@ -47,7 +47,7 @@ wkt,name1,ref1,name2,ref2
<Style name="beta"> <Style name="beta">
<Rule> <Rule>
<GroupSymbolizer start-column="1" num-columns="1" placement="line" avoid-edges="false" minimum-distance="20" spacing="1" repeat-key="bar"> <GroupSymbolizer start-column="1" num-columns="1" placement="line" avoid-edges="false" repeat-distance="20" spacing="1" repeat-key="bar">
<PairLayout item-margin="1"/> <PairLayout item-margin="1"/>
<GroupRule> <GroupRule>
<ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="10" placement="point" dy="0" fill="#ffffff">bar</ShieldSymbolizer> <ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="10" placement="point" dy="0" fill="#ffffff">bar</ShieldSymbolizer>
@ -72,7 +72,7 @@ wkt,name1,ref1,name2,ref2
<Style name="gamma"> <Style name="gamma">
<Rule> <Rule>
<GroupSymbolizer start-column="1" num-columns="1" placement="line" avoid-edges="false" minimum-distance="20" spacing="1" repeat-key="foo"> <GroupSymbolizer start-column="1" num-columns="1" placement="line" avoid-edges="false" repeat-distance="20" spacing="1" repeat-key="foo">
<PairLayout item-margin="1"/> <PairLayout item-margin="1"/>
<GroupRule> <GroupRule>
<ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="10" placement="point" dy="0" fill="#000000">foo</ShieldSymbolizer> <ShieldSymbolizer file="../../data/svg/rect.svg" face-name="DejaVu Sans Book" size="10" placement="point" dy="0" fill="#000000">foo</ShieldSymbolizer>