Find placements and render in process_group_symbolizer.

Create a group_symbolizer_helper for group placments, and extract some code from
text_symbolizer_helper into a base class to share with group_symbolizer_helper.
Also, move tolerance_iterator into its own header file. Use helper in
process_group_symbolizer to find placement positions.
This commit is contained in:
Jordan Hollinger 2014-01-09 08:42:32 -05:00 committed by Jordan Hollinger
parent 1ccdc5b76d
commit 76329028d2
9 changed files with 652 additions and 243 deletions

View file

@ -24,8 +24,8 @@
#define MAPNIK_GROUP_LAYOUT_MANAGER_HPP
// mapnik
#include <mapnik/pixel_position.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/pixel_position.hpp>
#include <mapnik/group/group_layout.hpp>
// stl

View file

@ -0,0 +1,105 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef GROUP_SYMBOLIZER_HELPER_HPP
#define GROUP_SYMBOLIZER_HELPER_HPP
//mapnik
#include <mapnik/text/symbolizer_helpers.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/marker.hpp>
#include <mapnik/marker_cache.hpp>
#include <mapnik/text/placement_finder.hpp>
#include <mapnik/proj_transform.hpp>
#include <mapnik/ctrans.hpp>
namespace mapnik {
class label_collision_detector4;
typedef label_collision_detector4 DetectorType;
typedef std::list<pixel_position> pixel_position_list;
/** Helper object that does some of the GroupSymbolizer placement finding work. */
class group_symbolizer_helper : public base_symbolizer_helper
{
public:
struct box_element
{
box_element(box2d<double> const& box, value_unicode_string const& repeat_key = "")
: box_(box),
repeat_key_(repeat_key)
{}
box2d<double> box_;
value_unicode_string repeat_key_;
};
group_symbolizer_helper(group_symbolizer const& sym,
feature_impl const& feature,
proj_transform const& prj_trans,
unsigned width,
unsigned height,
double scale_factor,
CoordTransform const &t,
DetectorType &detector,
box2d<double> const& query_extent);
inline void add_box_element(box2d<double> const& box, value_unicode_string const& repeat_key = "")
{
box_elements_.push_back(box_element(box, repeat_key));
}
inline void clear_box_elements()
{
box_elements_.clear();
}
inline text_symbolizer_properties const& get_properties()
{
return placement_->properties;
}
pixel_position_list const& get();
private:
/** Iterate over the given path, placing line-following labels or point labels with respect to label_spacing. */
template <typename T>
bool find_line_placements(T & path);
/** Check if a point placement fits at given position */
bool check_point_placement(pixel_position const& pos);
/** Checks for collision. */
bool collision(box2d<double> const& box, const value_unicode_string &repeat_key = "") const;
double get_spacing(double path_length) const;
DetectorType &detector_;
/** Boxes and repeat keys to take into account when finding placement.
* Boxes are relative to starting point of current placement.
*/
std::list<box_element> box_elements_;
pixel_position_list results_;
};
} //namespace
#endif // GROUP_SYMBOLIZER_HELPER_HPP

View file

@ -33,10 +33,52 @@
namespace mapnik {
class base_symbolizer_helper
{
public:
base_symbolizer_helper(symbolizer_base const& sym,
feature_impl const& feature,
proj_transform const& prj_trans,
unsigned width,
unsigned height,
double scale_factor,
CoordTransform const& t,
box2d<double> const& query_extent);
protected:
void initialize_geometries();
void initialize_points();
//Input
symbolizer_base const& sym_;
feature_impl const& feature_;
proj_transform const& prj_trans_;
CoordTransform const& t_;
box2d<double> dims_;
box2d<double> const& query_extent_;
float scale_factor_;
bool clipped_;
//Processing
/* Using list instead of vector, because we delete random elements and need iterators to stay valid. */
/** Remaining geometries to be processed. */
std::list<geometry_type*> geometries_to_process_;
/** Remaining points to be processed. */
std::list<pixel_position> points_;
/** Geometry currently being processed. */
std::list<geometry_type*>::iterator geo_itr_;
/** Point currently being processed. */
std::list<pixel_position>::iterator point_itr_;
/** Use point placement. Otherwise line placement is used. */
bool point_placement_;
text_placement_info_ptr placement_;
};
/** Helper object that does all the TextSymbolizer placement finding
* work except actually rendering the object. */
class text_symbolizer_helper
class text_symbolizer_helper : public base_symbolizer_helper
{
public:
template <typename FaceManagerT, typename DetectorT>
@ -68,34 +110,12 @@ public:
protected:
bool next_point_placement();
bool next_line_placement(bool clipped);
void initialize_geometries();
void initialize_points();
//Input
text_symbolizer const& sym_;
feature_impl const& feature_;
proj_transform const& prj_trans_;
CoordTransform const& t_;
box2d<double> dims_;
box2d<double> const& query_extent_;
//Processing
/* Using list instead of vector, because we delete random elements and need iterators to stay valid. */
/** Remaining geometries to be processed. */
std::list<geometry_type*> geometries_to_process_;
/** Geometry currently being processed. */
std::list<geometry_type*>::iterator geo_itr_;
/** Remaining points to be processed. */
std::list<pixel_position> points_;
/** Point currently being processed. */
std::list<pixel_position>::iterator point_itr_;
/** Use point placement. Otherwise line placement is used. */
bool point_placement_;
placement_finder finder_;
/** Place text at points on a line instead of following the line (used for ShieldSymbolizer) .*/
bool points_on_line_;
text_placement_info_ptr placement_;
placement_finder finder_;
//ShieldSymbolizer only
void init_marker();
};

View file

@ -0,0 +1,100 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#ifndef MAPNIK_TOLERANCE_ITERATOR_HPP
#define MAPNIK_TOLERANCE_ITERATOR_HPP
//mapnik
#include <mapnik/debug.hpp>
namespace mapnik
{
class tolerance_iterator
{
public:
tolerance_iterator(double label_position_tolerance, double spacing)
: tolerance_(label_position_tolerance > 0 ?
label_position_tolerance : spacing/2.0),
tolerance_delta_(std::max(1.0, tolerance_/100.0)),
value_(0),
initialized_(false),
values_tried_(0)
{
}
~tolerance_iterator()
{
//std::cout << "values tried:" << values_tried_ << "\n";
}
double get() const
{
return -value_;
}
bool next()
{
++values_tried_;
if (values_tried_ > 255)
{
/* This point should not be reached during normal operation. But I can think of
* cases where very bad spacing and or tolerance values are choosen and the
* placement finder tries an excessive number of placements.
* 255 is an arbitrarily chosen limit.
*/
MAPNIK_LOG_WARN(placement_finder) << "Tried a huge number of placements. Please check "
"'label-position-tolerance' and 'spacing' parameters "
"of your TextSymbolizers.\n";
return false;
}
if (!initialized_)
{
initialized_ = true;
return true; //Always return value 0 as the first value.
}
if (value_ == 0)
{
value_ = tolerance_delta_;
return true;
}
value_ = -value_;
if (value_ > 0)
{
value_ += tolerance_delta_;
}
if (value_ > tolerance_)
{
return false;
}
return true;
}
private:
double tolerance_;
double tolerance_delta_;
double value_;
bool initialized_;
unsigned values_tried_;
};
}//ns mapnik
#endif // MAPNIK_TOLERANCE_ITERATOR_HPP

View file

@ -27,10 +27,12 @@
#include <mapnik/image_util.hpp>
#include <mapnik/feature_factory.hpp>
#include <mapnik/attribute_collector.hpp>
#include <mapnik/text/layout.hpp>
#include <mapnik/group/group_layout_manager.hpp>
#include <mapnik/text/placement_finder.hpp>
#include <mapnik/text/layout.hpp>
#include <mapnik/text/renderer.hpp>
#include <mapnik/group/group_layout_manager.hpp>
#include <mapnik/group/group_symbolizer_helper.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/symbolizer.hpp>
@ -194,7 +196,14 @@ private:
{
for (auto const &label : detector)
{
box_.expand_to_include(label.box);
if (box_.width() > 0 && box_.height() > 0)
{
box_.expand_to_include(label.box);
}
else
{
box_ = label.box;
}
}
}
};
@ -214,7 +223,7 @@ struct thunk_renderer : public boost::static_visitor<>
thunk_renderer(renderer_type &ren,
buffer_type *buf,
renderer_common &common,
layout_offset const &offset)
pixel_position const &offset)
: ren_(ren), buf_(buf), common_(common), offset_(offset)
{}
@ -245,7 +254,7 @@ struct thunk_renderer : public boost::static_visitor<>
glyphs->set_marker(marker_info, new_marker_pos);
}
ren.render(*glyphs);
//ren.render(*glyphs); // <--- TODO: This causes seg fault?
}
}
@ -259,7 +268,7 @@ private:
renderer_type &ren_;
buffer_type *buf_;
renderer_common &common_;
layout_offset offset_;
pixel_position offset_;
};
geometry_type *origin_point(proj_transform const &prj_trans,
@ -282,6 +291,8 @@ void agg_renderer<T0,T1>::process(group_symbolizer const& sym,
mapnik::feature_impl & feature,
proj_transform const& prj_trans)
{
box2d<double> clip_box = clipping_extent();
// find all column names referenced in the group rules and symbolizers
std::set<std::string> columns;
attribute_collector column_collector(columns);
@ -384,8 +395,6 @@ void agg_renderer<T0,T1>::process(group_symbolizer const& sym,
sub_feature->add_geometry(origin_point(prj_trans, common_));
// get the layout for this set of properties
group_layout const &layout = props->get_layout();
for (auto const& rule : props->get_rules())
{
if (boost::apply_visitor(evaluate<Feature,value_type>(*sub_feature),
@ -398,7 +407,7 @@ void agg_renderer<T0,T1>::process(group_symbolizer const& sym,
bound_box bounds;
render_thunk_list thunks;
extract_bboxes extractor(bounds, thunks, *sub_feature, prj_trans,
common_, text, clipping_extent());
common_, text, clip_box);
for (auto const& sym : *rule)
{
@ -415,23 +424,56 @@ void agg_renderer<T0,T1>::process(group_symbolizer const& sym,
}
}
// TODO: run placement_finder to get placements
// create a symbolizer helper
group_symbolizer_helper helper(sym, feature, prj_trans,
common_.width_, common_.height_,
common_.scale_factor_, common_.t_,
*common_.detector_, clip_box);
// NOTE that the placement finder will have already inserted bboxes
// for the found placements, so we don't need to do that again.
// determine if we should be tracking repeat distance
bool check_repeat = (helper.get_properties().minimum_distance > 0);
// TODO: foreach placement
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)
{
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>(*match_feature), *rpt_key_expr).to_unicode();
}
helper.add_box_element(layout_manager.offset_box_at(i), rpt_key_value);
}
else
{
helper.add_box_element(layout_manager.offset_box_at(i));
}
}
pixel_position_list positions = helper.get();
for (pixel_position const& pos : positions)
{
pixel_position pos; // <-- pixel position given by placement_finder
for (size_t layout_i = 0; layout_i < num_layout_thunks; ++layout_i)
{
layout_offset offset = layout_manager.offset_at(layout_i);
const pixel_position &offset = layout_manager.offset_at(layout_i);
pixel_position ren_pos = pos + offset;
// to get actual placement, need to shift original bbox by layout
// offset and placement finder position (assuming origin of
// layout offsets is 0,0.
offset += layout_offset(pos.x, pos.y);
thunk_renderer ren(*this, current_buffer_, common_, offset);
thunk_renderer ren(*this, current_buffer_, common_, ren_pos);
for (render_thunk_ptr &thunk : layout_thunks[layout_i])
{

View file

@ -235,6 +235,7 @@ source = Split(
text/placements/simple.cpp
group/group_layout_manager.cpp
group/group_rule.cpp
group/group_symbolizer_helper.cpp
xml_tree.cpp
config_error.cpp
color_factory.cpp

View file

@ -0,0 +1,196 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2013 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
// mapnik
#include <mapnik/group/group_symbolizer_helper.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/geom_util.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/text/placements/base.hpp>
#include <mapnik/text/placements/dummy.hpp>
#include <mapnik/text/vertex_cache.hpp>
#include <mapnik/text/tolerance_iterator.hpp>
//agg
#include "agg_conv_clip_polyline.h"
namespace mapnik {
group_symbolizer_helper::group_symbolizer_helper(
const group_symbolizer &sym, const feature_impl &feature,
const proj_transform &prj_trans,
unsigned width, unsigned height, double scale_factor,
const CoordTransform &t, DetectorType &detector,
const box2d<double> &query_extent)
: base_symbolizer_helper(sym, feature, prj_trans, width, height, scale_factor, t, query_extent),
detector_(detector)
{}
pixel_position_list const& group_symbolizer_helper::get()
{
results_.clear();
if (point_placement_)
{
for (pixel_position const& point : points_)
{
check_point_placement(point);
}
}
else
{
for (auto const& geom : geometries_to_process_)
{
if (clipped_)
{
typedef agg::conv_clip_polyline<geometry_type> clipped_geometry_type;
typedef coord_transform<CoordTransform,clipped_geometry_type> path_type;
clipped_geometry_type clipped(*geom);
clipped.clip_box(query_extent_.minx(), query_extent_.miny(),
query_extent_.maxx(), query_extent_.maxy());
path_type path(t_, clipped, prj_trans_);
find_line_placements(path);
}
else
{
typedef coord_transform<CoordTransform,geometry_type> path_type;
path_type path(t_, *geom, prj_trans_);
find_line_placements(path);
}
}
}
return results_;
}
template <typename T>
bool group_symbolizer_helper::find_line_placements(T & path)
{
if (box_elements_.empty()) return true;
vertex_cache pp(path);
bool success = false;
while (pp.next_subpath())
{
if (pp.length() <= 0.001)
{
success = check_point_placement(pp.current_position()) || success;
continue;
}
double spacing = get_spacing(pp.length());
pp.forward(spacing/2.0);
do
{
tolerance_iterator tolerance_offset(placement_->properties.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()) && check_point_placement(pp.current_position()))
{
success = true;
break;
}
}
} while (pp.forward(spacing));
}
return success;
}
bool group_symbolizer_helper::check_point_placement(pixel_position const& pos)
{
if (box_elements_.empty()) return false;
// offset boxes and check collision
std::list< box2d<double> > real_boxes;
for (auto const& box_elem : box_elements_)
{
box2d<double> real_box = box2d<double>(box_elem.box_);
real_box.move(pos.x, pos.y);
if (collision(real_box, box_elem.repeat_key_))
{
return false;
}
real_boxes.push_back(real_box);
}
// add boxes to collision detector
std::list<box_element>::iterator elem_itr = box_elements_.begin();
std::list< box2d<double> >::iterator real_itr = real_boxes.begin();
while (elem_itr != box_elements_.end() && real_itr != real_boxes.end())
{
detector_.insert(*real_itr, elem_itr->repeat_key_);
elem_itr++;
real_itr++;
}
results_.push_back(pos);
return true;
}
bool group_symbolizer_helper::collision(const box2d<double> &box, const value_unicode_string &repeat_key) const
{
if (!detector_.extent().intersects(box)
||
(placement_->properties.avoid_edges && !query_extent_.contains(box))
||
(placement_->properties.minimum_padding > 0 &&
!query_extent_.contains(box + (scale_factor_ * placement_->properties.minimum_padding)))
||
(!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, repeat_key, placement_->properties.minimum_distance * scale_factor_))))
)
{
return true;
}
return false;
}
double group_symbolizer_helper::get_spacing(double path_length) const
{
int num_labels = 1;
if (placement_->properties.label_spacing > 0)
{
num_labels = static_cast<int>(floor(
path_length / (placement_->properties.label_spacing * scale_factor_)));
}
if (placement_->properties.force_odd_labels && num_labels % 2 == 0)
{
--num_labels;
}
if (num_labels <= 0)
{
num_labels = 1;
}
return path_length / num_labels;
}
} //namespace

View file

@ -29,6 +29,7 @@
#include <mapnik/text/text_properties.hpp>
#include <mapnik/text/placements_list.hpp>
#include <mapnik/text/vertex_cache.hpp>
#include <mapnik/text/tolerance_iterator.hpp>
// agg
#include "agg_conv_clip_polyline.h"
@ -39,74 +40,6 @@
namespace mapnik
{
class tolerance_iterator
{
public:
tolerance_iterator(double label_position_tolerance, double spacing)
: tolerance_(label_position_tolerance > 0 ?
label_position_tolerance : spacing/2.0),
tolerance_delta_(std::max(1.0, tolerance_/100.0)),
value_(0),
initialized_(false),
values_tried_(0)
{
}
~tolerance_iterator()
{
//std::cout << "values tried:" << values_tried_ << "\n";
}
double get() const
{
return -value_;
}
bool next()
{
++values_tried_;
if (values_tried_ > 255)
{
/* This point should not be reached during normal operation. But I can think of
* cases where very bad spacing and or tolerance values are choosen and the
* placement finder tries an excessive number of placements.
* 255 is an arbitrarily chosen limit.
*/
MAPNIK_LOG_WARN(placement_finder) << "Tried a huge number of placements. Please check "
"'label-position-tolerance' and 'spacing' parameters "
"of your TextSymbolizers.\n";
return false;
}
if (!initialized_)
{
initialized_ = true;
return true; //Always return value 0 as the first value.
}
if (value_ == 0)
{
value_ = tolerance_delta_;
return true;
}
value_ = -value_;
if (value_ > 0)
{
value_ += tolerance_delta_;
}
if (value_ > tolerance_)
{
return false;
}
return true;
}
private:
double tolerance_;
double tolerance_delta_;
double value_;
bool initialized_;
unsigned values_tried_;
};
// Output is centered around (0,0)
static void rotated_box2d(box2d<double> & box, rotation const& rot, double width, double height)
{

View file

@ -40,24 +40,150 @@
namespace mapnik {
template <typename FaceManagerT, typename DetectorT>
text_symbolizer_helper::text_symbolizer_helper(const text_symbolizer &sym, const feature_impl &feature, const proj_transform &prj_trans, unsigned width, unsigned height, double scale_factor, const CoordTransform &t, FaceManagerT &font_manager, DetectorT &detector, const box2d<double> &query_extent)
base_symbolizer_helper::base_symbolizer_helper(
const symbolizer_base &sym, const feature_impl &feature,
const proj_transform &prj_trans,
unsigned width, unsigned height, double scale_factor,
const CoordTransform &t, const box2d<double> &query_extent)
: sym_(sym),
feature_(feature),
prj_trans_(prj_trans),
t_(t),
dims_(0, 0, width, height),
query_extent_(query_extent),
points_on_line_(false),
placement_(mapnik::get<text_placements_ptr>(sym_, keys::text_placements_)->get_placement_info(scale_factor)),
finder_(feature, detector, dims_, placement_, font_manager, scale_factor)
scale_factor_(scale_factor),
clipped_(mapnik::get<bool>(sym_, keys::clip, feature_, true /*TODO*/)),
placement_(mapnik::get<text_placements_ptr>(sym_, keys::text_placements_)->get_placement_info(scale_factor))
{
initialize_geometries();
if (!geometries_to_process_.size()) return;
finder_.next_position();
initialize_points();
}
struct largest_bbox_first
{
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();
}
};
void base_symbolizer_helper::initialize_geometries()
{
// FIXME
bool largest_box_only = false;//get<value_bool>(sym_, keys::largest_box_only);
double minimum_path_length = 0; // get<value_double>(sym_, keys::minimum_path_length);
for ( auto const& geom : feature_.paths())
{
// don't bother with empty geometries
if (geom.size() == 0) continue;
mapnik::geometry_type::types type = geom.type();
if (type == geometry_type::types::Polygon)
{
if (minimum_path_length > 0)
{
box2d<double> gbox = t_.forward(geom.envelope(), prj_trans_);
if (gbox.width() < minimum_path_length)
{
continue;
}
}
}
// TODO - calculate length here as well
geometries_to_process_.push_back(const_cast<geometry_type*>(&geom));
}
if (largest_box_only)
{
geometries_to_process_.sort(largest_bbox_first());
geo_itr_ = geometries_to_process_.begin();
geometries_to_process_.erase(++geo_itr_,geometries_to_process_.end());
}
geo_itr_ = geometries_to_process_.begin();
}
void base_symbolizer_helper::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.size(); i++)
{
geom.vertex(&label_x, &label_y);
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(pixel_position(label_x, label_y));
}
}
else
{
// https://github.com/mapnik/mapnik/issues/1423
bool success = false;
// https://github.com/mapnik/mapnik/issues/1350
if (geom.type() == geometry_type::types::LineString)
{
success = label::middle_point(geom, label_x,label_y);
}
else if (how_placed == POINT_PLACEMENT)
{
success = label::centroid(geom, label_x, label_y);
}
else if (how_placed == INTERIOR_PLACEMENT)
{
success = label::interior_position(geom, label_x, label_y);
}
else
{
MAPNIK_LOG_ERROR(symbolizer_helpers) << "ERROR: Unknown placement type in initialize_points()";
}
if (success)
{
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(pixel_position(label_x, label_y));
}
}
}
point_itr_ = points_.begin();
}
template <typename FaceManagerT, typename DetectorT>
text_symbolizer_helper::text_symbolizer_helper(
const text_symbolizer &sym, const feature_impl &feature,
const proj_transform &prj_trans,
unsigned width, unsigned height, double scale_factor,
const CoordTransform &t, FaceManagerT &font_manager,
DetectorT &detector, const box2d<double> &query_extent)
: base_symbolizer_helper(sym, feature, prj_trans, width, height, scale_factor, t, query_extent),
finder_(feature, detector, dims_, placement_, font_manager, scale_factor),
points_on_line_(false)
{
if (geometries_to_process_.size()) finder_.next_position();
}
placements_list const& text_symbolizer_helper::get()
{
if (point_placement_)
@ -66,7 +192,7 @@ placements_list const& text_symbolizer_helper::get()
}
else
{
while (next_line_placement(mapnik::get<bool>(sym_, keys::clip, feature_, true /*TODO*/)));
while (next_line_placement(clipped_));
}
return finder_.placements();
}
@ -137,116 +263,6 @@ bool text_symbolizer_helper::next_point_placement()
return false;
}
struct largest_bbox_first
{
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();
}
};
void text_symbolizer_helper::initialize_geometries()
{
// FIXME
bool largest_box_only = false;//get<value_bool>(sym_, keys::largest_box_only);
double minimum_path_length = 0; // get<value_double>(sym_, keys::minimum_path_length);
for ( auto const& geom : feature_.paths())
{
// don't bother with empty geometries
if (geom.size() == 0) continue;
mapnik::geometry_type::types type = geom.type();
if (type == geometry_type::types::Polygon)
{
if (minimum_path_length > 0)
{
box2d<double> gbox = t_.forward(geom.envelope(), prj_trans_);
if (gbox.width() < minimum_path_length)
{
continue;
}
}
}
// TODO - calculate length here as well
geometries_to_process_.push_back(const_cast<geometry_type*>(&geom));
}
if (largest_box_only)
{
geometries_to_process_.sort(largest_bbox_first());
geo_itr_ = geometries_to_process_.begin();
geometries_to_process_.erase(++geo_itr_,geometries_to_process_.end());
}
geo_itr_ = geometries_to_process_.begin();
}
void text_symbolizer_helper::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.size(); i++)
{
geom.vertex(&label_x, &label_y);
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(pixel_position(label_x, label_y));
}
}
else
{
// https://github.com/mapnik/mapnik/issues/1423
bool success = false;
// https://github.com/mapnik/mapnik/issues/1350
if (geom.type() == geometry_type::types::LineString)
{
success = label::middle_point(geom, label_x,label_y);
}
else if (how_placed == POINT_PLACEMENT)
{
success = label::centroid(geom, label_x, label_y);
}
else if (how_placed == INTERIOR_PLACEMENT)
{
success = label::interior_position(geom, label_x, label_y);
}
else
{
MAPNIK_LOG_ERROR(symbolizer_helpers) << "ERROR: Unknown placement type in initialize_points()";
}
if (success)
{
prj_trans_.backward(label_x, label_y, z);
t_.forward(&label_x, &label_y);
points_.push_back(pixel_position(label_x, label_y));
}
}
}
point_itr_ = points_.begin();
}
/*****************************************************************************/
template <typename FaceManagerT, typename DetectorT>
@ -256,21 +272,15 @@ text_symbolizer_helper::text_symbolizer_helper(
unsigned width, unsigned height, double scale_factor,
const CoordTransform &t, FaceManagerT &font_manager,
DetectorT &detector, const box2d<double> &query_extent)
: sym_(sym),
feature_(feature),
prj_trans_(prj_trans),
t_(t),
dims_(0, 0, width, height),
query_extent_(query_extent),
points_on_line_(true),
placement_(mapnik::get<text_placements_ptr>(sym_, keys::text_placements_)->get_placement_info(scale_factor)),
finder_(feature, detector, dims_, placement_, font_manager, scale_factor)
: base_symbolizer_helper(sym, feature, prj_trans, width, height, scale_factor, t, query_extent),
finder_(feature, detector, dims_, placement_, font_manager, scale_factor),
points_on_line_(true)
{
initialize_geometries();
if (!geometries_to_process_.size()) return;
finder_.next_position();
initialize_points();
init_marker();
if (geometries_to_process_.size())
{
init_marker();
finder_.next_position();
}
}
@ -316,6 +326,8 @@ void text_symbolizer_helper::init_marker()
finder_.set_marker(std::make_shared<marker_info>(m, trans), bbox, unlock_image, marker_displacement);
}
/*****************************************************************************/
template text_symbolizer_helper::text_symbolizer_helper(const text_symbolizer &sym,
const feature_impl &feature,
const proj_transform &prj_trans,