Skip to content

Commit

Permalink
Merge branch 'visibility_fix_point_out_polygon' into 'master'
Browse files Browse the repository at this point in the history
Visibility: fix case when point is outside polygon

See merge request sfcgal/SFCGAL!297
  • Loading branch information
lbartoletti committed Dec 15, 2023
2 parents aa1194b + 9e2399c commit cefb8f3
Show file tree
Hide file tree
Showing 2 changed files with 246 additions and 12 deletions.
38 changes: 26 additions & 12 deletions src/algorithm/visibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ using Segment_2 = Kernel::Segment_2;
using Traits_2 = CGAL::Arr_segment_traits_2<Kernel>;
using Arrangement_2 = CGAL::Arrangement_2<Traits_2>;
using Face_handle = Arrangement_2::Face_handle;
using Face_const_handle = Arrangement_2::Face_const_handle;
using Halfedge_const_handle = Arrangement_2::Halfedge_const_handle;
using TEV =
CGAL::Triangular_expansion_visibility_2<Arrangement_2, CGAL::Tag_true>;
Expand Down Expand Up @@ -95,24 +96,20 @@ visibility(const Geometry &polygon, const Geometry &point, NoValidityCheck)
CGAL::insert(arr, hit->edges_begin(), hit->edges_end());

// Find the face
Arrangement_2::Face_const_handle *face;
CGAL::Arr_naive_point_location<Arrangement_2> pl(arr);
CGAL::Arr_point_location_result<Arrangement_2>::Type obj =
pl.locate(queryPoint);

// The query point locates in the interior of a face
face = boost::get<Arrangement_2::Face_const_handle>(&obj);
Arrangement_2 output_arr;
Face_handle fh;
Halfedge_const_handle he = Halfedge_const_handle();
Arrangement_2 output_arr;
Face_handle fh;

// Create Triangular Expansion Visibility object.
TEV tev(arr);

// If the point is within a face, we can compute the visibility that way
if (face != nullptr) {
fh = tev.compute_visibility(queryPoint, *face, output_arr);
} else {
if (obj.which() == 0) {

Halfedge_const_handle he = Halfedge_const_handle();

// If the point is in a boundary segment, find the corresponding half edge
he = arr.halfedges_begin();
bool cont = !Segment_2(he->source()->point(), he->target()->point())
Expand All @@ -125,7 +122,7 @@ visibility(const Geometry &polygon, const Geometry &point, NoValidityCheck)
he++;
if (he == arr.halfedges_end()) {
BOOST_THROW_EXCEPTION(
Exception("Can not find corresponding half edge."));
Exception("Can not find corresponding half edge (from vertex)."));
}

cont = !Segment_2(he->source()->point(), he->target()->point())
Expand All @@ -135,9 +132,26 @@ visibility(const Geometry &polygon, const Geometry &point, NoValidityCheck)

// Use the half edge to compute the visibility
fh = tev.compute_visibility(queryPoint, he, output_arr);

} else if (obj.which() == 1) {
Halfedge_const_handle *he =
boost::get<Arrangement_2::Halfedge_const_handle>(&obj);
if (he != nullptr) {
fh = tev.compute_visibility(queryPoint, *he, output_arr);
} else {
BOOST_THROW_EXCEPTION(Exception("Can not find corresponding hedge."));
}
} else if (obj.which() == 2) {
Face_const_handle *face =
boost::get<Arrangement_2::Face_const_handle>(&obj);
if ((face != nullptr) && !((*face)->is_unbounded())) {
fh = tev.compute_visibility(queryPoint, *face, output_arr);
} else {
BOOST_THROW_EXCEPTION(Exception("Can not find corresponding face."));
}
}

