Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find Boost rtree elements inside a polygon

I would like to find all the elements indexed in a rtree that intersects with the exterior ring of a polygon with holes but that are not completely inside any of the holes using Boost C++ libraries.

I know how to get the element intersecting the exterior ring with:

// Constructing the exterior ring polygon
Boost2dRing p;
for (int i = 0; i < numPunts; i++)
{
    x = Punts.at(i).x;
    y = Punts.at(i).y;
    p.push_back(Boost2dPoint(x, y));
}

// Getting the intersecting elements with that polygon
m_RTree.query(bgi::intersects(p), std::back_inserter(res));
...
// Constructing the polygon for the inner ring (hole)
Boost2dRing p;
for (int i = 0; i < numPuntsHole; i++)
{
    x = PuntsHole.at(i).x;
    y = PuntsHole.at(i).y;
    pHole.push_back(Boost2dPoint(x, y));
}

// Now I try to get the elements inside completely this polygon but I get a compilation error
m_RTree.query(bgi::within(pHole), std::back_inserter(res));

Error message:

error C2664: 'int boost::mpl::assertion_failed(boost::mpl::assert::type)': cannot convert argument 1 from 'boost::mpl::failed ************(__cdecl boost::geometry::strategy::within::services::default_strategy::NOT_IMPLEMENTED_FOR_THESE_TYPES::* ***********)(boost::mpl::assert_::types)' to 'boost::mpl::assert::type' 1> with 1> [ 1> Geometry1=Boost2dBox, 1> Geometry2=Boost2dRing, 1>
GeometryContained=Boost2dBox, 1>
GeometryContaining=Boost2dRing 1> ] note: No constructor could take the source type, or constructor overload resolution was ambiguous

Any hint to achieve this goal?

like image 368
tonipozo Avatar asked Jan 18 '26 14:01

tonipozo


1 Answers

The within predicate isn't implemented for your choice of geometry operands.

However, you can do what you want with much less work. Let's say you have your rings, for example:

Boost2dRing exterior, interior;
bg::read_wkt("POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1))", exterior);
bg::read_wkt("POLYGON((0.2 0.2,0.2 0.4,0.4 0.4,0.4 0.2,0.2 0.2))", interior);

Now, Boost Geometry has the concept of a Polygon which is an outer ring and (multiple) inner rings:

A polygon is A polygon is a planar surface defined by one exterior boundary and zero or more interior boundaries (OGC Simple Feature Specification)

So, let's use that instead:

bg::reverse(interior);
Boost2dPolygon polygon;
polygon.outer() = exterior;
polygon.inners().push_back(interior);

Note that the orientation of the inner ring is inverted.

Or, indeed, directly using the constructor:

Boost2dPolygon polygon({exterior, interior});

Or, even reading it from WKT at once:

bg::read_wkt("POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1) (0.2 0.2,0.4 0.2,0.4 0.4,0.2 0.4,0.2 0.2))", polygon);

Now you can just query in one pass:

std::vector<RTree::value_type> res;
m_RTree.query(bgi::intersects(polygon), std::back_inserter(res));

Full Demo!

Given the following sample() tree contents:

using RTree = bgi::rtree<std::pair<Boost2dBox, std::string>, bgi::rstar<16> >;
RTree sample() {
    RTree tree;

    std::pair<std::string, std::string> items[] = {
        { "BOX(0.2 0.2,0.2 0.4,0.4 0.4,0.4 0.2,0.2 0.2)",           "ok" },
        { "BOX(0.28 0.28,0.28 0.32,0.32 0.32,0.32 0.28,0.28 0.28)", "within gap" },
        { "BOX(0.28 0.28,0.28 0.32,0.36 0.32,0.36 0.28,0.28 0.28)", "small overlap" },
        { "BOX(2 2,2 4,4 4,4 2,2 2)",                               "outside exterior" },
    };
    for (auto& item : items) {
        Boost2dBox box;
        bg::read_wkt(item.first, box);
        checks("box", box);
        tree.insert({box, item.second});
    }

    return tree;
}

We can test things manually:

RTree const m_RTree = sample();

std::cout << "Sample tree:\n";
for (auto& value : m_RTree) {
    std::cout << " - " << std::quoted(value.second) << ": " << bg::wkt(value.first) << "\n";

    Boost2dMultiPolygon mp;
    if (bg::intersection(polygon, value.first, mp))
        std::cout << "      (intersection is " << bg::wkt(mp) << ")\n";
}

Which prints

