From adf0d002ad43a9d0f64002ed72a553585fd1fd2b Mon Sep 17 00:00:00 2001 From: Marcel Jacobse Date: Sun, 14 Jun 2026 11:23:07 +0200 Subject: [PATCH 1/3] Fix degenerate linestring relation on boundary of areal --- .../algorithms/detail/relate/linear_areal.hpp | 18 +++++++++++++++--- test/algorithms/covered_by/covered_by.cpp | 18 ++++++++++++++++++ test/algorithms/relate/relate_linear_areal.cpp | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index 37a7a76519..695a0da59b 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -86,6 +86,11 @@ class no_turns_la_linestring_pred { m_interrupt_flags |= 8; } + + if ( ! may_update(m_result) ) + { + m_interrupt_flags |= 0x10; + } } template @@ -102,7 +107,7 @@ class no_turns_la_linestring_pred } // if those flags are set nothing will change - if ( m_interrupt_flags == 0xF ) + if ( m_interrupt_flags == 0x1F ) { return false; } @@ -110,13 +115,20 @@ class no_turns_la_linestring_pred int const pig = detail::within::point_in_geometry(range::front(linestring), m_geometry2, m_strategy); - //BOOST_GEOMETRY_ASSERT_MSG(pig != 0, "There should be no IPs"); if ( pig > 0 ) { update(m_result); m_interrupt_flags |= 1; } + else if ( pig == 0 ) + { + // no turns but still point on boundary can actually happen when the + // linestring is degenerate. so handle this case explicitly here. + // for consistency let's still report dimension 1 instead of 0 + update(m_result); + m_interrupt_flags |= 0x10; + } else { update(m_result); @@ -140,7 +152,7 @@ class no_turns_la_linestring_pred } } - return m_interrupt_flags != 0xF + return m_interrupt_flags != 0x1F && ! m_result.interrupt; } diff --git a/test/algorithms/covered_by/covered_by.cpp b/test/algorithms/covered_by/covered_by.cpp index 84f69b0ce7..310e7d7f0f 100644 --- a/test/algorithms/covered_by/covered_by.cpp +++ b/test/algorithms/covered_by/covered_by.cpp @@ -141,6 +141,24 @@ void test_all() test_geometry, poly

>("BOX(1 1,2 2)", "POLYGON((0 0,0 3,3 1,1 0,0 0))", false); test_geometry, mpoly

>("BOX(1 1,2 2)", "MULTIPOLYGON(((0 0,0 3,3 3,3 0,0 0)),((-1 -1,-3 -4,-7 -7,-4 -3,-1 -1)))", true); test_geometry, mpoly

>("BOX(1 1,2 2)", "MULTIPOLYGON(((0 0,0 3,3 1,1 0,0 0)),((-1 -1,-3 -4,-7 -7,-4 -3,-1 -1)))", false); + + // degenerate line segment - polygon + { + // on corner + test_geometry, poly

