Add new angled-point marker placement mode for lines

This adds a new mode called 'angled-point' to the marker-placement modes.
The full list of modes is then:
  point, angled-point, interior, line, vertex-first, vertex-last

Angled point is identical to point, except that when placing a marker on
a line, the marker's angle is taken from the angle of the line segment.

There is another possible use of the "angled-point" concept for polygons,
and that is for placing labels on stand (aka erf) polygons. By computing
a dominant angle for a mostly rectangular polygon, this can produce quite
good results. I'm not sure whether I should implement that now, or if I
could do that later.
This commit is contained in:
Ben Harper 2017-10-12 16:42:44 +02:00
parent f02a259011
commit 05849f0da5
5 changed files with 31 additions and 5 deletions

12
include/mapnik/geom_util.hpp Normal file → Executable file
View file

@ -35,6 +35,12 @@
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
// boost
#pragma GCC diagnostic push
#include <mapnik/warning_ignore.hpp>
#include <boost/optional.hpp>
#pragma GCC diagnostic pop
namespace mapnik namespace mapnik
{ {
template <typename T> template <typename T>
@ -298,7 +304,7 @@ bool hit_test_first(PathType & path, double x, double y)
namespace label { namespace label {
template <typename PathType> template <typename PathType>
bool middle_point(PathType & path, double & x, double & y) bool middle_point(PathType & path, double & x, double & y, boost::optional<double&> angle = boost::none)
{ {
double x0 = 0; double x0 = 0;
double y0 = 0; double y0 = 0;
@ -319,6 +325,10 @@ bool middle_point(PathType & path, double & x, double & y)
double r = (mid_length - dist)/seg_length; double r = (mid_length - dist)/seg_length;
x = x0 + (x1 - x0) * r; x = x0 + (x1 - x0) * r;
y = y0 + (y1 - y0) * r; y = y0 + (y1 - y0) * r;
if (angle)
{
*angle = atan2(y1 - y0, x1 - x0);
}
break; break;
} }
dist += seg_length; dist += seg_length;

View file

@ -49,6 +49,10 @@ public:
case MARKER_POINT_PLACEMENT: case MARKER_POINT_PLACEMENT:
construct(&point_, locator, detector, params); construct(&point_, locator, detector, params);
break; break;
case MARKER_ANGLED_POINT_PLACEMENT:
construct(&point_, locator, detector, params);
point_.use_angle(true);
break;
case MARKER_INTERIOR_PLACEMENT: case MARKER_INTERIOR_PLACEMENT:
construct(&interior_, locator, detector, params); construct(&interior_, locator, detector, params);
break; break;
@ -70,6 +74,7 @@ public:
{ {
default: default:
case MARKER_POINT_PLACEMENT: case MARKER_POINT_PLACEMENT:
case MARKER_ANGLED_POINT_PLACEMENT:
destroy(&point_); destroy(&point_);
break; break;
case MARKER_INTERIOR_PLACEMENT: case MARKER_INTERIOR_PLACEMENT:
@ -94,6 +99,7 @@ public:
{ {
default: default:
case MARKER_POINT_PLACEMENT: case MARKER_POINT_PLACEMENT:
case MARKER_ANGLED_POINT_PLACEMENT:
return point_.get_point(x, y, angle, ignore_placement); return point_.get_point(x, y, angle, ignore_placement);
case MARKER_INTERIOR_PLACEMENT: case MARKER_INTERIOR_PLACEMENT:
return interior_.get_point(x, y, angle, ignore_placement); return interior_.get_point(x, y, angle, ignore_placement);

16
include/mapnik/markers_placements/point.hpp Normal file → Executable file
View file

@ -38,11 +38,18 @@ public:
: markers_basic_placement(params), : markers_basic_placement(params),
locator_(locator), locator_(locator),
detector_(detector), detector_(detector),
done_(false) done_(false),
use_angle_(false)
{ {
locator_.rewind(0); locator_.rewind(0);
} }
// Use angle of line
void use_angle(bool enable)
{
use_angle_ = enable;
}
// Start again at first marker. Returns the same list of markers only works when they were NOT added to the detector. // Start again at first marker. Returns the same list of markers only works when they were NOT added to the detector.
void rewind() void rewind()
{ {
@ -58,9 +65,11 @@ public:
return false; return false;
} }
angle = 0;
if (this->locator_.type() == geometry::geometry_types::LineString) if (this->locator_.type() == geometry::geometry_types::LineString)
{ {
if (!label::middle_point(this->locator_, x, y)) if (!label::middle_point(this->locator_, x, y, use_angle_ ? boost::optional<double&>(angle) : boost::none))
{ {
this->done_ = true; this->done_ = true;
return false; return false;
@ -75,8 +84,6 @@ public:
} }
} }
angle = 0;
if (!this->push_to_detector(x, y, angle, ignore_placement)) if (!this->push_to_detector(x, y, angle, ignore_placement))
{ {
return false; return false;
@ -90,6 +97,7 @@ protected:
Locator & locator_; Locator & locator_;
Detector & detector_; Detector & detector_;
bool done_; bool done_;
bool use_angle_;
// Checks transformed box placement with collision detector. // Checks transformed box placement with collision detector.
// returns false if the box: // returns false if the box:

View file

@ -105,6 +105,7 @@ enum marker_placement_enum : std::uint8_t
MARKER_LINE_PLACEMENT, MARKER_LINE_PLACEMENT,
MARKER_VERTEX_FIRST_PLACEMENT, MARKER_VERTEX_FIRST_PLACEMENT,
MARKER_VERTEX_LAST_PLACEMENT, MARKER_VERTEX_LAST_PLACEMENT,
MARKER_ANGLED_POINT_PLACEMENT,
marker_placement_enum_MAX marker_placement_enum_MAX
}; };

View file

@ -67,6 +67,7 @@ static const char * marker_placement_strings[] = {
"line", "line",
"vertex-first", "vertex-first",
"vertex-last", "vertex-last",
"angled-point",
"" ""
}; };