How to create box using multiple linestrings in Boost geometry?

Viewed 44

I am trying to create a box using multiple linestrings. Of course these linestrings are connected with each other and are able to make a box. Are there any functions in boost can do this? Thanks!

1 Answers

I'm not sure what you're looking for exactly. So let me just go both ways.

One Way

If you have a box you can use segment iterators to get the segments:

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <iostream>

namespace bg = boost::geometry;
namespace bgm = bg::model;

using Point      = bgm::d2::point_xy<int>;
using Box        = bgm::box<Point>;
using Poly       = bgm::polygon<Point>;

int main()
{
    Box const box { Point {0,1}, {1,2} };

    std::cout << "box: " << bg::wkt(box) << "\n";

    // now get the segments:
    Poly p;
    bg::assign(p, box);

    for (auto f = bg::segments_begin(p), l = bg::segments_end(p); f != l; ++f) {
        std::cout << bg::wkt(*f) << "\n";
    }
}

Prints

box: POLYGON((0 1,0 2,1 2,1 1,0 1))
LINESTRING(0 1,0 2)
LINESTRING(0 2,1 2)
LINESTRING(1 2,1 1)
LINESTRING(1 1,0 1)

The Other Way

The most flexible is to use the envelope algorithm to get a box:

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <iostream>

namespace bg = boost::geometry;
namespace bgm = bg::model;

using Point      = bgm::d2::point_xy<int>;
using Box        = bgm::box<Point>;
using LineString = bgm::linestring<Point>;

int main()
{
    LineString const ls{{1, 1}, {1, 2}, {0, 2}, {0, 1}};

    std::cout << "ls: " << bg::wkt(ls) << "\n";
    
    Box box;
    bg::envelope(ls, box);

    std::cout << "box: " << bg::wkt(box) << "\n";
}

Prints

ls: LINESTRING(1 1,1 2,0 2,0 1)
box: POLYGON((0 1,0 2,1 2,1 1,0 1))

(Note that the point ordering is corrected to make a valid ring)

With Validation?

To get less flexibility, in other words, more validation on the input, I would manually create a polygon from the points.

After validating, you could create a box via the WKT... That's not ideal, but probably beats writing your own logic. However, reading Box from WKT appears to only apply min/max algorithm on points without any actual validation - thus effectively the same as getting bg::envelope.

So, the best way to approach this, IMO, would be to compare the resulting box with the manual polygon to check that the input was already a box.

bool is_box(Poly const& p) {
    Box env;
    bg::envelope(p, env);
    return bg::equals(p, env);
}

For example testing with a few inputs:

 LineString{{1, 1}, {0, 1}, {0, 2}, {1, 2}, {1, 1}},
 LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}},
 LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}, {1, 1}},
 LineString{{1, 1}, {0, 2}, {0, 1}, {1, 1}}, // triangle

is_box will return false for the last one (even after correcting the polygon)

Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <iostream>

namespace bg = boost::geometry;
namespace bgm = bg::model;

using Point      = bgm::d2::point_xy<int>;
using Box        = bgm::box<Point>;
using Poly       = bgm::polygon<Point>;
using LineString = bgm::linestring<Point>;

bool is_box(Poly const& p) {
    Box env;
    bg::envelope(p, env);
    return bg::equals(p, env);
}

int main()
{
    for (auto& ls : {
             LineString{{1, 1}, {0, 1}, {0, 2}, {1, 2}, {1, 1}},
             LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}},
             LineString{{1, 1}, {1, 2}, {0, 2}, {0, 1}, {1, 1}},
             LineString{{1, 1}, {0, 2}, {0, 1}, {1, 1}}, // triangle
         })
    {
        std::cout << " --\nls: " << bg::wkt(ls) << "\n";

        Poly p;
        bg::assign_points(p, ls);

        if (std::string reason; !bg::is_valid(p, reason)) {
            std::cout << "Trying to correct...: " << reason << "\n";
            bg::correct(p);

            if (!bg::is_valid(p))
                continue;
        }
        std::cout << "poly: " << bg::wkt(p) << "\n";

        std::cout << "is a box? " << (is_box(p) ? "yes" : "no") << "\n";
    }
}

Prints

 --
ls: LINESTRING(1 1,0 1,0 2,1 2,1 1)
poly: POLYGON((1 1,0 1,0 2,1 2,1 1))
is a box? yes
 --
ls: LINESTRING(1 1,1 2,0 2,0 1)
Trying to correct...: Geometry is defined as closed but is open
poly: POLYGON((1 1,0 1,0 2,1 2,1 1))
is a box? yes
 --
ls: LINESTRING(1 1,1 2,0 2,0 1,1 1)
Trying to correct...: Geometry has wrong orientation
poly: POLYGON((1 1,0 1,0 2,1 2,1 1))
is a box? yes
 --
ls: LINESTRING(1 1,0 2,0 1,1 1)
Trying to correct...: Geometry has wrong orientation
poly: POLYGON((1 1,0 1,0 2,1 1))
is a box? no
Related