Skip to content

Add index for holes-in-shell validity check - #1251

Open
msbarry wants to merge 1 commit into
locationtech:masterfrom
msbarry:isvalid-shell-index
Open

msbarry wants to merge 1 commit into
locationtech:masterfrom
msbarry:isvalid-shell-index

Conversation

@msbarry

@msbarry msbarry commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

While profiling planetiler I noticed IsValidOp.checkHolesInShell can get slow for very complex polygons with lots of holes since it's testing whether each hole is inside the shell without an index. For example Lake Nasser which has 1.3M points and thousands of island holes takes 14.7 seconds to run an isValid() check.

This PR uses IndexedPointInAreaLocator to test if holes are inside the shell. I added a perf test which showed that the overhead of building the index was larger than the perf gain for less than 20 holes so I added a threshold to only build the index when there are >20 holes. This brings Lake Nasser validity check from 14.7 seconds down to 1.6 seconds.

I used opus 5.5 to help identify the bottleneck and prototype the improvement.

Signed-off-by: Mike Barry <msb5014@gmail.com>

private TopologyValidationError validateIndexedShell(String wkt) {
IsValidOp validOp = new IsValidOp(read(wkt));
validOp.setMinHolesIndexedShell(0);

@msbarry msbarry Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this to keep the tests verifying index behavior smaller, if you want I could set the min holes to 0 on all tests in this file so they all exercise the new code path, or make the indexed ones create 20 extra holes to force it without the setMinHoles method?

* Tests performance of IsValidOp.checkHolesInShell
* for polygons with a large shell and many holes.
*/
public class IsValidHolesInShellPerfTest extends PerformanceTestCase {

@msbarry msbarry Oct 29, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On my m1 macbook:

BeforeAfter
Running with # holes 1
runValidateSmallShell : 16 ms
runValidateLargeShell : 21 ms

Running with # holes 4
runValidateSmallShell : 3 ms  ( 0.2x - size 4.0x)
runValidateLargeShell : 9 ms  ( 0.4x - size 4.0x)

Running with # holes 16
runValidateSmallShell : 3 ms  ( 1.0x - size 4.0x)
runValidateLargeShell : 26 ms  ( 2.9x - size 4.0x)

Running with # holes 64
runValidateSmallShell : 6 ms  ( 2.0x - size 4.0x)
runValidateLargeShell : 93 ms  ( 3.6x - size 4.0x)

Running with # holes 256
runValidateSmallShell : 16 ms  ( 2.7x - size 4.0x)
runValidateLargeShell : 356 ms  ( 3.8x - size 4.0x)

Running with # holes 1024
runValidateSmallShell : 50 ms  ( 3.1x - size 4.0x)
runValidateLargeShell : 1389 ms  ( 3.9x - size 4.0x)

Running with # holes 4096
runValidateSmallShell : 194 ms  ( 3.9x - size 4.0x)
runValidateLargeShell : 5587 ms  ( 4.0x - size 4.0x)
Running with # holes 1
runValidateSmallShell : 10 ms
runValidateLargeShell : 18 ms

Running with # holes 4
runValidateSmallShell : 3 ms  ( 0.3x - size 4.0x)
runValidateLargeShell : 10 ms  ( 0.6x - size 4.0x)

Running with # holes 16
runValidateSmallShell : 3 ms  ( 1.0x - size 4.0x)
runValidateLargeShell : 26 ms  ( 2.6x - size 4.0x)

Running with # holes 64
runValidateSmallShell : 11 ms  ( 3.7x - size 4.0x)
runValidateLargeShell : 91 ms  ( 3.5x - size 4.0x)

Running with # holes 256
runValidateSmallShell : 15 ms  ( 1.4x - size 4.0x)
runValidateLargeShell : 41 ms  ( 0.5x - size 4.0x)

Running with # holes 1024
runValidateSmallShell : 41 ms  ( 2.7x - size 4.0x)
runValidateLargeShell : 58 ms  ( 1.4x - size 4.0x)

Running with # holes 4096
runValidateSmallShell : 127 ms  ( 3.1x - size 4.0x)
runValidateLargeShell : 155 ms  ( 2.7x - size 4.0x)

public void testPolygonHoleOutsideShellInConcavityTouch() {
checkInvalidIndexedShell(TopologyValidationError.HOLE_OUTSIDE_SHELL,
"POLYGON ((10 10, 10 90, 40 90, 40 30, 60 30, 60 90, 90 90, 90 10, 10 10), (15 70, 25 70, 25 80, 15 80, 15 70), (70 50, 80 50, 80 60, 70 60, 70 50), (40 50, 50 45, 50 55, 40 50))");
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These test add some extra coverage around the indexed version:

Image

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant