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')
|
||||
|
||||
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']:
|
||||
subdirs.append('svg/output')
|
||||
|
|
|
@ -80,72 +80,29 @@ 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);
|
||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||
|
||||
box2d<double> bbox(0,0, src_.width(),src_.height());
|
||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
||||
path.type() == geometry_type::types::Point)
|
||||
box2d<double> bbox(0,0, src_.width(), src_.height());
|
||||
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_finder<T, label_collision_detector4> 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() == 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))
|
||||
matrix.rotate(angle);
|
||||
matrix.translate(x, y);
|
||||
render_raster_marker(matrix);
|
||||
if (!placed_)
|
||||
{
|
||||
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 max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||
markers_placement<T, label_collision_detector4> placement(path, bbox, marker_trans_, detector_,
|
||||
spacing * scale_factor_,
|
||||
max_error,
|
||||
allow_overlap);
|
||||
double x, y, angle;
|
||||
while (placement.get_point(x, y, angle, ignore_placement))
|
||||
{
|
||||
agg::trans_affine matrix = marker_trans_;
|
||||
matrix.rotate(angle);
|
||||
matrix.translate(x, y);
|
||||
render_raster_marker(matrix);
|
||||
if (!placed_)
|
||||
{
|
||||
pixmap_.add_feature(feature_);
|
||||
placed_ = true;
|
||||
}
|
||||
pixmap_.add_feature(feature_);
|
||||
placed_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,69 +196,34 @@ struct vector_markers_rasterizer_dispatch_grid : mapnik::noncopyable
|
|||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||
double opacity = get<double>(sym_,keys::opacity, feature_, vars_, 1.0);
|
||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||
|
||||
coord2d center = bbox_.center();
|
||||
agg::trans_affine_translation recenter(-center.x, -center.y);
|
||||
|
||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
||||
path.type() == geometry_type::types::Point)
|
||||
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_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() == 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))
|
||||
matrix.rotate(angle);
|
||||
matrix.translate(x, y);
|
||||
svg_renderer_.render_id(ras_, sl_, renb_, feature_.id(), matrix, opacity, bbox_);
|
||||
if (!placed_)
|
||||
{
|
||||
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 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, y, angle;
|
||||
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_id(ras_, sl_, renb_, feature_.id(), matrix, opacity, bbox_);
|
||||
if (!placed_)
|
||||
{
|
||||
pixmap_.add_feature(feature_);
|
||||
placed_ = true;
|
||||
}
|
||||
pixmap_.add_feature(feature_);
|
||||
placed_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BufferType & buf_;
|
||||
pixfmt_type pixf_;
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
// boost
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
// std
|
||||
#include <memory>
|
||||
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include <mapnik/markers_placement.hpp>
|
||||
#include <mapnik/attribute.hpp>
|
||||
#include <mapnik/box2d.hpp>
|
||||
#include <mapnik/vertex_converters.hpp>
|
||||
#include <mapnik/label_collision_detector.hpp>
|
||||
|
||||
// agg
|
||||
#include "agg_ellipse.h"
|
||||
|
@ -111,69 +113,35 @@ struct vector_markers_rasterizer_dispatch : mapnik::noncopyable
|
|||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, 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);
|
||||
|
||||
coord2d center = bbox_.center();
|
||||
agg::trans_affine_translation recenter(-center.x, -center.y);
|
||||
|
||||
if (placement_method != MARKER_LINE_PLACEMENT ||
|
||||
path.type() == mapnik::geometry_type::types::Point)
|
||||
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_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_;
|
||||
matrix.translate(x,y);
|
||||
matrix.rotate(angle);
|
||||
matrix.translate(x, y);
|
||||
if (snap_to_pixels_)
|
||||
{
|
||||
// https://github.com/mapnik/mapnik/issues/1316
|
||||
matrix.tx = std::floor(matrix.tx+.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_);
|
||||
matrix.tx = std::floor(matrix.tx + .5);
|
||||
matrix.ty = std::floor(matrix.ty + .5);
|
||||
}
|
||||
svg_renderer_.render(ras_, sl_, renb_, matrix, opacity, bbox_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
BufferType & buf_;
|
||||
pixfmt_type pixf_;
|
||||
|
@ -236,54 +204,24 @@ struct raster_markers_rasterizer_dispatch : mapnik::noncopyable
|
|||
box2d<double> bbox_(0,0, src_.width(),src_.height());
|
||||
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.0);
|
||||
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 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_finder<T, label_collision_detector4> 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 = 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 max_error = get<double>(sym_, keys::max_error, feature_, vars_, 0.2);
|
||||
markers_placement<T, label_collision_detector4> placement(path, bbox_, marker_trans_, detector_,
|
||||
spacing * scale_factor_,
|
||||
max_error,
|
||||
allow_overlap);
|
||||
double x, y, angle;
|
||||
while (placement.get_point(x, y, angle, ignore_placement))
|
||||
{
|
||||
agg::trans_affine matrix = marker_trans_;
|
||||
matrix.rotate(angle);
|
||||
matrix.translate(x, y);
|
||||
render_raster_marker(matrix, opacity);
|
||||
}
|
||||
matrix.rotate(angle);
|
||||
matrix.translate(x, y);
|
||||
render_raster_marker(matrix, opacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,83 +23,55 @@
|
|||
#ifndef MAPNIK_MARKERS_PLACEMENT_HPP
|
||||
#define MAPNIK_MARKERS_PLACEMENT_HPP
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/ctrans.hpp>
|
||||
#include <mapnik/debug.hpp>
|
||||
#include <mapnik/label_collision_detector.hpp>
|
||||
#include <mapnik/global.hpp> //round
|
||||
#include <mapnik/box2d.hpp>
|
||||
#include <mapnik/noncopyable.hpp>
|
||||
#include <mapnik/markers_placements/line.hpp>
|
||||
#include <mapnik/markers_placements/point.hpp>
|
||||
#include <mapnik/markers_placements/interior.hpp>
|
||||
#include <mapnik/symbolizer_enumerations.hpp>
|
||||
|
||||
// agg
|
||||
#include "agg_basics.h"
|
||||
#include "agg_trans_affine.h"
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/functional/value_factory.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
// stl
|
||||
#include <cmath>
|
||||
|
||||
namespace mapnik {
|
||||
namespace mapnik
|
||||
{
|
||||
|
||||
template <typename Locator, typename Detector>
|
||||
class markers_placement : mapnik::noncopyable
|
||||
class markers_placement_finder : mapnik::noncopyable
|
||||
{
|
||||
public:
|
||||
/** Constructor for markers_placement object.
|
||||
* \param locator Path along which markers are placed (type: vertex source)
|
||||
* \param size Size of the marker
|
||||
* \param tr Affine transform
|
||||
* \param detector Collision detection
|
||||
* \param spacing Distance between markers. If the value is negative it is
|
||||
* converted to a positive value with similar magnitude, but
|
||||
* choosen to optimize marker placement. 0 = no markers
|
||||
*/
|
||||
markers_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),
|
||||
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();
|
||||
}
|
||||
using markers_placement = boost::variant<markers_point_placement<Locator, Detector>,
|
||||
markers_line_placement<Locator, Detector>,
|
||||
markers_interior_placement<Locator, Detector>>;
|
||||
|
||||
/** Start again at first marker.
|
||||
* \note Returns the same list of markers only works when they were NOT added
|
||||
* to the detector.
|
||||
*/
|
||||
void rewind()
|
||||
class get_point_visitor : public boost::static_visitor<bool>
|
||||
{
|
||||
public:
|
||||
get_point_visitor(double &x, double &y, double &angle, bool ignore_placement)
|
||||
: x_(x), y_(y), angle_(angle), ignore_placement_(ignore_placement)
|
||||
{
|
||||
}
|
||||
|
||||
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,
|
||||
agg::trans_affine const& tr,
|
||||
Detector &detector,
|
||||
double spacing,
|
||||
double max_error,
|
||||
bool allow_overlap)
|
||||
: placement_(create(placement_type, locator, size, tr, detector, spacing, max_error, allow_overlap))
|
||||
{
|
||||
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.
|
||||
|
@ -110,184 +82,40 @@ public:
|
|||
* \param ignore_placement Whether to add selected position to detector
|
||||
* \return True if a place is found, false if none is found.
|
||||
*/
|
||||
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 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;
|
||||
}
|
||||
return boost::apply_visitor(get_point_visitor(x, y, angle, ignore_placement), placement_);
|
||||
}
|
||||
|
||||
private:
|
||||
Locator &locator_;
|
||||
box2d<double> size_;
|
||||
agg::trans_affine tr_;
|
||||
Detector &detector_;
|
||||
double spacing_;
|
||||
double max_error_;
|
||||
bool allow_overlap_;
|
||||
double marker_width_;
|
||||
|
||||
bool done_;
|
||||
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)
|
||||
/** Factory function for particular placement implementations.
|
||||
*/
|
||||
static markers_placement create(marker_placement_e placement_type,
|
||||
Locator &locator,
|
||||
box2d<double> const& size,
|
||||
agg::trans_affine const& tr,
|
||||
Detector &detector,
|
||||
double spacing,
|
||||
double max_error,
|
||||
bool allow_overlap)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
static const std::map<marker_placement_e, boost::function<markers_placement(
|
||||
Locator &locator,
|
||||
box2d<double> const& size,
|
||||
agg::trans_affine const& tr,
|
||||
Detector &detector,
|
||||
double spacing,
|
||||
double max_error,
|
||||
bool allow_overlap)>> factories =
|
||||
{
|
||||
{ MARKER_POINT_PLACEMENT, boost::value_factory<markers_point_placement<Locator, Detector>>() },
|
||||
{ MARKER_INTERIOR_PLACEMENT, boost::value_factory<markers_interior_placement<Locator, Detector>>() },
|
||||
{ MARKER_LINE_PLACEMENT, boost::value_factory<markers_line_placement<Locator, Detector>>() }
|
||||
};
|
||||
return factories.at(placement_type)(locator, size, tr, detector, spacing, max_error, allow_overlap);
|
||||
}
|
||||
|
||||
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,63 +798,37 @@ struct markers_dispatch : mapnik::noncopyable
|
|||
template <typename T>
|
||||
void add_path(T & path)
|
||||
{
|
||||
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 allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.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);
|
||||
|
||||
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;
|
||||
}
|
||||
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_,
|
||||
max_error,
|
||||
allow_overlap);
|
||||
double x, y, angle;
|
||||
while (placement.get_point(x, y, angle, ignore_placement))
|
||||
{
|
||||
agg::trans_affine matrix = marker_trans_;
|
||||
matrix.rotate(angle);
|
||||
render_vector_marker(ctx_, pixel_position(x,y), marker_, bbox_, attributes_, matrix, opacity, true);
|
||||
|
||||
}
|
||||
}
|
||||
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 allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.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);
|
||||
markers_placement_finder<T, label_collision_detector4> 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))
|
||||
{
|
||||
agg::trans_affine matrix = marker_trans_;
|
||||
matrix.rotate(angle);
|
||||
render_vector_marker(
|
||||
ctx_,
|
||||
pixel_position(x, y),
|
||||
marker_,
|
||||
bbox_,
|
||||
attributes_,
|
||||
matrix,
|
||||
opacity,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
SvgPath & marker_;
|
||||
|
@ -893,67 +867,32 @@ struct raster_markers_dispatch : mapnik::noncopyable
|
|||
template <typename T>
|
||||
void add_path(T & path)
|
||||
{
|
||||
marker_placement_enum placement_method = get<marker_placement_enum>(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
||||
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.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);
|
||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||
|
||||
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;
|
||||
}
|
||||
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_,
|
||||
max_error,
|
||||
allow_overlap);
|
||||
double x, y, angle;
|
||||
while (placement.get_point(x, y, angle, ignore_placement))
|
||||
{
|
||||
coord2d center = bbox_.center();
|
||||
agg::trans_affine matrix = agg::trans_affine_translation(-center.x, -center.y);
|
||||
matrix *= marker_trans_;
|
||||
matrix *= agg::trans_affine_rotation(angle);
|
||||
matrix *= agg::trans_affine_translation(x, y);
|
||||
ctx_.add_image(matrix, marker_, opacity);
|
||||
}
|
||||
}
|
||||
marker_placement_enum placement_method = get<marker_placement_enum>(sym_, keys::markers_placement_type, feature_, vars_, MARKER_POINT_PLACEMENT);
|
||||
double opacity = get<double>(sym_, keys::opacity, feature_, vars_, 1.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);
|
||||
bool allow_overlap = get<bool>(sym_, keys::allow_overlap, feature_, vars_, false);
|
||||
bool ignore_placement = get<bool>(sym_, keys::ignore_placement, feature_, vars_, false);
|
||||
markers_placement_finder<T, label_collision_detector4> 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))
|
||||
{
|
||||
coord2d center = bbox_.center();
|
||||
agg::trans_affine matrix = agg::trans_affine_translation(
|
||||
-center.x, -center.y);
|
||||
matrix *= marker_trans_;
|
||||
matrix *= agg::trans_affine_rotation(angle);
|
||||
matrix *= agg::trans_affine_translation(x, y);
|
||||
ctx_.add_image(matrix, marker_, opacity);
|
||||
}
|
||||
}
|
||||
|
||||
ImageMarker & marker_;
|
||||
|
|
Loading…
Reference in a new issue