Extracted a lot of code from agg render process_group_symbolizer.
Move a lot of processing into a common process_group_symbolizer function. Also, extract column collection out of process_group_symbolizer function. This will reduce duplication needed for other renderers.
This commit is contained in:
parent
efc29649ab
commit
3d1c30db1e
6 changed files with 539 additions and 428 deletions
|
@ -50,6 +50,8 @@
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
|
class group_attribute_collector;
|
||||||
|
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
struct expression_attributes : boost::static_visitor<void>
|
struct expression_attributes : boost::static_visitor<void>
|
||||||
{
|
{
|
||||||
|
@ -92,13 +94,28 @@ private:
|
||||||
Container& names_;
|
Container& names_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class group_attribute_collector : public mapnik::noncopyable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::set<std::string>& names_;
|
||||||
|
bool expand_index_columns_;
|
||||||
|
public:
|
||||||
|
group_attribute_collector(std::set<std::string>& names,
|
||||||
|
bool expand_index_columns)
|
||||||
|
: names_(names),
|
||||||
|
expand_index_columns_(expand_index_columns) {}
|
||||||
|
|
||||||
|
void operator() (group_symbolizer const& sym);
|
||||||
|
};
|
||||||
|
|
||||||
struct symbolizer_attributes : public boost::static_visitor<>
|
struct symbolizer_attributes : public boost::static_visitor<>
|
||||||
{
|
{
|
||||||
symbolizer_attributes(std::set<std::string>& names,
|
symbolizer_attributes(std::set<std::string>& names,
|
||||||
double & filter_factor)
|
double & filter_factor)
|
||||||
: names_(names),
|
: names_(names),
|
||||||
filter_factor_(filter_factor),
|
filter_factor_(filter_factor),
|
||||||
f_attr(names) {}
|
f_attr(names),
|
||||||
|
g_attr(names, true) {}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void operator () (T const&) const {}
|
void operator () (T const&) const {}
|
||||||
|
@ -226,12 +243,16 @@ struct symbolizer_attributes : public boost::static_visitor<>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator () (group_symbolizer const& sym);
|
void operator () (group_symbolizer const& sym)
|
||||||
|
{
|
||||||
|
g_attr(sym);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<std::string>& names_;
|
std::set<std::string>& names_;
|
||||||
double & filter_factor_;
|
double & filter_factor_;
|
||||||
expression_attributes<std::set<std::string> > f_attr;
|
expression_attributes<std::set<std::string> > f_attr;
|
||||||
|
group_attribute_collector g_attr;
|
||||||
void collect_transform(transform_list_ptr const& trans_expr)
|
void collect_transform(transform_list_ptr const& trans_expr)
|
||||||
{
|
{
|
||||||
if (trans_expr)
|
if (trans_expr)
|
||||||
|
@ -275,7 +296,8 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void symbolizer_attributes::operator () (group_symbolizer const& sym)
|
|
||||||
|
inline void group_attribute_collector::operator() (group_symbolizer const& sym)
|
||||||
{
|
{
|
||||||
// find all column names referenced in the group symbolizer
|
// find all column names referenced in the group symbolizer
|
||||||
std::set<std::string> group_columns;
|
std::set<std::string> group_columns;
|
||||||
|
@ -294,7 +316,10 @@ inline void symbolizer_attributes::operator () (group_symbolizer const& sym)
|
||||||
if (props) {
|
if (props) {
|
||||||
for (auto const& rule : props->get_rules())
|
for (auto const& rule : props->get_rules())
|
||||||
{
|
{
|
||||||
|
// note that this recurses down on to the symbolizer
|
||||||
|
// internals too, so we get all free variables.
|
||||||
column_collector(*rule);
|
column_collector(*rule);
|
||||||
|
// still need to collect repeat key columns
|
||||||
if (rule->get_repeat_key())
|
if (rule->get_repeat_key())
|
||||||
{
|
{
|
||||||
boost::apply_visitor(rk_attr, *(rule->get_repeat_key()));
|
boost::apply_visitor(rk_attr, *(rule->get_repeat_key()));
|
||||||
|
@ -305,14 +330,14 @@ inline void symbolizer_attributes::operator () (group_symbolizer const& sym)
|
||||||
// get indexed column names
|
// get indexed column names
|
||||||
for (auto const& col_name : group_columns)
|
for (auto const& col_name : group_columns)
|
||||||
{
|
{
|
||||||
if (col_name.find('%') != std::string::npos)
|
if (expand_index_columns_ && col_name.find('%') != std::string::npos)
|
||||||
{
|
{
|
||||||
// Note: ignore column name if it is '%' by itself.
|
// Note: ignore column name if it is '%' by itself.
|
||||||
// '%' is a special case to access the index value itself,
|
// '%' is a special case to access the index value itself,
|
||||||
// rather than acessing indexed columns from data source.
|
// rather than acessing indexed columns from data source.
|
||||||
if (col_name.size() > 1)
|
if (col_name.size() > 1)
|
||||||
{
|
{
|
||||||
// indexed column name. add column name for each index value.
|
// Indexed column name. add column name for each index value.
|
||||||
int start = get<value_integer>(sym, keys::start_column);
|
int start = get<value_integer>(sym, keys::start_column);
|
||||||
int end = start + get<value_integer>(sym, keys::num_columns);
|
int end = start + get<value_integer>(sym, keys::num_columns);
|
||||||
for (int col_idx = start; col_idx < end; ++col_idx)
|
for (int col_idx = start; col_idx < end; ++col_idx)
|
||||||
|
@ -325,7 +350,8 @@ inline void symbolizer_attributes::operator () (group_symbolizer const& sym)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// non indexed column name. insert as is.
|
// This is not an indexed column, or we are ignoring indexes.
|
||||||
|
// Insert the name as is.
|
||||||
names_.insert(col_name);
|
names_.insert(col_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
339
include/mapnik/renderer_common/process_group_symbolizer.hpp
Normal file
339
include/mapnik/renderer_common/process_group_symbolizer.hpp
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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_RENDERER_COMMON_PROCESS_GROUP_SYMBOLIZER_HPP
|
||||||
|
#define MAPNIK_RENDERER_COMMON_PROCESS_GROUP_SYMBOLIZER_HPP
|
||||||
|
|
||||||
|
#include <mapnik/pixel_position.hpp>
|
||||||
|
#include <mapnik/marker_cache.hpp>
|
||||||
|
#include <mapnik/feature.hpp>
|
||||||
|
#include <mapnik/feature_factory.hpp>
|
||||||
|
#include <mapnik/renderer_common.hpp>
|
||||||
|
#include <mapnik/label_collision_detector.hpp>
|
||||||
|
#include <mapnik/symbolizer.hpp>
|
||||||
|
#include <mapnik/attribute_collector.hpp>
|
||||||
|
#include <mapnik/group/group_layout.hpp>
|
||||||
|
#include <mapnik/group/group_layout_manager.hpp>
|
||||||
|
#include <mapnik/group/group_rule.hpp>
|
||||||
|
#include <mapnik/group/group_symbolizer_helper.hpp>
|
||||||
|
#include <mapnik/group/group_symbolizer_properties.hpp>
|
||||||
|
#include <mapnik/renderer_common/process_point_symbolizer.hpp>
|
||||||
|
#include <mapnik/text/placements_list.hpp>
|
||||||
|
|
||||||
|
#include <agg_trans_affine.h>
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
class proj_transform;
|
||||||
|
|
||||||
|
/* General:
|
||||||
|
*
|
||||||
|
* The approach here is to run the normal symbolizers, but in
|
||||||
|
* a 'virtual' blank environment where the changes that they
|
||||||
|
* make are recorded (the detector, the render_* calls).
|
||||||
|
*
|
||||||
|
* The recorded boxes are then used to lay out the items and
|
||||||
|
* the offsets from old to new positions can be used to perform
|
||||||
|
* the actual rendering calls.
|
||||||
|
*
|
||||||
|
* This should allow us to re-use as much as possible of the
|
||||||
|
* existing symbolizer layout and rendering code while still
|
||||||
|
* being able to interpose our own decisions about whether
|
||||||
|
* a collision has occured or not.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thunk for rendering a particular instance of a point - this
|
||||||
|
* stores all the arguments necessary to re-render this point
|
||||||
|
* symbolizer at a later time.
|
||||||
|
*/
|
||||||
|
struct point_render_thunk
|
||||||
|
{
|
||||||
|
pixel_position pos_;
|
||||||
|
marker_ptr marker_;
|
||||||
|
agg::trans_affine tr_;
|
||||||
|
double opacity_;
|
||||||
|
composite_mode_e comp_op_;
|
||||||
|
|
||||||
|
point_render_thunk(pixel_position const &pos, marker const &m,
|
||||||
|
agg::trans_affine const &tr, double opacity,
|
||||||
|
composite_mode_e comp_op);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct text_render_thunk
|
||||||
|
{
|
||||||
|
// need to keep these around, annoyingly, as the glyph_position
|
||||||
|
// struct keeps a pointer to the glyph_info, so we have to
|
||||||
|
// ensure the lifetime is the same.
|
||||||
|
placements_list placements_;
|
||||||
|
std::shared_ptr<std::vector<glyph_info> > glyphs_;
|
||||||
|
composite_mode_e comp_op_;
|
||||||
|
halo_rasterizer_enum halo_rasterizer_;
|
||||||
|
|
||||||
|
text_render_thunk(placements_list const &placements,
|
||||||
|
composite_mode_e comp_op,
|
||||||
|
halo_rasterizer_enum halo_rasterizer);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variant type for render thunks to allow us to re-render them
|
||||||
|
* via a static visitor later.
|
||||||
|
*/
|
||||||
|
typedef boost::variant<point_render_thunk,
|
||||||
|
text_render_thunk> render_thunk;
|
||||||
|
typedef std::shared_ptr<render_thunk> render_thunk_ptr;
|
||||||
|
typedef std::list<render_thunk_ptr> render_thunk_list;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for extracting the bounding boxes associated with placing
|
||||||
|
* a symbolizer at a fake, virtual point - not real geometry.
|
||||||
|
*
|
||||||
|
* The bounding boxes can be used for layout, and the thunks are
|
||||||
|
* used to re-render at locations according to the group layout.
|
||||||
|
*/
|
||||||
|
struct render_thunk_extractor : public boost::static_visitor<>
|
||||||
|
{
|
||||||
|
render_thunk_extractor(box2d<double> &box,
|
||||||
|
render_thunk_list &thunks,
|
||||||
|
mapnik::feature_impl &feature,
|
||||||
|
proj_transform const &prj_trans,
|
||||||
|
renderer_common &common,
|
||||||
|
box2d<double> const &clipping_extent);
|
||||||
|
|
||||||
|
void operator()(point_symbolizer const &sym) const;
|
||||||
|
|
||||||
|
void operator()(text_symbolizer const &sym) const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(T const &) const
|
||||||
|
{
|
||||||
|
// TODO: warning if unimplemented?
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
box2d<double> &box_;
|
||||||
|
render_thunk_list &thunks_;
|
||||||
|
mapnik::feature_impl &feature_;
|
||||||
|
proj_transform const &prj_trans_;
|
||||||
|
renderer_common &common_;
|
||||||
|
box2d<double> clipping_extent_;
|
||||||
|
|
||||||
|
void update_box() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
geometry_type *origin_point(proj_transform const &prj_trans,
|
||||||
|
renderer_common const &common);
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void render_offset_placements(placements_list const& placements,
|
||||||
|
pixel_position const& offset,
|
||||||
|
F render_text) {
|
||||||
|
|
||||||
|
for (glyph_positions_ptr glyphs : placements)
|
||||||
|
{
|
||||||
|
// move the glyphs to the correct offset
|
||||||
|
pixel_position base_point = glyphs->get_base_point();
|
||||||
|
glyphs->set_base_point(base_point + offset);
|
||||||
|
|
||||||
|
// update the position of any marker
|
||||||
|
marker_info_ptr marker_info = glyphs->marker();
|
||||||
|
pixel_position marker_pos = glyphs->marker_pos();
|
||||||
|
if (marker_info)
|
||||||
|
{
|
||||||
|
glyphs->set_marker(marker_info, marker_pos + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
render_text(glyphs);
|
||||||
|
|
||||||
|
// Need to put the base_point back how it was in case something else calls this again
|
||||||
|
// (don't want to add offset twice) or calls with a different offset.
|
||||||
|
glyphs->set_base_point(base_point);
|
||||||
|
if (marker_info)
|
||||||
|
{
|
||||||
|
glyphs->set_marker(marker_info, marker_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void render_group_symbolizer(group_symbolizer const &sym,
|
||||||
|
mapnik::feature_impl &feature,
|
||||||
|
proj_transform const &prj_trans,
|
||||||
|
box2d<double> const &clipping_extent,
|
||||||
|
renderer_common &common,
|
||||||
|
F render_thunks)
|
||||||
|
{
|
||||||
|
// find all column names referenced in the group rules and symbolizers
|
||||||
|
std::set<std::string> columns;
|
||||||
|
group_attribute_collector column_collector(columns, false);
|
||||||
|
column_collector(sym);
|
||||||
|
|
||||||
|
group_symbolizer_properties_ptr props = get<group_symbolizer_properties_ptr>(sym, keys::group_properties);
|
||||||
|
|
||||||
|
// create a new context for the sub features of this group
|
||||||
|
context_ptr sub_feature_ctx = std::make_shared<mapnik::context_type>();
|
||||||
|
|
||||||
|
// populate new context with column names referenced in the group rules and symbolizers
|
||||||
|
for (auto const& col_name : columns)
|
||||||
|
{
|
||||||
|
sub_feature_ctx->push(col_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep track of the sub features that we'll want to symbolize
|
||||||
|
// along with the group rules that they matched
|
||||||
|
std::vector< std::pair<group_rule_ptr, feature_ptr> > matches;
|
||||||
|
|
||||||
|
// create a copied 'virtual' common renderer for processing sub feature symbolizers
|
||||||
|
// create an empty detector for it, so we are sure we won't hit anything
|
||||||
|
renderer_common virtual_renderer(common);
|
||||||
|
virtual_renderer.detector_ = std::make_shared<label_collision_detector4>(common.detector_->extent());
|
||||||
|
|
||||||
|
// keep track of which lists of render thunks correspond to
|
||||||
|
// entries in the group_layout_manager.
|
||||||
|
std::vector<render_thunk_list> layout_thunks;
|
||||||
|
size_t num_layout_thunks = 0;
|
||||||
|
|
||||||
|
// layout manager to store and arrange bboxes of matched features
|
||||||
|
group_layout_manager layout_manager(props->get_layout(), pixel_position(common.width_ / 2.0, common.height_ / 2.0));
|
||||||
|
|
||||||
|
// run feature or sub feature through the group rules & symbolizers
|
||||||
|
// for each index value in the range
|
||||||
|
int start = get<value_integer>(sym, keys::start_column);
|
||||||
|
int end = start + get<value_integer>(sym, keys::num_columns);
|
||||||
|
for (int col_idx = start; col_idx < end; ++col_idx)
|
||||||
|
{
|
||||||
|
// create sub feature with indexed column values
|
||||||
|
feature_ptr sub_feature = feature_factory::create(sub_feature_ctx, col_idx);
|
||||||
|
|
||||||
|
// copy the necessary columns to sub feature
|
||||||
|
for(auto const& col_name : columns)
|
||||||
|
{
|
||||||
|
if (col_name.find('%') != std::string::npos)
|
||||||
|
{
|
||||||
|
if (col_name.size() == 1)
|
||||||
|
{
|
||||||
|
// column name is '%' by itself, so give the index as the value
|
||||||
|
sub_feature->put(col_name, (value_integer)col_idx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// indexed column
|
||||||
|
std::string col_idx_name = col_name;
|
||||||
|
boost::replace_all(col_idx_name, "%", boost::lexical_cast<std::string>(col_idx));
|
||||||
|
sub_feature->put(col_name, feature.get(col_idx_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// non-indexed column
|
||||||
|
sub_feature->put(col_name, feature.get(col_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a single point geometry at pixel origin
|
||||||
|
sub_feature->add_geometry(origin_point(prj_trans, common));
|
||||||
|
|
||||||
|
// get the layout for this set of properties
|
||||||
|
for (auto const& rule : props->get_rules())
|
||||||
|
{
|
||||||
|
if (boost::apply_visitor(evaluate<Feature,value_type>(*sub_feature),
|
||||||
|
*(rule->get_filter())).to_bool())
|
||||||
|
{
|
||||||
|
// add matched rule and feature to the list of things to draw
|
||||||
|
matches.push_back(std::make_pair(rule, sub_feature));
|
||||||
|
|
||||||
|
// construct a bounding box around all symbolizers for the matched rule
|
||||||
|
bound_box bounds;
|
||||||
|
render_thunk_list thunks;
|
||||||
|
render_thunk_extractor extractor(bounds, thunks, *sub_feature, prj_trans,
|
||||||
|
virtual_renderer, clipping_extent);
|
||||||
|
|
||||||
|
for (auto const& sym : *rule)
|
||||||
|
{
|
||||||
|
// TODO: construct layout and obtain bounding box
|
||||||
|
boost::apply_visitor(extractor, sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the bounding box to the layout manager
|
||||||
|
layout_manager.add_member_bound_box(bounds);
|
||||||
|
layout_thunks.emplace_back(std::move(thunks));
|
||||||
|
++num_layout_thunks;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a symbolizer helper
|
||||||
|
group_symbolizer_helper helper(sym, feature, prj_trans,
|
||||||
|
common.width_, common.height_,
|
||||||
|
common.scale_factor_, common.t_,
|
||||||
|
*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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
for (size_t layout_i = 0; layout_i < num_layout_thunks; ++layout_i)
|
||||||
|
{
|
||||||
|
const pixel_position &offset = layout_manager.offset_at(layout_i);
|
||||||
|
pixel_position render_offset = pos + offset;
|
||||||
|
|
||||||
|
render_thunks(layout_thunks[layout_i], render_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mapnik
|
||||||
|
|
||||||
|
#endif /* MAPNIK_RENDERER_COMMON_PROCESS_GROUP_SYMBOLIZER_HPP */
|
|
@ -23,6 +23,7 @@
|
||||||
#ifndef MAPNIK_RENDERER_COMMON_PROCESS_POINT_SYMBOLIZER_HPP
|
#ifndef MAPNIK_RENDERER_COMMON_PROCESS_POINT_SYMBOLIZER_HPP
|
||||||
#define MAPNIK_RENDERER_COMMON_PROCESS_POINT_SYMBOLIZER_HPP
|
#define MAPNIK_RENDERER_COMMON_PROCESS_POINT_SYMBOLIZER_HPP
|
||||||
|
|
||||||
|
#include <mapnik/geom_util.hpp>
|
||||||
#include <mapnik/marker.hpp>
|
#include <mapnik/marker.hpp>
|
||||||
#include <mapnik/marker_cache.hpp>
|
#include <mapnik/marker_cache.hpp>
|
||||||
|
|
||||||
|
|
|
@ -25,224 +25,21 @@
|
||||||
#include <mapnik/agg_renderer.hpp>
|
#include <mapnik/agg_renderer.hpp>
|
||||||
#include <mapnik/agg_rasterizer.hpp>
|
#include <mapnik/agg_rasterizer.hpp>
|
||||||
#include <mapnik/image_util.hpp>
|
#include <mapnik/image_util.hpp>
|
||||||
#include <mapnik/feature_factory.hpp>
|
|
||||||
#include <mapnik/attribute_collector.hpp>
|
|
||||||
|
|
||||||
#include <mapnik/text/placement_finder.hpp>
|
|
||||||
#include <mapnik/text/layout.hpp>
|
|
||||||
#include <mapnik/text/renderer.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/geom_util.hpp>
|
||||||
#include <mapnik/symbolizer.hpp>
|
#include <mapnik/symbolizer.hpp>
|
||||||
#include <mapnik/parse_path.hpp>
|
|
||||||
#include <mapnik/pixel_position.hpp>
|
#include <mapnik/pixel_position.hpp>
|
||||||
#include <mapnik/label_collision_detector.hpp>
|
#include <mapnik/renderer_common/process_group_symbolizer.hpp>
|
||||||
|
|
||||||
#include <mapnik/renderer_common/process_point_symbolizer.hpp>
|
|
||||||
#include <mapnik/text/symbolizer_helpers.hpp>
|
|
||||||
|
|
||||||
// agg
|
// agg
|
||||||
#include "agg_trans_affine.h"
|
#include "agg_trans_affine.h"
|
||||||
|
|
||||||
// stl
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// boost
|
// boost
|
||||||
#include <boost/variant/apply_visitor.hpp>
|
#include <boost/variant/apply_visitor.hpp>
|
||||||
|
|
||||||
namespace mapnik {
|
namespace mapnik {
|
||||||
|
|
||||||
/* General:
|
|
||||||
*
|
|
||||||
* The approach here is to run the normal symbolizers, but in
|
|
||||||
* a 'virtual' blank environment where the changes that they
|
|
||||||
* make are recorded (the detector, the render_* calls).
|
|
||||||
*
|
|
||||||
* The recorded boxes are then used to lay out the items and
|
|
||||||
* the offsets from old to new positions can be used to perform
|
|
||||||
* the actual rendering calls.
|
|
||||||
*
|
|
||||||
* This should allow us to re-use as much as possible of the
|
|
||||||
* existing symbolizer layout and rendering code while still
|
|
||||||
* being able to interpose our own decisions about whether
|
|
||||||
* a collision has occured or not.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thunk for rendering a particular instance of a point - this
|
|
||||||
* stores all the arguments necessary to re-render this point
|
|
||||||
* symbolizer at a later time.
|
|
||||||
*/
|
|
||||||
struct point_render_thunk
|
|
||||||
{
|
|
||||||
pixel_position pos_;
|
|
||||||
marker_ptr marker_;
|
|
||||||
agg::trans_affine tr_;
|
|
||||||
double opacity_;
|
|
||||||
composite_mode_e comp_op_;
|
|
||||||
|
|
||||||
point_render_thunk(pixel_position const &pos, marker const &m,
|
|
||||||
agg::trans_affine const &tr, double opacity,
|
|
||||||
composite_mode_e comp_op)
|
|
||||||
: pos_(pos), marker_(std::make_shared<marker>(m)),
|
|
||||||
tr_(tr), opacity_(opacity), comp_op_(comp_op)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct text_render_thunk
|
|
||||||
{
|
|
||||||
halo_rasterizer_enum halo_rasterizer_;
|
|
||||||
composite_mode_e comp_op_;
|
|
||||||
|
|
||||||
// need to keep these around, annoyingly, as the glyph_position
|
|
||||||
// struct keeps a pointer to the glyph_info, so we have to
|
|
||||||
// ensure the lifetime is the same.
|
|
||||||
placements_list placements_;
|
|
||||||
std::shared_ptr<std::vector<glyph_info> > glyphs_;
|
|
||||||
|
|
||||||
text_render_thunk(placements_list const &placements,
|
|
||||||
halo_rasterizer_enum halo_rasterizer,
|
|
||||||
composite_mode_e comp_op)
|
|
||||||
: halo_rasterizer_(halo_rasterizer), comp_op_(comp_op),
|
|
||||||
placements_(), glyphs_(std::make_shared<std::vector<glyph_info> >())
|
|
||||||
{
|
|
||||||
std::vector<glyph_info> &glyph_vec = *glyphs_;
|
|
||||||
|
|
||||||
size_t glyph_count = 0;
|
|
||||||
for (glyph_positions_ptr positions : placements)
|
|
||||||
{
|
|
||||||
glyph_count += std::distance(positions->begin(), positions->end());
|
|
||||||
}
|
|
||||||
glyph_vec.reserve(glyph_count);
|
|
||||||
|
|
||||||
for (glyph_positions_ptr positions : placements)
|
|
||||||
{
|
|
||||||
glyph_positions_ptr new_positions = std::make_shared<glyph_positions>();
|
|
||||||
new_positions->reserve(std::distance(positions->begin(), positions->end()));
|
|
||||||
glyph_positions &new_pos = *new_positions;
|
|
||||||
|
|
||||||
new_pos.set_base_point(positions->get_base_point());
|
|
||||||
if (positions->marker())
|
|
||||||
{
|
|
||||||
new_pos.set_marker(positions->marker(), positions->marker_pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (glyph_position const &pos : *positions)
|
|
||||||
{
|
|
||||||
glyph_vec.push_back(*pos.glyph);
|
|
||||||
new_pos.push_back(glyph_vec.back(), pos.pos, pos.rot);
|
|
||||||
}
|
|
||||||
|
|
||||||
placements_.push_back(new_positions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Variant type for render thunks to allow us to re-render them
|
|
||||||
// via a static visitor later.
|
|
||||||
typedef boost::variant<point_render_thunk,
|
|
||||||
text_render_thunk> render_thunk;
|
|
||||||
typedef std::shared_ptr<render_thunk> render_thunk_ptr;
|
|
||||||
typedef std::list<render_thunk_ptr> render_thunk_list;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visitor to extract the bounding boxes associated with placing
|
|
||||||
* a symbolizer at a fake, virtual point - not real geometry.
|
|
||||||
*
|
|
||||||
* The bounding boxes can be used for layout, and the thunks are
|
|
||||||
* used to re-render at locations according to the group layout.
|
|
||||||
*/
|
|
||||||
struct extract_bboxes : public boost::static_visitor<>
|
|
||||||
{
|
|
||||||
extract_bboxes(box2d<double> &box,
|
|
||||||
render_thunk_list &thunks,
|
|
||||||
mapnik::feature_impl &feature,
|
|
||||||
proj_transform const &prj_trans,
|
|
||||||
renderer_common const &common,
|
|
||||||
text_layout const &text,
|
|
||||||
box2d<double> const &clipping_extent)
|
|
||||||
: box_(box), thunks_(thunks), feature_(feature), prj_trans_(prj_trans),
|
|
||||||
common_(common), text_(text), clipping_extent_(clipping_extent)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void operator()(point_symbolizer const &sym) const
|
|
||||||
{
|
|
||||||
// create an empty detector, so we are sure we won't hit
|
|
||||||
// anything
|
|
||||||
renderer_common common(common_);
|
|
||||||
common.detector_ = std::make_shared<label_collision_detector4>(common_.detector_->extent());
|
|
||||||
|
|
||||||
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature_, src_over);
|
|
||||||
|
|
||||||
render_point_symbolizer(
|
|
||||||
sym, feature_, prj_trans_, common,
|
|
||||||
[&](pixel_position const &pos, marker const &marker,
|
|
||||||
agg::trans_affine const &tr, double opacity) {
|
|
||||||
point_render_thunk thunk(pos, marker, tr, opacity, comp_op);
|
|
||||||
thunks_.push_back(std::make_shared<render_thunk>(std::move(thunk)));
|
|
||||||
});
|
|
||||||
|
|
||||||
update_box(*common.detector_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator()(text_symbolizer const &sym) const
|
|
||||||
{
|
|
||||||
// create an empty detector, so we are sure we won't hit
|
|
||||||
// anything
|
|
||||||
renderer_common common(common_);
|
|
||||||
common.detector_ = std::make_shared<label_collision_detector4>(common_.detector_->extent());
|
|
||||||
|
|
||||||
box2d<double> clip_box = clipping_extent_;
|
|
||||||
text_symbolizer_helper helper(
|
|
||||||
sym, feature_, prj_trans_,
|
|
||||||
common.width_, common.height_,
|
|
||||||
common.scale_factor_,
|
|
||||||
common.t_, common.font_manager_, *common.detector_,
|
|
||||||
clip_box);
|
|
||||||
|
|
||||||
halo_rasterizer_enum halo_rasterizer = get<halo_rasterizer_enum>(sym, keys::halo_rasterizer, HALO_RASTERIZER_FULL);
|
|
||||||
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature_, src_over);
|
|
||||||
|
|
||||||
placements_list const& placements = helper.get();
|
|
||||||
text_render_thunk thunk(placements, halo_rasterizer, comp_op);
|
|
||||||
thunks_.push_back(std::make_shared<render_thunk>(thunk));
|
|
||||||
|
|
||||||
update_box(*common.detector_);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void operator()(T const &) const
|
|
||||||
{
|
|
||||||
// TODO: warning if unimplemented?
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
box2d<double> &box_;
|
|
||||||
render_thunk_list &thunks_;
|
|
||||||
mapnik::feature_impl &feature_;
|
|
||||||
proj_transform const &prj_trans_;
|
|
||||||
renderer_common const &common_;
|
|
||||||
text_layout const &text_;
|
|
||||||
box2d<double> clipping_extent_;
|
|
||||||
|
|
||||||
void update_box(label_collision_detector4 &detector) const
|
|
||||||
{
|
|
||||||
for (auto const &label : detector)
|
|
||||||
{
|
|
||||||
if (box_.width() > 0 && box_.height() > 0)
|
|
||||||
{
|
|
||||||
box_.expand_to_include(label.box);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
box_ = label.box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a thunk which was frozen from a previous call to
|
* Render a thunk which was frozen from a previous call to
|
||||||
* extract_bboxes. We should now have a new offset at which
|
* extract_bboxes. We should now have a new offset at which
|
||||||
|
@ -273,24 +70,13 @@ struct thunk_renderer : public boost::static_visitor<>
|
||||||
{
|
{
|
||||||
text_renderer_type ren(*buf_, thunk.halo_rasterizer_, thunk.comp_op_,
|
text_renderer_type ren(*buf_, thunk.halo_rasterizer_, thunk.comp_op_,
|
||||||
common_.scale_factor_, common_.font_manager_.get_stroker());
|
common_.scale_factor_, common_.font_manager_.get_stroker());
|
||||||
for (glyph_positions_ptr glyphs : thunk.placements_)
|
|
||||||
{
|
|
||||||
// move the glyphs to the correct offset
|
|
||||||
pixel_position const &base_point = glyphs->get_base_point();
|
|
||||||
pixel_position new_base_point(base_point.x + offset_.x, base_point.y + offset_.y);
|
|
||||||
glyphs->set_base_point(new_base_point);
|
|
||||||
|
|
||||||
// update the position of any marker
|
|
||||||
marker_info_ptr marker_info = glyphs->marker();
|
|
||||||
if (marker_info)
|
|
||||||
{
|
|
||||||
pixel_position const &marker_pos = glyphs->marker_pos();
|
|
||||||
pixel_position new_marker_pos(marker_pos.x + offset_.x, marker_pos.y + offset_.y);
|
|
||||||
glyphs->set_marker(marker_info, new_marker_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
render_offset_placements(
|
||||||
|
thunk.placements_,
|
||||||
|
offset_,
|
||||||
|
[&] (glyph_positions_ptr glyphs) {
|
||||||
ren.render(*glyphs);
|
ren.render(*glyphs);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -306,216 +92,21 @@ private:
|
||||||
pixel_position offset_;
|
pixel_position offset_;
|
||||||
};
|
};
|
||||||
|
|
||||||
geometry_type *origin_point(proj_transform const &prj_trans,
|
|
||||||
renderer_common const &common)
|
|
||||||
{
|
|
||||||
// note that we choose a point in the middle of the screen to
|
|
||||||
// try to ensure that we don't get edge artefacts due to any
|
|
||||||
// symbolizers with avoid-edges set: only the avoid-edges of
|
|
||||||
// the group symbolizer itself should matter.
|
|
||||||
double x = common.width_ / 2.0, y = common.height_ / 2.0, z = 0.0;
|
|
||||||
common.t_.backward(&x, &y);
|
|
||||||
prj_trans.forward(x, y, z);
|
|
||||||
geometry_type *geom = new geometry_type(geometry_type::Point);
|
|
||||||
geom->move_to(x, y);
|
|
||||||
return geom;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T0, typename T1>
|
template <typename T0, typename T1>
|
||||||
void agg_renderer<T0,T1>::process(group_symbolizer const& sym,
|
void agg_renderer<T0,T1>::process(group_symbolizer const& sym,
|
||||||
mapnik::feature_impl & feature,
|
mapnik::feature_impl & feature,
|
||||||
proj_transform const& prj_trans)
|
proj_transform const& prj_trans)
|
||||||
{
|
{
|
||||||
box2d<double> clip_box = clipping_extent();
|
render_group_symbolizer(
|
||||||
|
sym, feature, prj_trans, clipping_extent(), common_,
|
||||||
// find all column names referenced in the group rules and symbolizers
|
[&](render_thunk_list const& thunks, pixel_position const& render_offset)
|
||||||
std::set<std::string> columns;
|
|
||||||
attribute_collector column_collector(columns);
|
|
||||||
expression_attributes<std::set<std::string> > rk_attr(columns);
|
|
||||||
|
|
||||||
expression_ptr repeat_key = get<mapnik::expression_ptr>(sym, keys::repeat_key);
|
|
||||||
if (repeat_key)
|
|
||||||
{
|
{
|
||||||
boost::apply_visitor(rk_attr, *repeat_key);
|
thunk_renderer ren(*this, current_buffer_, common_, render_offset);
|
||||||
}
|
for (render_thunk_ptr const& thunk : thunks)
|
||||||
|
|
||||||
// get columns from child rules and symbolizers
|
|
||||||
group_symbolizer_properties_ptr props = get<group_symbolizer_properties_ptr>(sym, keys::group_properties);
|
|
||||||
if (props) {
|
|
||||||
for (auto const& rule : props->get_rules())
|
|
||||||
{
|
|
||||||
// note that this recurses down on to the symbolizer
|
|
||||||
// internals too, so we get all free variables.
|
|
||||||
column_collector(*rule);
|
|
||||||
// still need to collect repeat key columns
|
|
||||||
if (rule->get_repeat_key())
|
|
||||||
{
|
|
||||||
boost::apply_visitor(rk_attr, *(rule->get_repeat_key()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new context for the sub features of this group
|
|
||||||
context_ptr sub_feature_ctx = std::make_shared<mapnik::context_type>();
|
|
||||||
|
|
||||||
// populate new context with column names referenced in the group rules and symbolizers
|
|
||||||
// and determine if any indexed columns are present
|
|
||||||
bool has_idx_cols = false;
|
|
||||||
for (auto const& col_name : columns)
|
|
||||||
{
|
|
||||||
sub_feature_ctx->push(col_name);
|
|
||||||
if (col_name.find('%') != std::string::npos)
|
|
||||||
{
|
|
||||||
has_idx_cols = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep track of the sub features that we'll want to symbolize
|
|
||||||
// along with the group rules that they matched
|
|
||||||
std::vector< std::pair<group_rule_ptr, feature_ptr> > matches;
|
|
||||||
// keep track of which lists of render thunks correspond to
|
|
||||||
// entries in the group_layout_manager.
|
|
||||||
std::vector<render_thunk_list> layout_thunks;
|
|
||||||
size_t num_layout_thunks = 0;
|
|
||||||
|
|
||||||
// layout manager to store and arrange bboxes of matched features
|
|
||||||
group_layout_manager layout_manager(props->get_layout(), pixel_position(common_.width_ / 2.0, common_.height_ / 2.0));
|
|
||||||
text_layout text(common_.font_manager_, common_.scale_factor_);
|
|
||||||
|
|
||||||
// run feature or sub feature through the group rules & symbolizers
|
|
||||||
// for each index value in the range
|
|
||||||
int start = get<value_integer>(sym, keys::start_column);
|
|
||||||
int end = start + get<value_integer>(sym, keys::num_columns);
|
|
||||||
for (int col_idx = start; col_idx < end; ++col_idx)
|
|
||||||
{
|
|
||||||
feature_ptr sub_feature;
|
|
||||||
|
|
||||||
if (has_idx_cols)
|
|
||||||
{
|
|
||||||
// create sub feature with indexed column values
|
|
||||||
sub_feature = feature_factory::create(sub_feature_ctx, col_idx);
|
|
||||||
|
|
||||||
// copy the necessary columns to sub feature
|
|
||||||
for(auto const& col_name : columns)
|
|
||||||
{
|
|
||||||
if (col_name.find('%') != std::string::npos)
|
|
||||||
{
|
|
||||||
if (col_name.size() == 1)
|
|
||||||
{
|
|
||||||
// column name is '%' by itself, so give the index as the value
|
|
||||||
sub_feature->put(col_name, (value_integer)col_idx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// indexed column
|
|
||||||
std::string col_idx_name = col_name;
|
|
||||||
boost::replace_all(col_idx_name, "%", boost::lexical_cast<std::string>(col_idx));
|
|
||||||
sub_feature->put(col_name, feature.get(col_idx_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// non-indexed column
|
|
||||||
sub_feature->put(col_name, feature.get(col_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// no indexed columns, so use the existing feature instead of copying
|
|
||||||
sub_feature = feature_ptr(&feature);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add a single point geometry at pixel origin
|
|
||||||
sub_feature->add_geometry(origin_point(prj_trans, common_));
|
|
||||||
|
|
||||||
// get the layout for this set of properties
|
|
||||||
for (auto const& rule : props->get_rules())
|
|
||||||
{
|
|
||||||
if (boost::apply_visitor(evaluate<Feature,value_type>(*sub_feature),
|
|
||||||
*(rule->get_filter())).to_bool())
|
|
||||||
{
|
|
||||||
// add matched rule and feature to the list of things to draw
|
|
||||||
matches.push_back(std::make_pair(rule, sub_feature));
|
|
||||||
|
|
||||||
// construct a bounding box around all symbolizers for the matched rule
|
|
||||||
bound_box bounds;
|
|
||||||
render_thunk_list thunks;
|
|
||||||
extract_bboxes extractor(bounds, thunks, *sub_feature, prj_trans,
|
|
||||||
common_, text, clip_box);
|
|
||||||
|
|
||||||
for (auto const& sym : *rule)
|
|
||||||
{
|
|
||||||
// TODO: construct layout and obtain bounding box
|
|
||||||
boost::apply_visitor(extractor, sym);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the bounding box to the layout manager
|
|
||||||
layout_manager.add_member_bound_box(bounds);
|
|
||||||
layout_thunks.emplace_back(std::move(thunks));
|
|
||||||
++num_layout_thunks;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
for (size_t layout_i = 0; layout_i < num_layout_thunks; ++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.
|
|
||||||
thunk_renderer ren(*this, current_buffer_, common_, ren_pos);
|
|
||||||
|
|
||||||
for (render_thunk_ptr &thunk : layout_thunks[layout_i])
|
|
||||||
{
|
{
|
||||||
boost::apply_visitor(ren, *thunk);
|
boost::apply_visitor(ren, *thunk);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template void agg_renderer<image_32>::process(group_symbolizer const&,
|
template void agg_renderer<image_32>::process(group_symbolizer const&,
|
||||||
|
|
|
@ -240,6 +240,7 @@ source = Split(
|
||||||
config_error.cpp
|
config_error.cpp
|
||||||
color_factory.cpp
|
color_factory.cpp
|
||||||
renderer_common.cpp
|
renderer_common.cpp
|
||||||
|
renderer_common/process_group_symbolizer.cpp
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
153
src/renderer_common/process_group_symbolizer.cpp
Normal file
153
src/renderer_common/process_group_symbolizer.cpp
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include <mapnik/renderer_common/process_group_symbolizer.hpp>
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
point_render_thunk::point_render_thunk(pixel_position const &pos, marker const &m,
|
||||||
|
agg::trans_affine const &tr, double opacity,
|
||||||
|
composite_mode_e comp_op)
|
||||||
|
: pos_(pos), marker_(std::make_shared<marker>(m)),
|
||||||
|
tr_(tr), opacity_(opacity), comp_op_(comp_op)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
text_render_thunk::text_render_thunk(placements_list const &placements,
|
||||||
|
composite_mode_e comp_op,
|
||||||
|
halo_rasterizer_enum halo_rasterizer)
|
||||||
|
: placements_(), glyphs_(std::make_shared<std::vector<glyph_info> >()),
|
||||||
|
comp_op_(comp_op), halo_rasterizer_(halo_rasterizer)
|
||||||
|
{
|
||||||
|
std::vector<glyph_info> &glyph_vec = *glyphs_;
|
||||||
|
|
||||||
|
size_t glyph_count = 0;
|
||||||
|
for (glyph_positions_ptr positions : placements)
|
||||||
|
{
|
||||||
|
glyph_count += std::distance(positions->begin(), positions->end());
|
||||||
|
}
|
||||||
|
glyph_vec.reserve(glyph_count);
|
||||||
|
|
||||||
|
for (glyph_positions_ptr positions : placements)
|
||||||
|
{
|
||||||
|
glyph_positions_ptr new_positions = std::make_shared<glyph_positions>();
|
||||||
|
new_positions->reserve(std::distance(positions->begin(), positions->end()));
|
||||||
|
glyph_positions &new_pos = *new_positions;
|
||||||
|
|
||||||
|
new_pos.set_base_point(positions->get_base_point());
|
||||||
|
if (positions->marker())
|
||||||
|
{
|
||||||
|
new_pos.set_marker(positions->marker(), positions->marker_pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (glyph_position const &pos : *positions)
|
||||||
|
{
|
||||||
|
glyph_vec.push_back(*pos.glyph);
|
||||||
|
new_pos.push_back(glyph_vec.back(), pos.pos, pos.rot);
|
||||||
|
}
|
||||||
|
|
||||||
|
placements_.push_back(new_positions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render_thunk_extractor::render_thunk_extractor(box2d<double> &box,
|
||||||
|
render_thunk_list &thunks,
|
||||||
|
mapnik::feature_impl &feature,
|
||||||
|
proj_transform const &prj_trans,
|
||||||
|
renderer_common &common,
|
||||||
|
box2d<double> const &clipping_extent)
|
||||||
|
: box_(box), thunks_(thunks), feature_(feature), prj_trans_(prj_trans),
|
||||||
|
common_(common), clipping_extent_(clipping_extent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void render_thunk_extractor::operator()(point_symbolizer const &sym) const
|
||||||
|
{
|
||||||
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature_, src_over);
|
||||||
|
|
||||||
|
render_point_symbolizer(
|
||||||
|
sym, feature_, prj_trans_, common_,
|
||||||
|
[&](pixel_position const &pos, marker const &marker,
|
||||||
|
agg::trans_affine const &tr, double opacity) {
|
||||||
|
point_render_thunk thunk(pos, marker, tr, opacity, comp_op);
|
||||||
|
thunks_.push_back(std::make_shared<render_thunk>(std::move(thunk)));
|
||||||
|
});
|
||||||
|
|
||||||
|
update_box();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_thunk_extractor::operator()(text_symbolizer const &sym) const
|
||||||
|
{
|
||||||
|
box2d<double> clip_box = clipping_extent_;
|
||||||
|
text_symbolizer_helper helper(
|
||||||
|
sym, feature_, prj_trans_,
|
||||||
|
common_.width_, common_.height_,
|
||||||
|
common_.scale_factor_,
|
||||||
|
common_.t_, common_.font_manager_, *common_.detector_,
|
||||||
|
clip_box);
|
||||||
|
|
||||||
|
composite_mode_e comp_op = get<composite_mode_e>(sym, keys::comp_op, feature_, src_over);
|
||||||
|
halo_rasterizer_enum halo_rasterizer = get<halo_rasterizer_enum>(sym, keys::halo_rasterizer, HALO_RASTERIZER_FULL);
|
||||||
|
|
||||||
|
placements_list const& placements = helper.get();
|
||||||
|
text_render_thunk thunk(placements, comp_op, halo_rasterizer);
|
||||||
|
thunks_.push_back(std::make_shared<render_thunk>(thunk));
|
||||||
|
|
||||||
|
update_box();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_thunk_extractor::update_box() const
|
||||||
|
{
|
||||||
|
label_collision_detector4 &detector = *common_.detector_;
|
||||||
|
|
||||||
|
for (auto const &label : detector)
|
||||||
|
{
|
||||||
|
if (box_.width() > 0 && box_.height() > 0)
|
||||||
|
{
|
||||||
|
box_.expand_to_include(label.box);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
box_ = label.box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detector.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
geometry_type *origin_point(proj_transform const &prj_trans,
|
||||||
|
renderer_common const &common)
|
||||||
|
{
|
||||||
|
// note that we choose a point in the middle of the screen to
|
||||||
|
// try to ensure that we don't get edge artefacts due to any
|
||||||
|
// symbolizers with avoid-edges set: only the avoid-edges of
|
||||||
|
// the group symbolizer itself should matter.
|
||||||
|
double x = common.width_ / 2.0, y = common.height_ / 2.0, z = 0.0;
|
||||||
|
common.t_.backward(&x, &y);
|
||||||
|
prj_trans.forward(x, y, z);
|
||||||
|
geometry_type *geom = new geometry_type(geometry_type::Point);
|
||||||
|
geom->move_to(x, y);
|
||||||
|
return geom;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mapnik
|
Loading…
Reference in a new issue