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

View file

@ -49,6 +49,10 @@ public:
case MARKER_POINT_PLACEMENT:
construct(&point_, locator, detector, params);
break;
case MARKER_ANGLED_POINT_PLACEMENT:
construct(&point_, locator, detector, params);
point_.use_angle(true);
break;
case MARKER_INTERIOR_PLACEMENT:
construct(&interior_, locator, detector, params);
break;
@ -70,6 +74,7 @@ public:
{
default:
case MARKER_POINT_PLACEMENT:
case MARKER_ANGLED_POINT_PLACEMENT:
destroy(&point_);
break;
case MARKER_INTERIOR_PLACEMENT:
@ -94,6 +99,7 @@ public:
{
default:
case MARKER_POINT_PLACEMENT:
case MARKER_ANGLED_POINT_PLACEMENT:
return point_.get_point(x, y, angle, ignore_placement);
case MARKER_INTERIOR_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),
locator_(locator),
detector_(detector),
done_(false)
done_(false),
use_angle_(false)
{
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.
void rewind()
{
@ -58,9 +65,11 @@ public:
return false;
}
angle = 0;
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;
return false;
@ -75,8 +84,6 @@ public:
}
}
angle = 0;
if (!this->push_to_detector(x, y, angle, ignore_placement))
{
return false;
@ -90,6 +97,7 @@ protected:
Locator & locator_;
Detector & detector_;
bool done_;
bool use_angle_;
// Checks transformed box placement with collision detector.
// returns false if the box:

View file

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

View file

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