BOOST C++ get rotation or orientation of a linestring

Viewed 155

i'm trying to find the rotation of a linestring.

Basically i have a linestring like

typedef boost::geometry::model::linestring<point_type> linestring_type;
linestring_type line;
line.push_back(point_type(xx1,yy1));
line.push_back(point_type(xx2,yy2));

and i would like to know if we can know the rotation of a linestring. Basically, if the linestring point in direction of the north, i want to know.

1 Answers

You can lookup the arc-tangent in a "wind-rose" table. The raw output will be [-π,+π] so assume that we want to divide that in 8 segments:

double constexpr segment = 0.25;
struct {
    double      bound;
    char const* name;
    bool        operator<(double index) const { return index > bound; }
} constexpr table[] = {
    {-5*segment, "(none)"}, // for safety
    {-4*segment, "W"}, 
    {-3*segment, "SW"},
    {-2*segment, "S"},
    {-1*segment, "SE"},
    { 0*segment, "N"},
    {+1*segment, "NE"},
    {+2*segment, "E"},
    {+3*segment, "NW"},
    {+4*segment, "W"},
};

Now we can calculate the arctangent and scale it to be [-1,+1] on that lookup table:

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <iostream>
#include <string_view>

namespace bg = boost::geometry;
using point_type      = bg::model::d2::point_xy<int>;
using linestring_type = bg::model::linestring<point_type>;

std::string_view orientation(linestring_type const& ls)
{
    double constexpr segment = 0.25;
    struct {
        double      bound;
        char const* name;
        bool        operator<(double index) const { return index > bound; }
    } constexpr table[] = {
        {-5*segment, "(none)"}, // for safety
        {-4*segment, "W"}, 
        {-3*segment, "SW"},
        {-2*segment, "S"},
        {-1*segment, "SE"},
        { 0*segment, "N"},
        {+1*segment, "NE"},
        {+2*segment, "E"},
        {+3*segment, "NW"},
        {+4*segment, "W"},
    };

    assert(ls.size() == 2);
    auto delta = ls.back();
    bg::subtract_point(delta, ls.front());

    auto frac = atan2(delta.y(), delta.x()) / M_PI;
    std::cout << " [DEBUG " << frac << ", " << (180 * frac) << "°] ";

    return std::lower_bound( //
               std::begin(table), std::end(table), frac - (segment / 2))
        ->name;
}

int main()
{
    for (auto pt : { point_type //
             {0, 0},
             {1, 0},
             {1, 1},
             {0, 1},
             {1, -1},
             {0, -1},
             {-1, -1},
             {-1, 0},
             {-1, 1},
         }) {
        linestring_type line { {0,0}, pt };
        std::cout << bg::wkt(line) << " " << orientation(line) << "\n";
    }
}

Note the subtle-ish - segment / 2 to achieve "nearest" rounding.

Live On Coliru

Prints

LINESTRING(0 0,0 0)  [DEBUG 0, 0°] N
LINESTRING(0 0,1 0)  [DEBUG 0, 0°] N
LINESTRING(0 0,1 1)  [DEBUG 0.25, 45°] NE
LINESTRING(0 0,0 1)  [DEBUG 0.5, 90°] E
LINESTRING(0 0,1 -1)  [DEBUG -0.25, -45°] SE
LINESTRING(0 0,0 -1)  [DEBUG -0.5, -90°] S
LINESTRING(0 0,-1 -1)  [DEBUG -0.75, -135°] SW
LINESTRING(0 0,-1 0)  [DEBUG 1, 180°] W
LINESTRING(0 0,-1 1)  [DEBUG 0.75, 135°] NW

Alternative Test

An alternative test rotates a unit vector (NORTH) over 16 different angles:

Live On Coliru

linestring_type const NORTH{{0, 0}, {0, 1'000}};
for (double angle = 0; angle <= 360; angle += 360.0 / 16) {
    linestring_type ls;
    bg::transform(NORTH, ls, rotation(angle));
    std::cout << bg::wkt(ls) << " " << orientation(ls) << "\n";
}

Prints

LINESTRING(0 0,0 1000)  [DEBUG 0.5, 90°] E
LINESTRING(0 0,382 923)  [DEBUG 0.375094, 67.5169°] E
LINESTRING(0 0,707 707)  [DEBUG 0.25, 45°] NE
LINESTRING(0 0,923 382)  [DEBUG 0.124906, 22.4831°] N
LINESTRING(0 0,1000 0)  [DEBUG 0, 0°] N
LINESTRING(0 0,923 -382)  [DEBUG -0.124906, -22.4831°] N
LINESTRING(0 0,707 -707)  [DEBUG -0.25, -45°] SE
LINESTRING(0 0,382 -923)  [DEBUG -0.375094, -67.5169°] S
LINESTRING(0 0,0 -1000)  [DEBUG -0.5, -90°] S
LINESTRING(0 0,-382 -923)  [DEBUG -0.624906, -112.483°] S
LINESTRING(0 0,-707 -707)  [DEBUG -0.75, -135°] SW
LINESTRING(0 0,-923 -382)  [DEBUG -0.875094, -157.517°] W
LINESTRING(0 0,-1000 0)  [DEBUG 1, 180°] W
LINESTRING(0 0,-923 382)  [DEBUG 0.875094, 157.517°] W
LINESTRING(0 0,-707 707)  [DEBUG 0.75, 135°] NW
LINESTRING(0 0,-382 923)  [DEBUG 0.624906, 112.483°] E
LINESTRING(0 0,0 1000)  [DEBUG 0.5, 90°] E
Related