#include "snake_geometry.h" #include #include #include #include #include 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 p; geometry::linear_ring lr1; for (size_t i = 0; i < polygon.size(); ++i) { geometry::point vertex(polygon[i][0], polygon[i][1]); lr1.push_back(vertex); } p.push_back(lr1); geometry::point 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 point_t; typedef boost::geometry::model::polygon 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 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 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::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 rotate(angle*180/M_PI); poly_t hull_rotated; boost::geometry::transform(convex_hull, hull_rotated, rotate); boost::geometry::model::box 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 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; }