user4971772
user4971772

Reputation:

How can I use C++ boost geometry to create ring (donut) from two polgons?

I am trying to calculate the differences between 2 polygons (in fact, rectilinear shapes for simplicity). For example, shape1 has point list {0 0, 100, 0, 100 100, 0 100} and shape2 has point list {25 25, 75 25, 75 75, 25 75}. So by concept, I expect "shape1 - shape2" will provide me with 4 rectangular boxes, which can make a ring or donut shape. I don't know how to achieve that, but I found "boost" library online and tried it like this:

enter image description here

#include <iostream>
#include <list>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <boost/foreach.hpp>


int main()
{
    typedef boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > polygon;

    polygon green, blue;

    boost::geometry::read_wkt( "POLYGON((0 0 100 0 100 100 0 100))", green);

    boost::geometry::read_wkt( "POLYGON((25 25 75 22 75 75 25 75))", blue);

    std::list<polygon> output;
    boost::geometry::difference(green, blue, output);

    int i = 0;
    std::cout << "green - blue:" << std::endl;
    BOOST_FOREACH(polygon const& p, output)
    {
        std::cout << i++ << ": " << boost::geometry::area(p) << std::endl;
    }


    output.clear();
    boost::geometry::difference(blue, green, output);

    i = 0;
    std::cout << "blue - green:" << std::endl;
    BOOST_FOREACH(polygon const& p, output)
    {
        std::cout << i++ << ": " << boost::geometry::area(p) << std::endl;
    }


    return 0;
}

However, nothing was printed out ... My plan was to figure out a way to convert the output results to rectangular boxes {0 0 100 0 100 25 0 25} {0 75 100 75 100 100 0 100} {0 25 25 25 25 75 0 75} and {75 25 100 25 100 75 75 75},but I feel frustrated that the above code not print out anything at all. Could anyone give me some guidance? I have attached a picture showing what I want to do. In fact, I have already had all the point coordinates to represent this donut, so maybe I can skip "difference() function" ? If this is the case, the difficult part will be to convert the donut into rectangular segments. (Let us assume all polygons here are rectilinear shapes.) Thanks.

Upvotes: 2

Views: 1421

Answers (2)

sehe
sehe

Reputation: 393134

The other answer will end your frustration/confusion, this answer will make you more productive :)

And, yes, you can skip the difference function:

bg::read_wkt("POLYGON((0 0 0 100 100 100 100 0 0 0) (25 25 75 22 75 75 25 75 25 25))", donut);

This specifies the inner ring (in reverse orientation!).

Result:

enter image description here

Generated by this program:

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <iostream>
#include <fstream>

namespace bg = boost::geometry; 

int main() {
    typedef bg::model::d2::point_xy<double> point;
    typedef bg::model::polygon<point> polygon;

    polygon donut;
    bg::read_wkt("POLYGON((0 0 0 100 100 100 100 0 0 0) (25 25 75 22 75 75 25 75 25 25))", donut);

    std::ofstream svg("output.svg");
    boost::geometry::svg_mapper<point> mapper(svg, 400, 400);
    mapper.add(donut);

    mapper.map(donut, "fill-opacity:0.5;fill:rgb(0,0,153);stroke:rgb(0,0,200);stroke-width:2");
}

Upvotes: 1

sehe
sehe

Reputation: 393134

Your starting polygons do not have the required orientation. You can find this out for yourself with is_valid. Conveniently, you can use correct to fix the most common problems with your input data:

std::string reason;
if (!bg::is_valid(geometry, reason))
{
    std::cout << "Correcting " << bg::wkt(geometry) << " to ";
    bg::correct(geometry);
    std::cout << bg::wkt(geometry) << "\n";

    if (!bg::is_valid(geometry, reason))
        std::cout << "UNCORRECTIBLE: " << reason << "\n";
}

When you do, you will find:

Correcting POLYGON((0 0,100 0,100 100,0 100,0 0)) to POLYGON((0 0,0 100,100 100,100 0,0 0))
Correcting POLYGON((25 25,75 22,75 75,25 75,25 25)) to POLYGON((25 25,25 75,75 75,75 22,25 25))

Now, when you get the difference, you will get the expected outcome:

auto do_diff = [](std::string caption, auto const& a, auto const& b) {
    multi output;
    bg::difference(a, b, output);

    std::cout << caption << bg::wkt(output) << std::endl;
};

do_diff("green - blue:", green, blue);
do_diff("blue - green:", blue, green);

Full Demo

Live On Coliru

#include <iostream>
#include <fstream>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>

#include <boost/foreach.hpp>

namespace bg = boost::geometry; 

int main() {
    typedef bg::model::d2::point_xy<double> point;
    typedef bg::model::polygon<point> polygon;
    typedef bg::model::multi_polygon<polygon> multi;

    polygon green, blue;

    bg::read_wkt("POLYGON((0 0 100 0 100 100 0 100))", green);
    bg::read_wkt("POLYGON((25 25 75 22 75 75 25 75))", blue);

    auto test = [](polygon& geometry) {
        std::string reason;
        if (!bg::is_valid(geometry, reason))
        {
            std::cout << "Correcting " << bg::wkt(geometry) << " to ";
            bg::correct(geometry);
            std::cout << bg::wkt(geometry) << "\n";

            if (!bg::is_valid(geometry, reason))
                std::cout << "UNCORRECTIBLE: " << reason << "\n";
        }
    };

    test(green);
    test(blue);

    auto do_diff = [](std::string caption, polygon const& a, polygon const& b) {
        multi output;
        bg::difference(a, b, output);

        std::cout << caption << bg::wkt(output) << std::endl;
    };

    do_diff("green - blue:", green, blue);
    do_diff("blue - green:", blue, green);

    {
        std::ofstream svg("output.svg");
        boost::geometry::svg_mapper<point> mapper(svg, 400, 400);
        mapper.add(blue);
        mapper.add(green);

        mapper.map(blue, "fill-opacity:0.5;fill:rgb(0,0,153);stroke:rgb(0,0,200);stroke-width:2");
        mapper.map(green, "fill-opacity:0.5;fill:rgb(0,153,0);stroke:rgb(0,200,0);stroke-width:2");
    }
}

Prints

Correcting POLYGON((0 0,100 0,100 100,0 100,0 0)) to POLYGON((0 0,0 100,100 100,100 0,0 0))
Correcting POLYGON((25 25,75 22,75 75,25 75,25 25)) to POLYGON((25 25,25 75,75 75,75 22,25 25))
green - blue:MULTIPOLYGON(((0 0,0 100,100 100,100 0,0 0),(25 25,75 22,75 75,25 75,25 25)))
blue - green:MULTIPOLYGON()

And writes the svg for:

enter image description here

Upvotes: 1

Related Questions