Search code examples
c++boostboost-geometry

boost geometry area with a vector of pairs?


Is there any way of making boost::geometry::area work with a vector of pairs? It says here https://www.boost.org/doc/libs/1_75_0/libs/geometry/doc/html/geometry/reference/algorithms/area/area_1.html that it should work with MultiPoint. So I have called BOOST_GEOMETRY_REGISTER_MULTI_POINT - which works for convex_hull, but it does not work for area. Or do I have to create a Polygon and append the points to it as shown here: https://www.boost.org/doc/libs/1_75_0/libs/geometry/doc/html/geometry/reference/models/model_polygon.html

This is my code:

#include <iostream>
#include <cstdlib>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/multi/geometries/register/multi_point.hpp>

BOOST_GEOMETRY_REGISTER_POINT_2D(decltype(std::pair<double, double>{}), double, cs::cartesian, first, second)
BOOST_GEOMETRY_REGISTER_MULTI_POINT(decltype(std::vector<std::pair<double, double>>{}))

using MultiPoint = std::vector<std::pair<double, double>>;
MultiPoint getHull(const MultiPoint& points)
{
  MultiPoint hull{};
  boost::geometry::convex_hull(points, hull);
  return hull; // This returns a vector of pairs of points respresenting a hull
}
double getHullArea(const MultiPoint& points)
{
  return boost::geometry::area(points); // This always return 0
}

int main()
{
    // Unit square
    auto points = MultiPoint{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0,0}};
    auto hull = getHull(points); // This works as expected
    auto area = getHullArea(hull); // This always return 0
}

Solution

  • Yes it support MultiPoint. And you get the documented behaviour:

    enter image description here

    So, you get area is 0, like you should be expecting.

    Clearly you want to adapt as an areal geometry. Let's assume your pairs are Points that form a Ring.

    using Point = std::pair<double, double>;
    using Ring  = std::vector<Point>;
    
    BOOST_GEOMETRY_REGISTER_POINT_2D(Point, double, cs::cartesian, first, second)
    BOOST_GEOMETRY_REGISTER_RING(Ring)
    

    Now it works as you should expect:

    Live On Coliru

    #include <iostream>
    #include <cstdlib>
    
    #include <boost/geometry.hpp>
    #include <boost/geometry/geometries/register/point.hpp>
    #include <boost/geometry/geometries/register/ring.hpp>
    namespace bg = boost::geometry;
    
    using Point = std::pair<double, double>;
    using Ring  = std::vector<Point>;
    
    BOOST_GEOMETRY_REGISTER_POINT_2D(Point, double, cs::cartesian, first, second)
    BOOST_GEOMETRY_REGISTER_RING(Ring)
    
    Ring getHull(const Ring& points) {
        Ring hull;
        bg::convex_hull(points, hull);
        return hull;
    }
    
    double getArea(const Ring& points) {
        return bg::area(points);
    }
    
    int main() {
        for (Ring points : {
                 Ring{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}},
                 Ring{{0, 0}, {0, 2}, {3, 2}, {3, 1}, {1, 1}, {1, 0}, {0, 0}},
             })
        {
            std::cout << "-----\nInput:\t" << bg::wkt(points) << "\n";
            if (std::string reason; !bg::is_valid(points)) {
                std::cout << "Input:\t" << reason << "\n";
                bg::correct(points);
                std::cout << bg::wkt(points) << "\n";
            }
    
            std::cout << "Hull:\t"      << bg::wkt(getHull(points)) << "\n";
            std::cout << "Area:\t"      << getArea(points)          << "\n";
            std::cout << "Hull Area:\t" << getArea(getHull(points)) << "\n";
        }
    }
    

    Prints

    -----
    Input:  POLYGON((0 0,0 1,1 1,1 0,0 0))
    Hull:   POLYGON((0 0,0 1,1 1,1 0,0 0))
    Area:   1
    Hull Area:      1
    -----
    Input:  POLYGON((0 0,0 2,3 2,3 1,1 1,1 0,0 0))
    Hull:   POLYGON((0 0,0 2,3 2,3 1,1 0,0 0))
    Area:   4
    Hull Area:      5
    

    As you can see I added a more interesting example:

    enter image description here