c++boostboost-geometry

BOOST C++ get rotation or orientation of a linestring


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.


Solution

  • 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