return query_visibility(fh, he);
return query_visibility(fh, fh->outer_ccb());
}
///
///
Expand Down
220 changes: 220 additions & 0 deletions test/unit/SFCGAL/algorithm/Visibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,50 @@ BOOST_AUTO_TEST_CASE(testVisibility_PointInPolygon)
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointOnPolygon)
{
std::vector<Point> points;
points.push_back(Point(0.0, 4.0));
points.push_back(Point(0.0, 0.0));
points.push_back(Point(3.0, 2.0));
points.push_back(Point(4.0, 0.0));
points.push_back(Point(4.0, 4.0));
points.push_back(Point(1.0, 2.0));
points.push_back(Point(0.0, 4.0));

LineString lineString(points);
Polygon poly(lineString);

Point queryPoint(0.0, 2.0);

std::unique_ptr<Polygon> result(algorithm::visibility(poly, queryPoint));
std::string expectedWkt =
"POLYGON((1.0 2.0,0.0 4.0,0.0 0.0,3.0 2.0,1.0 2.0))";
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointVertexOnPolygon)
{
std::vector<Point> points;
points.push_back(Point(0.0, 4.0));
points.push_back(Point(0.0, 0.0));
points.push_back(Point(3.0, 2.0));
points.push_back(Point(4.0, 0.0));
points.push_back(Point(4.0, 4.0));
points.push_back(Point(1.0, 2.0));
points.push_back(Point(0.0, 4.0));

LineString lineString(points);
Polygon poly(lineString);

Point queryPoint(3.0, 2.0);

std::unique_ptr<Polygon> result(algorithm::visibility(poly, queryPoint));
std::string expectedWkt =
"POLYGON((0.0 0.0,3.0 2.0,4.0 0.0,4.0 4.0,1.0 2.0,0.0 2.0,0.0 0.0))";
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointInPolygonHole)
{
std::vector<Point> points;
Expand Down Expand Up @@ -89,6 +133,129 @@ BOOST_AUTO_TEST_CASE(testVisibility_PointInPolygonHole)
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointOnPolygonHole)
{
std::vector<Point> points;
points.push_back(Point(0.0, 4.0));
points.push_back(Point(0.0, 0.0));
points.push_back(Point(3.0, 2.0));
points.push_back(Point(4.0, 0.0));
points.push_back(Point(4.0, 4.0));
points.push_back(Point(1.0, 2.0));
points.push_back(Point(0.0, 4.0));

std::vector<Point> points_hole;
points_hole.push_back(Point(0.2, 1.75));
points_hole.push_back(Point(0.9, 1.8));
points_hole.push_back(Point(0.7, 1.2));
points_hole.push_back(Point(0.2, 1.75));

LineString lineString(points);
LineString hole(points_hole);

Polygon poly(lineString);
poly.addInteriorRing(hole);

Point queryPoint(0.0, 2.0);

std::unique_ptr<Polygon> result(algorithm::visibility(poly, queryPoint));
std::string expectedWkt = "POLYGON((1.0 2.0,0.0 4.0,0.0 0.0,1.0 0.7,0.2 "
"1.8,0.9 1.8,2.2 1.5,3.0 2.0,1.0 2.0))";
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointVertexOnPolygonHole)
{
std::vector<Point> points;
points.push_back(Point(0.0, 4.0));
points.push_back(Point(0.0, 0.0));
points.push_back(Point(3.0, 2.0));
points.push_back(Point(4.0, 0.0));
points.push_back(Point(4.0, 4.0));
points.push_back(Point(1.0, 2.0));
points.push_back(Point(0.0, 4.0));

std::vector<Point> points_hole;
points_hole.push_back(Point(0.2, 1.75));
points_hole.push_back(Point(0.9, 1.8));
points_hole.push_back(Point(0.7, 1.2));
points_hole.push_back(Point(0.2, 1.75));

LineString lineString(points);
LineString hole(points_hole);

Polygon poly(lineString);
poly.addInteriorRing(hole);

Point queryPoint(3.0, 2.0);

std::unique_ptr<Polygon> result(algorithm::visibility(poly, queryPoint));
std::string expectedWkt =
"POLYGON((0.0 0.0,3.0 2.0,4.0 0.0,4.0 4.0,1.0 2.0,0.0 2.0,0.0 1.7,0.2 "
"1.8,0.9 1.8,0.7 1.2,0.0 1.0,0.0 0.0))";
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointOnHolePolygonHole)
{
std::vector<Point> points;
points.push_back(Point(0.0, 4.0));
points.push_back(Point(0.0, 0.0));
points.push_back(Point(3.0, 2.0));
points.push_back(Point(4.0, 0.0));
points.push_back(Point(4.0, 4.0));
points.push_back(Point(1.0, 2.0));
points.push_back(Point(0.0, 4.0));

std::vector<Point> points_hole;
points_hole.push_back(Point(0.2, 1.75));
points_hole.push_back(Point(0.9, 1.8));
points_hole.push_back(Point(0.7, 1.2));
points_hole.push_back(Point(0.2, 1.75));

LineString lineString(points);
LineString hole(points_hole);

Polygon poly(lineString);
poly.addInteriorRing(hole);

Point queryPoint(0.550, 1.775);

std::unique_ptr<Polygon> result(algorithm::visibility(poly, queryPoint));
std::string expectedWkt = "POLYGON((0.7 1.2,0.9 1.8,0.2 1.8,0.7 1.2))";
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointVertexOnHolePolygonHole)
{
std::vector<Point> points;
points.push_back(Point(0.0, 4.0));
points.push_back(Point(0.0, 0.0));
points.push_back(Point(3.0, 2.0));
points.push_back(Point(4.0, 0.0));
points.push_back(Point(4.0, 4.0));
points.push_back(Point(1.0, 2.0));
points.push_back(Point(0.0, 4.0));

std::vector<Point> points_hole;
points_hole.push_back(Point(0.2, 1.75));
points_hole.push_back(Point(0.9, 1.8));
points_hole.push_back(Point(0.7, 1.2));
points_hole.push_back(Point(0.2, 1.75));

LineString lineString(points);
LineString hole(points_hole);

Polygon poly(lineString);
poly.addInteriorRing(hole);

Point queryPoint(0.9, 1.8);

std::unique_ptr<Polygon> result(algorithm::visibility(poly, queryPoint));
std::string expectedWkt = "POLYGON((0.7 1.2,0.9 1.8,0.2 1.8,0.7 1.2))";
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_SegmentInPolygon)
{
std::vector<Point> points;
Expand Down Expand Up @@ -154,4 +321,57 @@ BOOST_AUTO_TEST_CASE(testVisibility_SegmentInPolygonHole)
"3.0,19.0 -2.0))";
BOOST_CHECK_EQUAL(result->asText(1), expectedWkt);
}