Sample tree:
 - "ok": POLYGON((0.2 0.2,0.2 0.4,0.4 0.4,0.4 0.2,0.2 0.2))
      (intersection is MULTIPOLYGON(((0.2 0.2,0.2 0.4,0.4 0.4,0.4 0.2,0.2 0.2),(0.25 0.25,0.35 0.25,0.35 0.35,0.25 0.35,0.25 0.25))))
 - "within gap": POLYGON((0.28 0.28,0.28 0.32,0.32 0.32,0.32 0.28,0.28 0.28))
      (intersection is MULTIPOLYGON())
 - "small overlap": POLYGON((0.28 0.28,0.28 0.32,0.36 0.32,0.36 0.28,0.28 0.28))
      (intersection is MULTIPOLYGON(((0.35 0.32,0.36 0.32,0.36 0.28,0.35 0.28,0.35 0.32))))
 - "outside exterior": POLYGON((2 2,2 4,4 4,4 2,2 2))
      (intersection is MULTIPOLYGON())

And verify the results comparing to the tree query:

m_RTree.query(bgi::intersects(polygon), std::back_inserter(matches));

std::cout << "Intersecting with: ";
for (auto& match : matches) std::cout << " " << std::quoted(match.second) << " ";

Which prints:

Intersecting with:  "ok"  "small overlap" 

See it all Live On Coliru

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/geometries/linestring.hpp>
#include <boost/geometry/geometries/multi_polygon.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/algorithms/intersects.hpp>
#include <boost/geometry/algorithms/envelope.hpp>
#include <boost/geometry/algorithms/intersection.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/geometry/index/predicates.hpp>
#include <boost/geometry/index/adaptors/query.hpp>
#include <boost/geometry/io/io.hpp>
#include <iostream>
#include <fstream>

namespace bg = boost::geometry;
namespace bgi = boost::geometry::index;

using Boost2dPoint        = bg::model::d2::point_xy<double>;
using Boost2dRing         = bg::model::ring<Boost2dPoint>;
using Boost2dPolygon      = bg::model::polygon<Boost2dPoint>;
using Boost2dMultiPolygon = bg::model::multi_polygon<Boost2dPolygon>;
using Boost2dBox          = bg::model::box<Boost2dPoint>;

template <typename G> void checks(std::string name, G& geom) {
    std::cout << name << ": " << bg::wkt(geom) << "\n";

    std::string reason;
    if (!bg::is_valid(geom, reason)) {
        std::cout << name << ": " << reason << "\n";

        bg::correct(geom);

        std::cout << bg::wkt(geom) << "\n";
        if (!bg::is_valid(geom, reason)) {
            std::cout << name << " corrected: " << reason << "\n";
        }
    }
}

using RTree = bgi::rtree<std::pair<Boost2dBox, std::string>, bgi::rstar<16> >;
RTree sample() {
    RTree tree;

    std::pair<std::string, std::string> items[] = {
        { "BOX(0.2 0.2,0.2 0.4,0.4 0.4,0.4 0.2,0.2 0.2)",           "ok" },
        { "BOX(0.28 0.28,0.28 0.32,0.32 0.32,0.32 0.28,0.28 0.28)", "within gap" },
        { "BOX(0.28 0.28,0.28 0.32,0.36 0.32,0.36 0.28,0.28 0.28)", "small overlap" },
        { "BOX(2 2,2 4,4 4,4 2,2 2)",                               "outside exterior" },
    };
    for (auto& item : items) {
        Boost2dBox box;
        bg::read_wkt(item.first, box);
        checks("box", box);
        tree.insert({box, item.second});
    }

    return tree;
}

int main() {
    Boost2dPolygon polygon;
    bg::read_wkt("POLYGON((0.1 0.1,0.1 0.5,0.5 0.5,0.5 0.1,0.1 0.1) (0.25 0.25,0.35 0.25,0.35 0.35,0.25 0.35,0.25 0.25))", polygon);
    checks("polygon", polygon);

    RTree const m_RTree = sample();

    std::cout << "Sample tree:\n";
    for (auto& value : m_RTree) {
        std::cout << " - " << std::quoted(value.second) << ": " << bg::wkt(value.first) << "\n";

        Boost2dMultiPolygon mp;
        if (bg::intersection(polygon, value.first, mp))
            std::cout << "      (intersection is " << bg::wkt(mp) << ")\n";
    }
    std::cout << "\n";

    std::vector<RTree::value_type> matches;
    m_RTree.query(bgi::intersects(polygon), std::back_inserter(matches));

    std::cout << "Intersecting with: ";
    for (auto& match : matches) std::cout << " " << std::quoted(match.second) << " ";
    std::cout << "\n";
}
like image 172
sehe Avatar answered Jan 21 '26 05:01

sehe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!