mapnik/src/group/group_symbolizer_helper.cpp
2016-12-02 12:42:07 +01:00

203 lines
6.4 KiB
C++

/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2015 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/geometry.hpp>
#include <mapnik/vertex_processor.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/vertex_cache.hpp>
#include <mapnik/tolerance_iterator.hpp>
namespace mapnik { namespace detail {
template <typename Helper>
struct apply_find_line_placements : util::noncopyable
{
apply_find_line_placements(view_transform const& t, proj_transform const& prj_trans, Helper & helper)
: t_(t),
prj_trans_(prj_trans),
helper_(helper) {}
template <typename Adapter>
void operator() (Adapter & va) const
{
using vertex_adapter_type = Adapter;
using path_type = transform_path_adapter<view_transform, vertex_adapter_type>;
path_type path(t_, va, prj_trans_);
helper_.find_line_placements(path);
}
view_transform const& t_;
proj_transform const& prj_trans_;
Helper & helper_;
};
} // ns detail
group_symbolizer_helper::group_symbolizer_helper(
group_symbolizer const& sym, feature_impl const& feature,
attributes const& vars,
proj_transform const& prj_trans,
unsigned width, unsigned height, double scale_factor,
view_transform const& t, DetectorType & detector,
box2d<double> const& query_extent)
: base_symbolizer_helper(sym, feature, vars, 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
{
using apply_find_line_placements = detail::apply_find_line_placements<group_symbolizer_helper>;
for (auto const& geom : geometries_to_process_)
{
// TODO to support clipped geometries this needs to use
// vertex_converters
apply_find_line_placements apply(t_, prj_trans_, *this);
mapnik::util::apply_visitor(geometry::vertex_processor<apply_find_line_placements>(apply), geom);
}
}
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(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()) && 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(box2d<double> const& box, value_unicode_string const& repeat_key) const
{
if (!detector_.extent().intersects(box)
||
(text_props_->avoid_edges && !dims_.contains(box))
||
(text_props_->minimum_padding > 0 &&
!dims_.contains(box + (scale_factor_ * text_props_->minimum_padding)))
||
(!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, text_props_->margin * scale_factor_,
repeat_key, text_props_->repeat_distance * scale_factor_))))
)
{
return true;
}
return false;
}
double group_symbolizer_helper::get_spacing(double path_length) const
{
int num_labels = 1;
if (text_props_->label_spacing > 0)
{
num_labels = static_cast<int>(std::floor(
path_length / (text_props_->label_spacing * scale_factor_)));
}
if (num_labels <= 0)
{
num_labels = 1;
}
return path_length / num_labels;
}
} //namespace