BOOST C++ 获取线串的旋转或方向

BOOST C++ get rotation or orientation of a linestring

我正在寻找线串的旋转。

基本上我有一个类似

的线串
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));

我想知道我们是否可以知道线串的旋转。基本上,如果线串指向北方,我想知道。

您可以在“风向图”中查找反正切table。原始输出将是 [-π,+π] 所以假设我们想把它分成 8 段:

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"},
};

现在我们可以计算反正切并将其缩放为 [-1,+1] 在该查找中 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

版画

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

替代测试

另一个测试将单位向量 (NORTH) 旋转 16 个不同的角度:

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";
}

版画

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