snake_geometry.cpp 6.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
#include "snake_geometry.h"
#include <mapbox/polylabel.hpp>
#include <mapbox/geometry.hpp>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/adapted/boost_tuple.hpp>

using namespace mapbox;
using namespace snake_geometrie;

BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian)


namespace snake_geometry {

Point3D toENU(const Point3D &WGS84Reference, const Point3D &WGS84Position)
{
    Point2D WGS84Reference2D{WGS84Reference[0], WGS84Reference[1]};
    Point2D WGS84Position2D{WGS84Position[0], WGS84Position[1]};

    Point2D ENUPosition2D = wgs84::toCartesian(WGS84Reference2D, WGS84Position2D);

    double deltaH = WGS84Position[3] - WGS84Reference[3];
    Point3D ENUPosition{ENUPosition2D[0], ENUPosition2D[1], deltaH};
    return ENUPosition;
}

Point3D fromENU(const Point3D &WGS84Reference, const Point3D &CartesianPosition)
{
    Point2D WGS84Reference2D{WGS84Reference[0], WGS84Reference[1]};
    Point2D CartesianPosition2D{CartesianPosition[0], CartesianPosition[1]};

    Point2D WGS84Position2D = wgs84::fromCartesian(WGS84Reference2D, CartesianPosition2D);

    double H = WGS84Reference[3] + CartesianPosition[3];
    Point3D WGS84Position{WGS84Position2D[0], WGS84Position2D[1], H};
    return WGS84Position;

}

Point2D polygonCenter(const Point2DList &polygon)
{
   geometry::polygon<double> p;
   geometry::linear_ring<double> lr1;
   for (size_t i = 0; i < polygon.size(); ++i) {
       geometry::point<double> vertex(polygon[i][0], polygon[i][1]);
       lr1.push_back(vertex);
   }
   p.push_back(lr1);
   geometry::point<double> center = polylabel(p);

   return Point2D{center.x, center.y};
}


}

min_bbox_rt minimalBoundingBox(const Point2DList &polygon)
{
    /*
    Find the minimum-area bounding box of a set of 2D points

    The input is a 2D convex hull, in an Nx2 numpy array of x-y co-ordinates.
    The first and last points points must be the same, making a closed polygon.
    This program finds the rotation angles of each edge of the convex polygon,
    then tests the area of a bounding box aligned with the unique angles in
    90 degrees of the 1st Quadrant.
    Returns the

    Tested with Python 2.6.5 on Ubuntu 10.04.4 (original version)
    Results verified using Matlab

    Copyright (c) 2013, David Butterworth, University of Queensland
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in the
          documentation and/or other materials provided with the distribution.
        * Neither the name of the Willow Garage, Inc. nor the names of its
          contributors may be used to endorse or promote products derived from
          this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
    */

    typedef boost::tuple<double, double> point_t;
    typedef boost::geometry::model::polygon<point_t> poly_t;
    namespace trans = boost::geometry::strategy::transform;

    poly_t poly;
    for (auto vertex : polygon)
        poly.outer().push_back(point_t{vertex[0], vertex[1]});

    poly_t convex_hull;
    boost::geometry::convex_hull(poly, convex_hull);

    //hull_points_2d = array(convex_polygon[0])
    //# print "Input convex hull points: "
    //# print hull_points_2d

    //# Compute edges (x2-x1,y2-y1)
    std::vector<point_t> edges;
    auto convex_hull_outer = convex_hull.outer();
    for (size_t i=0; i < convex_hull_outer.size(); ++i) {
        point_t p1      = convex_hull_outer.at(i);
        point_t p2      = convex_hull_outer.at(i+1);
        double edge_x   = p2.get<0>() - p1.get<0>();
        double edge_y   = p2.get<1>() - p1.get<1>();
        edges.push_back(point_t{edge_x, edge_y});
    }
    // print "Edges: \n", edges

    // Calculate edge angles   atan2(y/x)
    std::set<double> edge_angles;
    for (auto vertex : edges)
        edge_angles.insert(std::fmod(atan2(vertex.get<1>(), vertex.get<0>()), M_PI / 2)); // want strictly positive answers
    //# print "Unique edge angles: \n", edge_angles


    min_bbox_rt min_bbox{0, 0, 0, Point2D{0,0}};
    double min_area = std::numeric_limits<double>::infinity();
    // Test each angle to find bounding box with smallest area
    // print "Testing", len(edge_angles), "possible rotations for bounding box... \n"
    for (double angle : edge_angles){

        trans::rotate_transformer<boost::geometry::degree, double, 2, 2> rotate(angle*180/M_PI);
        poly_t hull_rotated;
        boost::geometry::transform(convex_hull, hull_rotated, rotate);

        boost::geometry::model::box<point_t> box;
        boost::geometry::envelope(hull_rotated, box);

        //# print "Rotated hull points are \n", rot_points
        point_t min_corner = box.min_corner();
        point_t max_corner = box.max_corner();
        double min_x = min_corner.get<0>();
        double max_x = min_corner.get<0>();
        double min_y = max_corner.get<1>();
        double max_y = max_corner.get<1>();
        //# print "Min x:", min_x, " Max x: ", max_x, "   Min y:", min_y, " Max y: ", max_y

        // Calculate height/width/area of this bounding rectangle
        double width    = max_x - min_x;
        double height   = max_y - min_y;
        double area     = width * height;
        // print "Potential bounding box ", i, ":  width: ", width, " height: ", height, "  area: ", area

        // Store the smallest rect found first (a simple convex hull might have 2 answers with same area)
        if (area < min_area){
            min_bbox.angle  = angle;
            min_bbox.width  = width;
            min_bbox.height = height;

            point_t origin;
            trans::rotate_transformer<boost::geometry::degree, double, 2, 2> rotate(-angle*180/M_PI);
            boost::geometry::transform(min_corner, origin, rotate);
            min_bbox.origin = Point2D{origin.get<0>(), origin.get<1>()};
        }
    }

    return min_bbox;
}