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.
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.
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
An alternative test rotates a unit vector (NORTH) over 16 different angles:
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