Merge pull request #2295 from mapycz/3.x-szn-markers-symbolizer
refactor markers placements
This commit is contained in:
commit
ba999ff4be
9 changed files with 719 additions and 576 deletions
|
@ -25,7 +25,20 @@ from glob import glob
|
||||||
Import('env')
|
Import('env')
|
||||||
|
|
||||||
base = './mapnik/'
|
base = './mapnik/'
|
||||||
subdirs = ['','svg','wkt','cairo','grid','json','util','group','text','text/placements','text/formatting']
|
subdirs = [
|
||||||
|
'',
|
||||||
|
'svg',
|
||||||
|
'wkt',
|
||||||
|
'cairo',
|
||||||
|
'grid',
|
||||||
|
'json',
|
||||||
|
'util',
|
||||||
|
'group',
|
||||||
|
'text',
|
||||||
|
'text/placements',
|
||||||
|
'text/formatting',
|
||||||
|
'markers_placements'
|
||||||
|
]
|
||||||
|
|
||||||
if env['SVG_RENDERER']:
|
if env['SVG_RENDERER']:
|
||||||
subdirs.append('svg/output')
|
subdirs.append('svg/output')
|
||||||
|
|
|
@ -80,62 +80,20 @@ struct raster_markers_rasterizer_dispatch_grid : mapnik::noncopyable
|
||||||
marker_placement_enum placement_method = get<marker_placement_enum>(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
marker_placement_enum placement_method = get<marker_placement_enum>(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
||||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||||
|
|
||||||
box2d<double> bbox(0,0, src_.width(), src_.height());
|
box2d<double> bbox(0,0, src_.width(), src_.height());
|
||||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
|
||||||
path.type() == geometry_type::types::Point)
|
|
||||||
{
|
|
||||||
double x = 0;
|
|
||||||
double y = 0;
|
|
||||||
if (path.type() == geometry_type::types::LineString)
|
|
||||||
{
|
|
||||||
if (!label::middle_point(path, x, y))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
|
|
||||||
{
|
|
||||||
if (!label::interior_position(path, x, y))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!label::centroid(path, x, y))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
agg::trans_affine matrix = marker_trans_;
|
|
||||||
matrix.translate(x,y);
|
|
||||||
box2d<double> transformed_bbox = bbox * matrix;
|
|
||||||
if (allow_overlap ||
|
|
||||||
detector_.has_placement(transformed_bbox))
|
|
||||||
{
|
|
||||||
render_raster_marker(matrix);
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
detector_.insert(transformed_bbox);
|
|
||||||
}
|
|
||||||
if (!placed_)
|
|
||||||
{
|
|
||||||
pixmap_.add_feature(feature_);
|
|
||||||
placed_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
||||||
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||||
markers_placement<T, label_collision_detector4> placement(path, bbox, marker_trans_, detector_,
|
markers_placement_finder<T, label_collision_detector4> placement_finder(
|
||||||
|
placement_method,
|
||||||
|
path,
|
||||||
|
bbox,
|
||||||
|
marker_trans_,
|
||||||
|
detector_,
|
||||||
spacing * scale_factor_,
|
spacing * scale_factor_,
|
||||||
max_error,
|
max_error,
|
||||||
allow_overlap);
|
allow_overlap);
|
||||||
double x, y, angle;
|
double x, y, angle = .0;
|
||||||
while (placement.get_point(x, y, angle, ignore_placement))
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
||||||
{
|
{
|
||||||
agg::trans_affine matrix = marker_trans_;
|
agg::trans_affine matrix = marker_trans_;
|
||||||
matrix.rotate(angle);
|
matrix.rotate(angle);
|
||||||
|
@ -148,7 +106,6 @@ struct raster_markers_rasterizer_dispatch_grid : mapnik::noncopyable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void render_raster_marker(agg::trans_affine const& marker_tr)
|
void render_raster_marker(agg::trans_affine const& marker_tr)
|
||||||
{
|
{
|
||||||
|
@ -239,56 +196,21 @@ struct vector_markers_rasterizer_dispatch_grid : mapnik::noncopyable
|
||||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||||
double opacity = get<double>(sym_,keys::opacity, feature_, vars_, 1.0);
|
double opacity = get<double>(sym_,keys::opacity, feature_, vars_, 1.0);
|
||||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||||
|
|
||||||
coord2d center = bbox_.center();
|
coord2d center = bbox_.center();
|
||||||
agg::trans_affine_translation recenter(-center.x, -center.y);
|
agg::trans_affine_translation recenter(-center.x, -center.y);
|
||||||
|
|
||||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
|
||||||
path.type() == geometry_type::types::Point)
|
|
||||||
{
|
|
||||||
double x = 0;
|
|
||||||
double y = 0;
|
|
||||||
if (path.type() == geometry_type::types::LineString)
|
|
||||||
{
|
|
||||||
if (!label::middle_point(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
|
|
||||||
{
|
|
||||||
if (!label::interior_position(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!label::centroid(path, x, y)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
agg::trans_affine matrix = recenter * marker_trans_;
|
|
||||||
matrix.translate(x,y);
|
|
||||||
box2d<double> transformed_bbox = bbox_ * matrix;
|
|
||||||
if (allow_overlap ||
|
|
||||||
detector_.has_placement(transformed_bbox))
|
|
||||||
{
|
|
||||||
svg_renderer_.render_id(ras_, sl_, renb_, feature_.id(), matrix, opacity, bbox_);
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
detector_.insert(transformed_bbox);
|
|
||||||
}
|
|
||||||
if (!placed_)
|
|
||||||
{
|
|
||||||
pixmap_.add_feature(feature_);
|
|
||||||
placed_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
||||||
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||||
markers_placement<T, Detector> placement(path, bbox_, marker_trans_, detector_,
|
markers_placement_finder<T, Detector> placement_finder(
|
||||||
|
placement_method,
|
||||||
|
path,
|
||||||
|
bbox_,
|
||||||
|
marker_trans_,
|
||||||
|
detector_,
|
||||||
spacing * scale_factor_,
|
spacing * scale_factor_,
|
||||||
max_error,
|
max_error,
|
||||||
allow_overlap);
|
allow_overlap);
|
||||||
double x, y, angle;
|
double x, y, angle = .0;
|
||||||
while (placement.get_point(x, y, angle, ignore_placement))
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
||||||
{
|
{
|
||||||
agg::trans_affine matrix = recenter * marker_trans_;
|
agg::trans_affine matrix = recenter * marker_trans_;
|
||||||
matrix.rotate(angle);
|
matrix.rotate(angle);
|
||||||
|
@ -301,7 +223,7 @@ struct vector_markers_rasterizer_dispatch_grid : mapnik::noncopyable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
BufferType & buf_;
|
BufferType & buf_;
|
||||||
pixfmt_type pixf_;
|
pixfmt_type pixf_;
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
// boost
|
// boost
|
||||||
#include <boost/variant.hpp>
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
|
// std
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace mapnik
|
namespace mapnik
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
#include <mapnik/markers_placement.hpp>
|
#include <mapnik/markers_placement.hpp>
|
||||||
#include <mapnik/attribute.hpp>
|
#include <mapnik/attribute.hpp>
|
||||||
#include <mapnik/box2d.hpp>
|
#include <mapnik/box2d.hpp>
|
||||||
|
#include <mapnik/vertex_converters.hpp>
|
||||||
|
#include <mapnik/label_collision_detector.hpp>
|
||||||
|
|
||||||
// agg
|
// agg
|
||||||
#include "agg_ellipse.h"
|
#include "agg_ellipse.h"
|
||||||
|
@ -111,28 +113,24 @@ struct vector_markers_rasterizer_dispatch : mapnik::noncopyable
|
||||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||||
double opacity = get<double>(sym_,keys::opacity, feature_, vars_, 1.0);
|
double opacity = get<double>(sym_,keys::opacity, feature_, vars_, 1.0);
|
||||||
|
|
||||||
coord2d center = bbox_.center();
|
coord2d center = bbox_.center();
|
||||||
agg::trans_affine_translation recenter(-center.x, -center.y);
|
agg::trans_affine_translation recenter(-center.x, -center.y);
|
||||||
|
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
||||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||||
path.type() == mapnik::geometry_type::types::Point)
|
markers_placement_finder<T, Detector> placement_finder(
|
||||||
|
placement_method,
|
||||||
|
path,
|
||||||
|
bbox_,
|
||||||
|
marker_trans_,
|
||||||
|
detector_,
|
||||||
|
spacing * scale_factor_,
|
||||||
|
max_error,
|
||||||
|
allow_overlap);
|
||||||
|
double x, y, angle = .0;
|
||||||
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
||||||
{
|
{
|
||||||
double x = 0;
|
|
||||||
double y = 0;
|
|
||||||
if (path.type() == mapnik::geometry_type::types::LineString)
|
|
||||||
{
|
|
||||||
if (!label::middle_point(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
|
|
||||||
{
|
|
||||||
if (!label::interior_position(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!label::centroid(path, x, y)) return;
|
|
||||||
}
|
|
||||||
agg::trans_affine matrix = recenter * marker_trans_;
|
agg::trans_affine matrix = recenter * marker_trans_;
|
||||||
|
matrix.rotate(angle);
|
||||||
matrix.translate(x, y);
|
matrix.translate(x, y);
|
||||||
if (snap_to_pixels_)
|
if (snap_to_pixels_)
|
||||||
{
|
{
|
||||||
|
@ -140,40 +138,10 @@ struct vector_markers_rasterizer_dispatch : mapnik::noncopyable
|
||||||
matrix.tx = std::floor(matrix.tx + .5);
|
matrix.tx = std::floor(matrix.tx + .5);
|
||||||
matrix.ty = std::floor(matrix.ty + .5);
|
matrix.ty = std::floor(matrix.ty + .5);
|
||||||
}
|
}
|
||||||
// TODO https://github.com/mapnik/mapnik/issues/1754
|
|
||||||
box2d<double> transformed_bbox = bbox_ * matrix;
|
|
||||||
|
|
||||||
if (allow_overlap ||
|
|
||||||
detector_.has_placement(transformed_bbox))
|
|
||||||
{
|
|
||||||
svg_renderer_.render(ras_, sl_, renb_, matrix, opacity, bbox_);
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
detector_.insert(transformed_bbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
|
||||||
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
|
||||||
markers_placement<T, Detector> placement(path, bbox_, marker_trans_, detector_,
|
|
||||||
spacing * scale_factor_,
|
|
||||||
max_error,
|
|
||||||
allow_overlap);
|
|
||||||
double x = 0;
|
|
||||||
double y = 0;
|
|
||||||
double angle = 0;
|
|
||||||
while (placement.get_point(x, y, angle, ignore_placement))
|
|
||||||
{
|
|
||||||
|
|
||||||
agg::trans_affine matrix = recenter * marker_trans_;
|
|
||||||
matrix.rotate(angle);
|
|
||||||
matrix.translate(x, y);
|
|
||||||
svg_renderer_.render(ras_, sl_, renb_, matrix, opacity, bbox_);
|
svg_renderer_.render(ras_, sl_, renb_, matrix, opacity, bbox_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
BufferType & buf_;
|
BufferType & buf_;
|
||||||
pixfmt_type pixf_;
|
pixfmt_type pixf_;
|
||||||
|
@ -236,48 +204,19 @@ struct raster_markers_rasterizer_dispatch : mapnik::noncopyable
|
||||||
box2d<double> bbox_(0,0, src_.width(),src_.height());
|
box2d<double> bbox_(0,0, src_.width(),src_.height());
|
||||||
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0);
|
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0);
|
||||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||||
|
|
||||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
|
||||||
path.type() == mapnik::geometry_type::types::Point)
|
|
||||||
{
|
|
||||||
double x = 0;
|
|
||||||
double y = 0;
|
|
||||||
if (path.type() == mapnik::geometry_type::types::LineString)
|
|
||||||
{
|
|
||||||
if (!label::middle_point(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
|
|
||||||
{
|
|
||||||
if (!label::interior_position(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!label::centroid(path, x, y)) return;
|
|
||||||
}
|
|
||||||
agg::trans_affine matrix = marker_trans_;
|
|
||||||
matrix.translate(x,y);
|
|
||||||
box2d<double> transformed_bbox = bbox_ * matrix;
|
|
||||||
|
|
||||||
if (allow_overlap ||
|
|
||||||
detector_.has_placement(transformed_bbox))
|
|
||||||
{
|
|
||||||
render_raster_marker(matrix, opacity);
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
detector_.insert(transformed_bbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
||||||
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||||
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
|
markers_placement_finder<T, label_collision_detector4> placement_finder(
|
||||||
|
placement_method,
|
||||||
|
path,
|
||||||
|
bbox_,
|
||||||
|
marker_trans_,
|
||||||
|
detector_,
|
||||||
spacing * scale_factor_,
|
spacing * scale_factor_,
|
||||||
max_error,
|
max_error,
|
||||||
allow_overlap);
|
allow_overlap);
|
||||||
double x, y, angle;
|
double x, y, angle = .0;
|
||||||
while (placement.get_point(x, y, angle, ignore_placement))
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
||||||
{
|
{
|
||||||
agg::trans_affine matrix = marker_trans_;
|
agg::trans_affine matrix = marker_trans_;
|
||||||
matrix.rotate(angle);
|
matrix.rotate(angle);
|
||||||
|
@ -285,7 +224,6 @@ struct raster_markers_rasterizer_dispatch : mapnik::noncopyable
|
||||||
render_raster_marker(matrix, opacity);
|
render_raster_marker(matrix, opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void render_raster_marker(agg::trans_affine const& marker_tr,
|
void render_raster_marker(agg::trans_affine const& marker_tr,
|
||||||
double opacity)
|
double opacity)
|
||||||
|
|
|
@ -23,83 +23,55 @@
|
||||||
#ifndef MAPNIK_MARKERS_PLACEMENT_HPP
|
#ifndef MAPNIK_MARKERS_PLACEMENT_HPP
|
||||||
#define MAPNIK_MARKERS_PLACEMENT_HPP
|
#define MAPNIK_MARKERS_PLACEMENT_HPP
|
||||||
|
|
||||||
// mapnik
|
#include <mapnik/markers_placements/line.hpp>
|
||||||
#include <mapnik/ctrans.hpp>
|
#include <mapnik/markers_placements/point.hpp>
|
||||||
#include <mapnik/debug.hpp>
|
#include <mapnik/markers_placements/interior.hpp>
|
||||||
#include <mapnik/label_collision_detector.hpp>
|
#include <mapnik/symbolizer_enumerations.hpp>
|
||||||
#include <mapnik/global.hpp> //round
|
|
||||||
#include <mapnik/box2d.hpp>
|
|
||||||
#include <mapnik/noncopyable.hpp>
|
|
||||||
|
|
||||||
// agg
|
#include <boost/variant.hpp>
|
||||||
#include "agg_basics.h"
|
#include <boost/functional/value_factory.hpp>
|
||||||
#include "agg_trans_affine.h"
|
#include <boost/function.hpp>
|
||||||
|
|
||||||
// stl
|
namespace mapnik
|
||||||
#include <cmath>
|
{
|
||||||
|
|
||||||
namespace mapnik {
|
|
||||||
|
|
||||||
template <typename Locator, typename Detector>
|
template <typename Locator, typename Detector>
|
||||||
class markers_placement : mapnik::noncopyable
|
class markers_placement_finder : mapnik::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Constructor for markers_placement object.
|
using markers_placement = boost::variant<markers_point_placement<Locator, Detector>,
|
||||||
* \param locator Path along which markers are placed (type: vertex source)
|
markers_line_placement<Locator, Detector>,
|
||||||
* \param size Size of the marker
|
markers_interior_placement<Locator, Detector>>;
|
||||||
* \param tr Affine transform
|
|
||||||
* \param detector Collision detection
|
class get_point_visitor : public boost::static_visitor<bool>
|
||||||
* \param spacing Distance between markers. If the value is negative it is
|
{
|
||||||
* converted to a positive value with similar magnitude, but
|
public:
|
||||||
* choosen to optimize marker placement. 0 = no markers
|
get_point_visitor(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
*/
|
: x_(x), y_(y), angle_(angle), ignore_placement_(ignore_placement)
|
||||||
markers_placement(Locator &locator,
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool operator()(T &placement) const
|
||||||
|
{
|
||||||
|
return placement.get_point(x_, y_, angle_, ignore_placement_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
double &x_, &y_, &angle_;
|
||||||
|
bool ignore_placement_;
|
||||||
|
};
|
||||||
|
|
||||||
|
markers_placement_finder(marker_placement_e placement_type,
|
||||||
|
Locator &locator,
|
||||||
box2d<double> const& size,
|
box2d<double> const& size,
|
||||||
agg::trans_affine const& tr,
|
agg::trans_affine const& tr,
|
||||||
Detector &detector,
|
Detector &detector,
|
||||||
double spacing,
|
double spacing,
|
||||||
double max_error,
|
double max_error,
|
||||||
bool allow_overlap)
|
bool allow_overlap)
|
||||||
: locator_(locator),
|
: placement_(create(placement_type, locator, size, tr, detector, spacing, max_error, allow_overlap))
|
||||||
size_(size),
|
|
||||||
tr_(tr),
|
|
||||||
detector_(detector),
|
|
||||||
max_error_(max_error),
|
|
||||||
allow_overlap_(allow_overlap),
|
|
||||||
marker_width_((size_ * tr_).width()),
|
|
||||||
done_(false),
|
|
||||||
last_x(0.0),
|
|
||||||
last_y(0.0),
|
|
||||||
next_x(0.0),
|
|
||||||
next_y(0.0),
|
|
||||||
error_(0.0),
|
|
||||||
spacing_left_(0.0),
|
|
||||||
marker_nr_(0)
|
|
||||||
{
|
{
|
||||||
if (spacing >= 1)
|
|
||||||
{
|
|
||||||
spacing_ = spacing;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spacing_ = 100;
|
|
||||||
}
|
|
||||||
rewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Start again at first marker.
|
|
||||||
* \note Returns the same list of markers only works when they were NOT added
|
|
||||||
* to the detector.
|
|
||||||
*/
|
|
||||||
void rewind()
|
|
||||||
{
|
|
||||||
locator_.rewind(0);
|
|
||||||
//Get first point
|
|
||||||
done_ = agg::is_stop(locator_.vertex(&next_x, &next_y));
|
|
||||||
last_x = next_x;
|
|
||||||
last_y = next_y; // Force request of new segment
|
|
||||||
error_ = 0.0;
|
|
||||||
marker_nr_ = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get a point where the marker should be placed.
|
/** Get a point where the marker should be placed.
|
||||||
|
@ -112,182 +84,38 @@ public:
|
||||||
*/
|
*/
|
||||||
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
{
|
{
|
||||||
if (done_)
|
return boost::apply_visitor(get_point_visitor(x, y, angle, ignore_placement), placement_);
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
unsigned cmd;
|
|
||||||
/* This functions starts at the position of the previous marker,
|
|
||||||
walks along the path, counting how far it has to go in spacing_left.
|
|
||||||
If one marker can't be placed at the position it should go to it is
|
|
||||||
moved a bit. The error is compensated for in the next call to this
|
|
||||||
function.
|
|
||||||
|
|
||||||
error > 0: Marker too near to the end of the path.
|
|
||||||
error = 0: Perfect position.
|
|
||||||
error < 0: Marker too near to the beginning of the path.
|
|
||||||
*/
|
|
||||||
if (marker_nr_ == 0)
|
|
||||||
{
|
|
||||||
//First marker
|
|
||||||
marker_nr_++;
|
|
||||||
spacing_left_ = spacing_ / 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spacing_left_ = spacing_;
|
|
||||||
}
|
|
||||||
spacing_left_ -= error_;
|
|
||||||
error_ = 0.0;
|
|
||||||
double max_err_allowed = max_error_ * spacing_;
|
|
||||||
// Loop exits when a position is found or when no more segments are available
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
// Do not place markers too close to the beginning of a segment
|
|
||||||
if (spacing_left_ < marker_width_/2)
|
|
||||||
{
|
|
||||||
set_spacing_left(marker_width_/2); //Only moves forward
|
|
||||||
}
|
|
||||||
// Error for this marker is too large. Skip to the next position.
|
|
||||||
if (std::fabs(error_) > max_err_allowed)
|
|
||||||
{
|
|
||||||
if (error_ > spacing_)
|
|
||||||
{
|
|
||||||
error_ = spacing_; // Avoid moving backwards
|
|
||||||
}
|
|
||||||
spacing_left_ += spacing_ - error_;
|
|
||||||
error_ = 0.0;
|
|
||||||
}
|
|
||||||
double dx = next_x - last_x;
|
|
||||||
double dy = next_y - last_y;
|
|
||||||
double segment_length = std::sqrt(dx * dx + dy * dy);
|
|
||||||
if (segment_length <= spacing_left_)
|
|
||||||
{
|
|
||||||
//Segment is too short to place marker. Find next segment
|
|
||||||
spacing_left_ -= segment_length;
|
|
||||||
last_x = next_x;
|
|
||||||
last_y = next_y;
|
|
||||||
while (agg::is_move_to(cmd = locator_.vertex(&next_x, &next_y)))
|
|
||||||
{
|
|
||||||
//Skip over "move" commands
|
|
||||||
last_x = next_x;
|
|
||||||
last_y = next_y;
|
|
||||||
}
|
|
||||||
if (agg::is_stop(cmd) || cmd == SEG_CLOSE)
|
|
||||||
{
|
|
||||||
done_ = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
continue; //Try again
|
|
||||||
}
|
|
||||||
/* At this point we know the following things:
|
|
||||||
- segment_length > spacing_left
|
|
||||||
- error is small enough
|
|
||||||
- at least half a marker fits into this segment
|
|
||||||
*/
|
|
||||||
//Check if marker really fits in this segment
|
|
||||||
if (segment_length < marker_width_)
|
|
||||||
{
|
|
||||||
//Segment to short => Skip this segment
|
|
||||||
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (segment_length - spacing_left_ < marker_width_/2)
|
|
||||||
{
|
|
||||||
//Segment is long enough, but we are to close to the end
|
|
||||||
//Note: This function moves backwards. This could lead to an infinite
|
|
||||||
// loop when another function adds a positive offset. Therefore we
|
|
||||||
// only move backwards when there is no offset
|
|
||||||
if (error_ == 0)
|
|
||||||
{
|
|
||||||
set_spacing_left(segment_length - marker_width_/2, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Skip this segment
|
|
||||||
set_spacing_left(segment_length + marker_width_/2); //Only moves forward
|
|
||||||
}
|
|
||||||
continue; //Force checking of max_error constraint
|
|
||||||
}
|
|
||||||
angle = std::atan2(dy, dx);
|
|
||||||
x = last_x + dx * (spacing_left_ / segment_length);
|
|
||||||
y = last_y + dy * (spacing_left_ / segment_length);
|
|
||||||
box2d<double> box = perform_transform(angle, x, y);
|
|
||||||
if (!allow_overlap_ && !detector_.has_placement(box))
|
|
||||||
{
|
|
||||||
//10.0 is the approximate number of positions tried and choosen arbitrarily
|
|
||||||
set_spacing_left(spacing_left_ + spacing_ * max_error_ / 10.0); //Only moves forward
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
detector_.insert(box);
|
|
||||||
}
|
|
||||||
last_x = x;
|
|
||||||
last_y = y;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Locator &locator_;
|
/** Factory function for particular placement implementations.
|
||||||
box2d<double> size_;
|
*/
|
||||||
agg::trans_affine tr_;
|
static markers_placement create(marker_placement_e placement_type,
|
||||||
Detector &detector_;
|
Locator &locator,
|
||||||
double spacing_;
|
box2d<double> const& size,
|
||||||
double max_error_;
|
agg::trans_affine const& tr,
|
||||||
bool allow_overlap_;
|
Detector &detector,
|
||||||
double marker_width_;
|
double spacing,
|
||||||
|
double max_error,
|
||||||
bool done_;
|
bool allow_overlap)
|
||||||
double last_x;
|
|
||||||
double last_y;
|
|
||||||
double next_x;
|
|
||||||
double next_y;
|
|
||||||
/** If a marker could not be placed at the exact point where it should
|
|
||||||
* go the next marker's distance will be a bit lower. */
|
|
||||||
double error_;
|
|
||||||
double spacing_left_;
|
|
||||||
unsigned marker_nr_;
|
|
||||||
|
|
||||||
/** Rotates the size_ box and translates the position. */
|
|
||||||
box2d<double> perform_transform(double angle, double dx, double dy)
|
|
||||||
{
|
{
|
||||||
double x1 = size_.minx();
|
static const std::map<marker_placement_e, boost::function<markers_placement(
|
||||||
double x2 = size_.maxx();
|
Locator &locator,
|
||||||
double y1 = size_.miny();
|
box2d<double> const& size,
|
||||||
double y2 = size_.maxy();
|
agg::trans_affine const& tr,
|
||||||
agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle).translate(dx, dy);
|
Detector &detector,
|
||||||
double xA = x1, yA = y1, xB = x2, yB = y1, xC = x2, yC = y2, xD = x1, yD = y2;
|
double spacing,
|
||||||
tr.transform(&xA, &yA);
|
double max_error,
|
||||||
tr.transform(&xB, &yB);
|
bool allow_overlap)>> factories =
|
||||||
tr.transform(&xC, &yC);
|
{
|
||||||
tr.transform(&xD, &yD);
|
{ MARKER_POINT_PLACEMENT, boost::value_factory<markers_point_placement<Locator, Detector>>() },
|
||||||
box2d<double> result(xA, yA, xC, yC);
|
{ MARKER_INTERIOR_PLACEMENT, boost::value_factory<markers_interior_placement<Locator, Detector>>() },
|
||||||
result.expand_to_include(xB, yB);
|
{ MARKER_LINE_PLACEMENT, boost::value_factory<markers_line_placement<Locator, Detector>>() }
|
||||||
result.expand_to_include(xD, yD);
|
};
|
||||||
return result;
|
return factories.at(placement_type)(locator, size, tr, detector, spacing, max_error, allow_overlap);
|
||||||
}
|
|
||||||
|
|
||||||
/** Set spacing_left_, adjusts error_ and performs sanity checks. */
|
|
||||||
void set_spacing_left(double sl, bool allow_negative=false)
|
|
||||||
{
|
|
||||||
double delta_error = sl - spacing_left_;
|
|
||||||
if (!allow_negative && delta_error < 0)
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_WARN(markers_placement) << "Unexpected negative error in markers_placement. Please file a bug report.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#ifdef MAPNIK_DEBUG
|
|
||||||
if (delta_error == 0.0)
|
|
||||||
{
|
|
||||||
MAPNIK_LOG_WARN(markers_placement) << "Not moving at all in set_spacing_left()! Please file a bug report.";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
error_ += delta_error;
|
|
||||||
spacing_left_ = sl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
markers_placement placement_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
99
include/mapnik/markers_placements/interior.hpp
Normal file
99
include/mapnik/markers_placements/interior.hpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 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_MARKERS_PLACEMENTS_INTERIOR_HPP
|
||||||
|
#define MAPNIK_MARKERS_PLACEMENTS_INTERIOR_HPP
|
||||||
|
|
||||||
|
#include <mapnik/markers_placements/point.hpp>
|
||||||
|
#include <mapnik/geom_util.hpp>
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
template <typename Locator, typename Detector>
|
||||||
|
class markers_interior_placement : public markers_point_placement<Locator, Detector>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
markers_interior_placement(
|
||||||
|
Locator &locator,
|
||||||
|
box2d<double> const& size,
|
||||||
|
agg::trans_affine const& tr,
|
||||||
|
Detector &detector,
|
||||||
|
double spacing,
|
||||||
|
double max_error,
|
||||||
|
bool allow_overlap)
|
||||||
|
: markers_point_placement<Locator, Detector>(
|
||||||
|
locator, size, tr, detector,
|
||||||
|
spacing, max_error, allow_overlap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
|
{
|
||||||
|
if (this->done_)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->locator_.type() == mapnik::geometry_type::types::Point)
|
||||||
|
{
|
||||||
|
return markers_point_placement<Locator, Detector>::get_point(x, y, angle, ignore_placement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->locator_.type() == mapnik::geometry_type::types::LineString)
|
||||||
|
{
|
||||||
|
if (!label::middle_point(this->locator_, x, y))
|
||||||
|
{
|
||||||
|
this->done_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!label::interior_position(this->locator_, x, y))
|
||||||
|
{
|
||||||
|
this->done_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angle = 0;
|
||||||
|
|
||||||
|
box2d<double> box = this->perform_transform(angle, x, y);
|
||||||
|
|
||||||
|
if (!this->allow_overlap_ && !this->detector_.has_placement(box))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignore_placement)
|
||||||
|
{
|
||||||
|
this->detector_.insert(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->done_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MAPNIK_MARKERS_PLACEMENTS_INTERIOR_HPP
|
241
include/mapnik/markers_placements/line.hpp
Normal file
241
include/mapnik/markers_placements/line.hpp
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 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_MARKERS_PLACEMENTS_LINE_HPP
|
||||||
|
#define MAPNIK_MARKERS_PLACEMENTS_LINE_HPP
|
||||||
|
|
||||||
|
#include <mapnik/markers_placements/point.hpp>
|
||||||
|
#include <mapnik/ctrans.hpp>
|
||||||
|
#include <mapnik/debug.hpp>
|
||||||
|
#include <mapnik/global.hpp> //round
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
template <typename Locator, typename Detector>
|
||||||
|
class markers_line_placement : public markers_point_placement<Locator, Detector>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
markers_line_placement(Locator &locator,
|
||||||
|
box2d<double> const& size,
|
||||||
|
agg::trans_affine const& tr,
|
||||||
|
Detector &detector,
|
||||||
|
double spacing,
|
||||||
|
double max_error,
|
||||||
|
bool allow_overlap)
|
||||||
|
: markers_point_placement<Locator, Detector>(
|
||||||
|
locator, size, tr, detector,
|
||||||
|
spacing, max_error, allow_overlap),
|
||||||
|
last_x(0.0),
|
||||||
|
last_y(0.0),
|
||||||
|
next_x(0.0),
|
||||||
|
next_y(0.0),
|
||||||
|
error_(0.0),
|
||||||
|
spacing_left_(0.0),
|
||||||
|
marker_nr_(0)
|
||||||
|
{
|
||||||
|
if (spacing >= 1)
|
||||||
|
{
|
||||||
|
this->spacing_ = spacing;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->spacing_ = 100;
|
||||||
|
}
|
||||||
|
rewind();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rewind()
|
||||||
|
{
|
||||||
|
this->locator_.rewind(0);
|
||||||
|
//Get first point
|
||||||
|
this->done_ = agg::is_stop(this->locator_.vertex(&next_x, &next_y));
|
||||||
|
last_x = next_x;
|
||||||
|
last_y = next_y; // Force request of new segment
|
||||||
|
error_ = 0.0;
|
||||||
|
marker_nr_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
|
{
|
||||||
|
if (this->done_)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->locator_.type() == mapnik::geometry_type::types::Point)
|
||||||
|
{
|
||||||
|
return markers_point_placement<Locator, Detector>::get_point(x, y, angle, ignore_placement);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned cmd;
|
||||||
|
/* This functions starts at the position of the previous marker,
|
||||||
|
walks along the path, counting how far it has to go in spacing_left.
|
||||||
|
If one marker can't be placed at the position it should go to it is
|
||||||
|
moved a bit. The error is compensated for in the next call to this
|
||||||
|
function.
|
||||||
|
|
||||||
|
error > 0: Marker too near to the end of the path.
|
||||||
|
error = 0: Perfect position.
|
||||||
|
error < 0: Marker too near to the beginning of the path.
|
||||||
|
*/
|
||||||
|
if (marker_nr_ == 0)
|
||||||
|
{
|
||||||
|
//First marker
|
||||||
|
marker_nr_++;
|
||||||
|
this->spacing_left_ = this->spacing_ / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->spacing_left_ = this->spacing_;
|
||||||
|
}
|
||||||
|
spacing_left_ -= error_;
|
||||||
|
error_ = 0.0;
|
||||||
|
double max_err_allowed = this->max_error_ * this->spacing_;
|
||||||
|
//Loop exits when a position is found or when no more segments are available
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
//Do not place markers too close to the beginning of a segment
|
||||||
|
if (spacing_left_ < this->marker_width_/2)
|
||||||
|
{
|
||||||
|
set_spacing_left(this->marker_width_/2); //Only moves forward
|
||||||
|
}
|
||||||
|
//Error for this marker is too large. Skip to the next position.
|
||||||
|
if (std::fabs(error_) > max_err_allowed)
|
||||||
|
{
|
||||||
|
while (this->error_ > this->spacing_)
|
||||||
|
{
|
||||||
|
error_ -= this->spacing_; //Avoid moving backwards
|
||||||
|
}
|
||||||
|
spacing_left_ += this->spacing_ - this->error_;
|
||||||
|
error_ = 0.0;
|
||||||
|
}
|
||||||
|
double dx = next_x - last_x;
|
||||||
|
double dy = next_y - last_y;
|
||||||
|
double segment_length = std::sqrt(dx * dx + dy * dy);
|
||||||
|
if (segment_length <= spacing_left_)
|
||||||
|
{
|
||||||
|
//Segment is too short to place marker. Find next segment
|
||||||
|
spacing_left_ -= segment_length;
|
||||||
|
last_x = next_x;
|
||||||
|
last_y = next_y;
|
||||||
|
while (agg::is_move_to(cmd = this->locator_.vertex(&next_x, &next_y)))
|
||||||
|
{
|
||||||
|
//Skip over "move" commands
|
||||||
|
last_x = next_x;
|
||||||
|
last_y = next_y;
|
||||||
|
}
|
||||||
|
if (agg::is_stop(cmd) || cmd == SEG_CLOSE)
|
||||||
|
{
|
||||||
|
this->done_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue; //Try again
|
||||||
|
}
|
||||||
|
/* At this point we know the following things:
|
||||||
|
- segment_length > spacing_left
|
||||||
|
- error is small enough
|
||||||
|
- at least half a marker fits into this segment
|
||||||
|
*/
|
||||||
|
//Check if marker really fits in this segment
|
||||||
|
if (segment_length < this->marker_width_)
|
||||||
|
{
|
||||||
|
//Segment to short => Skip this segment
|
||||||
|
set_spacing_left(segment_length + this->marker_width_/2); //Only moves forward
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (segment_length - spacing_left_ < this->marker_width_/2)
|
||||||
|
{
|
||||||
|
//Segment is long enough, but we are to close to the end
|
||||||
|
//Note: This function moves backwards. This could lead to an infinite
|
||||||
|
// loop when another function adds a positive offset. Therefore we
|
||||||
|
// only move backwards when there is no offset
|
||||||
|
if (error_ == 0)
|
||||||
|
{
|
||||||
|
set_spacing_left(segment_length - this->marker_width_/2, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Skip this segment
|
||||||
|
set_spacing_left(segment_length + this->marker_width_/2); //Only moves forward
|
||||||
|
}
|
||||||
|
continue; //Force checking of max_error constraint
|
||||||
|
}
|
||||||
|
angle = std::atan2(dy, dx);
|
||||||
|
x = last_x + dx * (spacing_left_ / segment_length);
|
||||||
|
y = last_y + dy * (spacing_left_ / segment_length);
|
||||||
|
box2d<double> box = this->perform_transform(angle, x, y);
|
||||||
|
if (!this->allow_overlap_ && !this->detector_.has_placement(box))
|
||||||
|
{
|
||||||
|
//10.0 is the approxmiate number of positions tried and choosen arbitrarily
|
||||||
|
set_spacing_left(spacing_left_ + this->spacing_ * this->max_error_ / 10.0); //Only moves forward
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!ignore_placement)
|
||||||
|
{
|
||||||
|
this->detector_.insert(box);
|
||||||
|
}
|
||||||
|
last_x = x;
|
||||||
|
last_y = y;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
double last_x;
|
||||||
|
double last_y;
|
||||||
|
double next_x;
|
||||||
|
double next_y;
|
||||||
|
/** If a marker could not be placed at the exact point where it should
|
||||||
|
* go the next marker's distance will be a bit lower. */
|
||||||
|
double error_;
|
||||||
|
double spacing_left_;
|
||||||
|
unsigned marker_nr_;
|
||||||
|
|
||||||
|
/** Set spacing_left_, adjusts error_ and performs sanity checks. */
|
||||||
|
void set_spacing_left(double sl, bool allow_negative=false)
|
||||||
|
{
|
||||||
|
double delta_error = sl - spacing_left_;
|
||||||
|
if (!allow_negative && delta_error < 0)
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_WARN(markers_line_placement)
|
||||||
|
<< "Unexpected negative error in markers_line_placement. "
|
||||||
|
"Please file a bug report.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef MAPNIK_DEBUG
|
||||||
|
if (delta_error == 0.0)
|
||||||
|
{
|
||||||
|
MAPNIK_LOG_WARN(markers_line_placement)
|
||||||
|
<< "Not moving at all in set_spacing_left()! "
|
||||||
|
"Please file a bug report.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
error_ += delta_error;
|
||||||
|
spacing_left_ = sl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MAPNIK_MARKERS_PLACEMENTS_LINE_HPP
|
160
include/mapnik/markers_placements/point.hpp
Normal file
160
include/mapnik/markers_placements/point.hpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* This file is part of Mapnik (c++ mapping toolkit)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 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_MARKERS_PLACEMENTS_POINT_HPP
|
||||||
|
#define MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
|
||||||
|
|
||||||
|
#include <mapnik/markers_placements/point.hpp>
|
||||||
|
#include <mapnik/geom_util.hpp>
|
||||||
|
|
||||||
|
#include "agg_basics.h"
|
||||||
|
#include "agg_trans_affine.h"
|
||||||
|
|
||||||
|
namespace mapnik {
|
||||||
|
|
||||||
|
template <typename Locator, typename Detector>
|
||||||
|
class markers_point_placement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
markers_point_placement(
|
||||||
|
Locator &locator,
|
||||||
|
box2d<double> const& size,
|
||||||
|
agg::trans_affine const& tr,
|
||||||
|
Detector &detector,
|
||||||
|
double spacing,
|
||||||
|
double max_error,
|
||||||
|
bool allow_overlap)
|
||||||
|
: locator_(locator),
|
||||||
|
size_(size),
|
||||||
|
tr_(tr),
|
||||||
|
detector_(detector),
|
||||||
|
spacing_(spacing),
|
||||||
|
max_error_(max_error),
|
||||||
|
allow_overlap_(allow_overlap),
|
||||||
|
marker_width_((size_ * tr_).width()),
|
||||||
|
done_(false)
|
||||||
|
{
|
||||||
|
rewind();
|
||||||
|
|
||||||
|
coord2d center = size.center();
|
||||||
|
tr_ = agg::trans_affine_translation(-center.x, -center.y) * tr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Start again at first marker.
|
||||||
|
* \note Returns the same list of markers only works when they were NOT added
|
||||||
|
* to the detector.
|
||||||
|
*/
|
||||||
|
virtual void rewind()
|
||||||
|
{
|
||||||
|
locator_.rewind(0);
|
||||||
|
done_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a point where the marker should be placed.
|
||||||
|
* Each time this function is called a new point is returned.
|
||||||
|
* \param x Return value for x position
|
||||||
|
* \param y Return value for x position
|
||||||
|
* \param angle Return value for rotation angle
|
||||||
|
* \param ignore_placement Whether to add selected position to detector
|
||||||
|
* \return True if a place is found, false if none is found.
|
||||||
|
*/
|
||||||
|
virtual bool get_point(double &x, double &y, double &angle, bool ignore_placement)
|
||||||
|
{
|
||||||
|
if (done_)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locator_.type() == mapnik::geometry_type::types::LineString)
|
||||||
|
{
|
||||||
|
if (!label::middle_point(locator_, x, y))
|
||||||
|
{
|
||||||
|
done_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!label::centroid(locator_, x, y))
|
||||||
|
{
|
||||||
|
done_ = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angle = 0;
|
||||||
|
|
||||||
|
box2d<double> box = perform_transform(angle, x, y);
|
||||||
|
|
||||||
|
if (!allow_overlap_ && !detector_.has_placement(box))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignore_placement)
|
||||||
|
{
|
||||||
|
detector_.insert(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
done_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Locator &locator_;
|
||||||
|
box2d<double> size_;
|
||||||
|
agg::trans_affine tr_;
|
||||||
|
Detector &detector_;
|
||||||
|
double spacing_;
|
||||||
|
double max_error_;
|
||||||
|
bool allow_overlap_;
|
||||||
|
double marker_width_;
|
||||||
|
bool done_;
|
||||||
|
|
||||||
|
/** Rotates the size_ box and translates the position. */
|
||||||
|
box2d<double> perform_transform(double angle, double dx, double dy)
|
||||||
|
{
|
||||||
|
double x1 = size_.minx();
|
||||||
|
double x2 = size_.maxx();
|
||||||
|
double y1 = size_.miny();
|
||||||
|
double y2 = size_.maxy();
|
||||||
|
agg::trans_affine tr = tr_ * agg::trans_affine_rotation(angle)
|
||||||
|
.translate(dx, dy);
|
||||||
|
double xA = x1, yA = y1,
|
||||||
|
xB = x2, yB = y1,
|
||||||
|
xC = x2, yC = y2,
|
||||||
|
xD = x1, yD = y2;
|
||||||
|
tr.transform(&xA, &yA);
|
||||||
|
tr.transform(&xB, &yB);
|
||||||
|
tr.transform(&xC, &yC);
|
||||||
|
tr.transform(&xD, &yD);
|
||||||
|
box2d<double> result(xA, yA, xC, yC);
|
||||||
|
result.expand_to_include(xB, yB);
|
||||||
|
result.expand_to_include(xD, yD);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MAPNIK_MARKERS_PLACEMENTS_POINT_HPP
|
||||||
|
|
|
@ -798,62 +798,36 @@ struct markers_dispatch : mapnik::noncopyable
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void add_path(T & path)
|
void add_path(T & path)
|
||||||
{
|
{
|
||||||
marker_placement_enum placement_method = get<marker_placement_enum>(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
marker_placement_enum placement_method = get<marker_placement_enum>(
|
||||||
|
sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
||||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||||
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0);
|
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0);
|
||||||
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
double spacing = get<double>(sym_, keys::spacing, feature_, vars_, 100.0);
|
||||||
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||||
|
markers_placement_finder<T, label_collision_detector4> placement_finder(
|
||||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
placement_method,
|
||||||
path.type() == geometry_type::types::Point)
|
path,
|
||||||
{
|
bbox_,
|
||||||
double x = 0;
|
marker_trans_,
|
||||||
double y = 0;
|
detector_,
|
||||||
if (path.type() == geometry_type::types::LineString)
|
|
||||||
{
|
|
||||||
if (!label::middle_point(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
|
|
||||||
{
|
|
||||||
if (!label::interior_position(path, x, y)) return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!label::centroid(path, x, y)) return;
|
|
||||||
}
|
|
||||||
coord2d center = bbox_.center();
|
|
||||||
agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y);
|
|
||||||
matrix *= marker_trans_;
|
|
||||||
matrix *=agg::trans_affine_translation(x, y);
|
|
||||||
|
|
||||||
box2d<double> transformed_bbox = bbox_ * matrix;
|
|
||||||
|
|
||||||
if (allow_overlap ||
|
|
||||||
detector_.has_placement(transformed_bbox))
|
|
||||||
{
|
|
||||||
render_vector_marker(ctx_, pixel_position(x,y), marker_, bbox_, attributes_, marker_trans_, opacity, true);
|
|
||||||
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
detector_.insert(transformed_bbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
|
|
||||||
spacing * scale_factor_,
|
spacing * scale_factor_,
|
||||||
max_error,
|
max_error,
|
||||||
allow_overlap);
|
allow_overlap);
|
||||||
double x, y, angle;
|
double x, y, angle = .0;
|
||||||
while (placement.get_point(x, y, angle, ignore_placement))
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
||||||
{
|
{
|
||||||
agg::trans_affine matrix = marker_trans_;
|
agg::trans_affine matrix = marker_trans_;
|
||||||
matrix.rotate(angle);
|
matrix.rotate(angle);
|
||||||
render_vector_marker(ctx_, pixel_position(x,y), marker_, bbox_, attributes_, matrix, opacity, true);
|
render_vector_marker(
|
||||||
|
ctx_,
|
||||||
}
|
pixel_position(x, y),
|
||||||
|
marker_,
|
||||||
|
bbox_,
|
||||||
|
attributes_,
|
||||||
|
matrix,
|
||||||
|
opacity,
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -899,62 +873,27 @@ struct raster_markers_dispatch : mapnik::noncopyable
|
||||||
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
double max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||||
|
markers_placement_finder<T, label_collision_detector4> placement_finder(
|
||||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
placement_method,
|
||||||
path.type() == geometry_type::types::Point)
|
path,
|
||||||
{
|
bbox_,
|
||||||
double x = 0;
|
marker_trans_,
|
||||||
double y = 0;
|
detector_,
|
||||||
if (path.type() == geometry_type::types::LineString)
|
|
||||||
{
|
|
||||||
if (!label::middle_point(path, x, y))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (placement_method == MARKER_INTERIOR_PLACEMENT)
|
|
||||||
{
|
|
||||||
if (!label::interior_position(path, x, y))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!label::centroid(path, x, y))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
coord2d center = bbox_.center();
|
|
||||||
agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y);
|
|
||||||
matrix *= marker_trans_;
|
|
||||||
matrix *=agg::trans_affine_translation(x, y);
|
|
||||||
|
|
||||||
box2d<double> transformed_bbox = bbox_ * matrix;
|
|
||||||
|
|
||||||
if (allow_overlap ||
|
|
||||||
detector_.has_placement(transformed_bbox))
|
|
||||||
{
|
|
||||||
ctx_.add_image(matrix, marker_, opacity);
|
|
||||||
if (!ignore_placement)
|
|
||||||
{
|
|
||||||
detector_.insert(transformed_bbox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
|
|
||||||
spacing * scale_factor_,
|
spacing * scale_factor_,
|
||||||
max_error,
|
max_error,
|
||||||
allow_overlap);
|
allow_overlap);
|
||||||
double x, y, angle;
|
double x, y, angle = .0;
|
||||||
while (placement.get_point(x, y, angle, ignore_placement))
|
while (placement_finder.get_point(x, y, angle, ignore_placement))
|
||||||
{
|
{
|
||||||
coord2d center = bbox_.center();
|
coord2d center = bbox_.center();
|
||||||
agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y);
|
agg::trans_affine matrix = agg::trans_affine_translation(
|
||||||
|
-center.x, -center.y);
|
||||||
matrix *= marker_trans_;
|
matrix *= marker_trans_;
|
||||||
matrix *= agg::trans_affine_rotation(angle);
|
matrix *= agg::trans_affine_rotation(angle);
|
||||||
matrix *= agg::trans_affine_translation(x, y);
|
matrix *= agg::trans_affine_translation(x, y);
|
||||||
ctx_.add_image(matrix, marker_, opacity);
|
ctx_.add_image(matrix, marker_, opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ImageMarker & marker_;
|
ImageMarker & marker_;
|
||||||
Detector & detector_;
|
Detector & detector_;
|
||||||
|
|
Loading…
Reference in a new issue