>("LINESTRING(0 0,0 0)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); + // on edge + test_geometry, poly

>("LINESTRING(1 2,1 2)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); + // inside + test_geometry, poly

>("LINESTRING(1 1,1 1)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); + // outside + test_geometry, poly

>("LINESTRING(1 3,1 3)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", false); + // inside hole + test_geometry, poly

>("LINESTRING(2 2,2 2)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", false); + // on corner of hole + test_geometry, poly

>("LINESTRING(3 3,3 3)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", true); + // on edge of hole + test_geometry, poly

>("LINESTRING(2 1,2 1)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", true); + } } diff --git a/test/algorithms/relate/relate_linear_areal.cpp b/test/algorithms/relate/relate_linear_areal.cpp index 337cc32b4f..ce39019183 100644 --- a/test/algorithms/relate/relate_linear_areal.cpp +++ b/test/algorithms/relate/relate_linear_areal.cpp @@ -277,6 +277,24 @@ void test_linestring_polygon() test_geometry("LINESTRING(2 9, 1 1, 10 1, 10 10, 1 10, 0 6, 5 6)", "POLYGON((0 0,0 10,10 10,10 0,0 0),(4 4,4 6,6 6,6 4,4 4))", "11F00F212"); + + // degenerate line segment + { + // on corner + test_geometry("LINESTRING(0 0,0 0)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "F1FFFF212"); + // on edge + test_geometry("LINESTRING(1 2,1 2)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "F1FFFF212"); + // inside + test_geometry("LINESTRING(1 1,1 1)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "1FFFFF212"); + // outside + test_geometry("LINESTRING(1 3,1 3)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "FF1FFF212"); + // inside hole + test_geometry("LINESTRING(2 2,2 2)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "FF1FFF212"); + // on corner of hole + test_geometry("LINESTRING(3 3,3 3)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "F1FFFF212"); + // on edge of hole + test_geometry("LINESTRING(2 1,2 1)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "F1FFFF212"); + } } template From 80cd1f60e6d16d2b595f927bf36ed4cf952f7368 Mon Sep 17 00:00:00 2001 From: Marcel Jacobse Date: Wed, 17 Jun 2026 19:48:31 +0200 Subject: [PATCH 2/3] Use hexadecimal for all interrupt flags --- .../algorithms/detail/relate/linear_areal.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index 695a0da59b..e26e46c8cb 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -65,26 +65,26 @@ class no_turns_la_linestring_pred , m_result(res) , m_strategy(strategy) , m_boundary_checker(boundary_checker) - , m_interrupt_flags(0) + , m_interrupt_flags(0x00) { if ( ! may_update(m_result) ) { - m_interrupt_flags |= 1; + m_interrupt_flags |= 0x01; } if ( ! may_update(m_result) ) { - m_interrupt_flags |= 2; + m_interrupt_flags |= 0x02; } if ( ! may_update(m_result) ) { - m_interrupt_flags |= 4; + m_interrupt_flags |= 0x04; } if ( ! may_update(m_result) ) { - m_interrupt_flags |= 8; + m_interrupt_flags |= 0x08; } if ( ! may_update(m_result) ) @@ -119,7 +119,7 @@ class no_turns_la_linestring_pred if ( pig > 0 ) { update(m_result); - m_interrupt_flags |= 1; + m_interrupt_flags |= 0x01; } else if ( pig == 0 ) { @@ -132,7 +132,7 @@ class no_turns_la_linestring_pred else { update(m_result); - m_interrupt_flags |= 2; + m_interrupt_flags |= 0x02; } // check if there is a boundary @@ -143,12 +143,12 @@ class no_turns_la_linestring_pred if ( pig > 0 ) { update(m_result); - m_interrupt_flags |= 4; + m_interrupt_flags |= 0x04; } else { update(m_result); - m_interrupt_flags |= 8; + m_interrupt_flags |= 0x08; } } From 4ff4b9439ec0a6394102b274eaa23bb44b1900a5 Mon Sep 17 00:00:00 2001 From: Marcel Jacobse Date: Wed, 17 Jun 2026 21:54:56 +0200 Subject: [PATCH 3/3] Also support single-point linestrings --- .../geometry/algorithms/detail/relate/linear_areal.hpp | 2 +- test/algorithms/covered_by/covered_by.cpp | 7 +++++++ test/algorithms/relate/relate_linear_areal.cpp | 7 +++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index e26e46c8cb..40845f34c1 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -99,7 +99,7 @@ class no_turns_la_linestring_pred std::size_t const count = boost::size(linestring); // invalid input - if ( count < 2 ) + if ( count < 1 ) { // ignore // TODO: throw an exception? diff --git a/test/algorithms/covered_by/covered_by.cpp b/test/algorithms/covered_by/covered_by.cpp index 310e7d7f0f..f7716b4e4e 100644 --- a/test/algorithms/covered_by/covered_by.cpp +++ b/test/algorithms/covered_by/covered_by.cpp @@ -146,18 +146,25 @@ void test_all() { // on corner test_geometry, poly

>("LINESTRING(0 0,0 0)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); + test_geometry, poly

>("LINESTRING(0 0)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); // on edge test_geometry, poly

>("LINESTRING(1 2,1 2)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); + test_geometry, poly

>("LINESTRING(1 2)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); // inside test_geometry, poly

>("LINESTRING(1 1,1 1)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); + test_geometry, poly

>("LINESTRING(1 1)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", true); // outside test_geometry, poly

>("LINESTRING(1 3,1 3)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", false); + test_geometry, poly

>("LINESTRING(1 3)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", false); // inside hole test_geometry, poly

>("LINESTRING(2 2,2 2)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", false); + test_geometry, poly

>("LINESTRING(2 2)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", false); // on corner of hole test_geometry, poly

>("LINESTRING(3 3,3 3)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", true); + test_geometry, poly

>("LINESTRING(3 3)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", true); // on edge of hole test_geometry, poly

>("LINESTRING(2 1,2 1)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", true); + test_geometry, poly

>("LINESTRING(2 1)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", true); } } diff --git a/test/algorithms/relate/relate_linear_areal.cpp b/test/algorithms/relate/relate_linear_areal.cpp index ce39019183..613d17af90 100644 --- a/test/algorithms/relate/relate_linear_areal.cpp +++ b/test/algorithms/relate/relate_linear_areal.cpp @@ -282,18 +282,25 @@ void test_linestring_polygon() { // on corner test_geometry("LINESTRING(0 0,0 0)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "F1FFFF212"); + test_geometry("LINESTRING(0 0)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "F1FFFF212"); // on edge test_geometry("LINESTRING(1 2,1 2)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "F1FFFF212"); + test_geometry("LINESTRING(1 2)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "F1FFFF212"); // inside test_geometry("LINESTRING(1 1,1 1)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "1FFFFF212"); + test_geometry("LINESTRING(1 1)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "1FFFFF212"); // outside test_geometry("LINESTRING(1 3,1 3)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "FF1FFF212"); + test_geometry("LINESTRING(1 3)", "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))", "FF1FFF212"); // inside hole test_geometry("LINESTRING(2 2,2 2)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "FF1FFF212"); + test_geometry("LINESTRING(2 2)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "FF1FFF212"); // on corner of hole test_geometry("LINESTRING(3 3,3 3)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "F1FFFF212"); + test_geometry("LINESTRING(3 3)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "F1FFFF212"); // on edge of hole test_geometry("LINESTRING(2 1,2 1)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "F1FFFF212"); + test_geometry("LINESTRING(2 1)", "POLYGON((0 0, 0 4, 4 4, 4 0, 0 0),(1 1, 1 3, 3 3, 3 1, 1 1))", "F1FFFF212"); } }