BOOST_AUTO_TEST_CASE(testVisibility_PointOutPolygon)
{
std::vector<Point> points;

points.emplace_back(24.2222222, 40);
points.emplace_back(24.183792760806462, 39.609819355967744);
points.emplace_back(24.069981265022573, 39.23463313526982);
points.emplace_back(23.88516142460509, 38.8888595339608);
points.emplace_back(23.636435762373097, 38.58578643762691);
points.emplace_back(23.333362666039207, 38.33706077539491);
points.emplace_back(22.98758906473018, 38.15224093497743);
points.emplace_back(22.612402844032257, 38.038429439193536);
points.emplace_back(22.2222222, 38);
points.emplace_back(21.832041555967745, 38.038429439193536);
points.emplace_back(21.45685533526982, 38.15224093497743);
points.emplace_back(21.1110817339608, 38.33706077539491);
points.emplace_back(20.808008637626905, 38.58578643762691);
points.emplace_back(20.55928297539491, 38.8888595339608);
points.emplace_back(20.37446313497743, 39.23463313526982);
points.emplace_back(20.26065163919354, 39.609819355967744);
points.emplace_back(20.2222222, 40);
points.emplace_back(20.26065163919354, 40.390180644032256);
points.emplace_back(20.37446313497743, 40.76536686473018);
points.emplace_back(20.55928297539491, 41.1111404660392);
points.emplace_back(20.808008637626905, 41.41421356237309);
points.emplace_back(21.111081733960795, 41.66293922460509);
points.emplace_back(21.45685533526982, 41.84775906502257);
points.emplace_back(21.832041555967745, 41.96157056080646);
points.emplace_back(22.2222222, 42);
points.emplace_back(22.612402844032257, 41.961570560806464);
points.emplace_back(22.98758906473018, 41.84775906502257);
points.emplace_back(23.333362666039204, 41.66293922460509);
points.emplace_back(23.636435762373097, 41.41421356237309);
points.emplace_back(23.88516142460509, 41.1111404660392);
points.emplace_back(24.069981265022573, 40.76536686473018);
points.emplace_back(24.183792760806462, 40.390180644032256);
points.emplace_back(24.2222222, 40);

LineString lineString(points);
lineString.reverse();
Polygon const poly(lineString);

Point const queryPoint(-10, 60);

try {
std::unique_ptr<Polygon> const result(
algorithm::visibility(poly, queryPoint));
} catch (std::exception &e) {
BOOST_CHECK_EQUAL(e.what(), "Can not find corresponding face.");
}
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit cefb8f3

Please sign in to comment.