s2geometry
s2geometry copied to clipboard
Usage: How to test for polygon being covered by another polygon with shared boundary?
Given two polygons, I would like to replicate what ST_Covers does using S2. While the case of one polygon being contained in the interior of another works (or at least yields the result I would expect), I have trouble with the case where two polygons share part of their boundary (compare script below):
#include <vector>
#include <iostream>
#include <s2/s2boolean_operation.h>
#include <s2/mutable_s2shape_index.h>
#include <s2/s2point.h>
#include <s2/s2polygon.h>
#include <s2/s2debug.h>
#include <s2/s2loop.h>
int main(int argc, char** argv) {
// "parent" polygon, the one that should cover all the other polygons
std::vector<S2Point> vertices_parent = {
S2LatLng::FromDegrees(60, -118).ToPoint(),
S2LatLng::FromDegrees(23, -118).ToPoint(),
S2LatLng::FromDegrees(23, 34).ToPoint(),
S2LatLng::FromDegrees(60, 34).ToPoint(),
};
std::unique_ptr<S2Loop> parent_loop = std::make_unique<S2Loop>(vertices_parent, S2Debug::DISABLE);
const auto parent_poly = std::make_unique<S2Polygon>(std::move(parent_loop), S2Debug::DISABLE);
MutableS2ShapeIndex parent_shape_index{MutableS2ShapeIndex::Options()};
parent_shape_index.Add(std::make_unique<S2Polygon::Shape>(parent_poly.get()));
// "child" polygon that shares a common boundary
std::vector<S2Point> vertices_child = {
S2LatLng::FromDegrees(45, -118).ToPoint(),
S2LatLng::FromDegrees(23, -118).ToPoint(),
S2LatLng::FromDegrees(23, 34).ToPoint(),
S2LatLng::FromDegrees(45, 34).ToPoint(),
};
std::unique_ptr<S2Loop> child_loop = std::make_unique<S2Loop>(vertices_child, S2Debug::DISABLE);
const auto child_poly = std::make_unique<S2Polygon>(std::move(child_loop), S2Debug::DISABLE);
MutableS2ShapeIndex child_shape_index{MutableS2ShapeIndex::Options()};
child_shape_index.Add(std::make_unique<S2Polygon::Shape>(child_poly.get()));
// "child" polygon that is full contained in the interior
std::vector<S2Point> vertices_child_int = {
S2LatLng::FromDegrees(40, -117).ToPoint(),
S2LatLng::FromDegrees(37, -117).ToPoint(),
S2LatLng::FromDegrees(37, -114).ToPoint(),
S2LatLng::FromDegrees(40, -114).ToPoint(),
};
std::unique_ptr<S2Loop> child_int_loop = std::make_unique<S2Loop>(vertices_child_int, S2Debug::DISABLE);
const auto child_int_poly = std::make_unique<S2Polygon>(std::move(child_int_loop), S2Debug::DISABLE);
MutableS2ShapeIndex child_int_shape_index{MutableS2ShapeIndex::Options()};
child_int_shape_index.Add(std::make_unique<S2Polygon::Shape>(child_int_poly.get()));
// test for containment
S2BooleanOperation::Options closedOptions{};
closedOptions.set_polygon_model(S2BooleanOperation::PolygonModel::CLOSED);
closedOptions.set_polyline_model(S2BooleanOperation::PolylineModel::CLOSED);
// containment in interior
const auto int_contains_polygon_str = parent_poly->Contains(*child_int_poly) ? "true" : "false";
const auto int_contains_boolop_str = S2BooleanOperation::Contains(parent_shape_index, child_int_shape_index, closedOptions) ? "true" : "false";
std::cout << "Interior => contains (boolop): " << int_contains_boolop_str << ", contains (poly): " << int_contains_polygon_str << std::endl;
// containment with shared boundary
const auto contains_polygon_str = parent_poly->Contains(*child_poly) ? "true" : "false";
const auto contains_boolop_str = S2BooleanOperation::Contains(parent_shape_index, child_shape_index, closedOptions) ? "true" : "false";
std::cout << "Shared => contains (boolop): " << contains_boolop_str << ", contains (poly): " << contains_polygon_str << std::endl;
return 0;
}
When I run this, it returns
Interior => contains (boolop): true, contains (poly): true
Shared => contains (boolop): false, contains (poly): false
which is not what I expect in the case of the shared boundary. Is this just a misunderstanding on my end and I am going about this the wrong way? Or do I have to use some combination of predicates to replicate what ST_Covers does? I would greatly appreciate any pointers!