Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buffer(0) drops interiors of valid shape #1069

Open
wikusbrinkNearmap opened this issue Jan 20, 2021 · 9 comments
Open

Buffer(0) drops interiors of valid shape #1069

wikusbrinkNearmap opened this issue Jan 20, 2021 · 9 comments

Comments

@wikusbrinkNearmap
Copy link

Expected behavior and actual behavior.

The shape in this file shape.txt is valid according to is_valid, so we expect buffer(0) to have no effect on the shape, instead we're seeing some of the interiors are dropped:

Screen Shot 2021-01-21 at 10 46 20 am

Steps to reproduce the problem.

import shapely.wkt

with open('shape.txt', 'r') as f:
    geom = shapely.wkt.loads(f.read())
    
assert geom.is_valid
assert geom.equals(geom.buffer(0))  

Operating system

Ubuntu 18.04.5 LTS

Shapely version and provenance

Installed with conda:
shapely, 1.7.1, py38hc7361b7_1, conda-forge

@tomplex
Copy link
Contributor

tomplex commented Jan 22, 2021

I can't say that I know what's going on here specifically, but it looks like the dropped interior rings are all clustered in one area, down in the bottom right of the problem polygon. They look like this, specifically:
Screen Shot 2021-01-22 at 12 44 11 AM

Here's the WKT of the dropped interiors as a multipolygon:

MULTIPOLYGON (((2377 490, 2366 507, 2350 520, 2345 537, 2336 546, 2321 552, 2310 563, 2305 576, 2297 583, 2290 583, 2285 577, 2285 568, 2297 545, 2299 513, 2306 504, 2306 494, 2300 486, 2287 481, 2275 470, 2273 449, 2269 442, 2271 430, 2280 422, 2303 418, 2318 426, 2339 430, 2350 445, 2348 468, 2354 471, 2370 469, 2378 479, 2377 490)), ((2405 392, 2385 397, 2366 397, 2353 406, 2340 398, 2338 389, 2345 382, 2343 360, 2355 339, 2370 339, 2377 346, 2386 367, 2408 374, 2409 392, 2405 392)), ((2476 394, 2476 408, 2467 426, 2433 456, 2418 461, 2406 455, 2396 454, 2385 460, 2375 460, 2371 451, 2372 439, 2392 421, 2403 419, 2409 414, 2419 401, 2418 395, 2431 387, 2447 371, 2456 370, 2467 375, 2476 385, 2476 394)), ((2848 467, 2847 490, 2835 500, 2823 499, 2810 490, 2800 494, 2799 513, 2805 522, 2806 538, 2801 555, 2794 562, 2760 562, 2746 556, 2740 559, 2736 571, 2737 584, 2762 609, 2768 611, 2784 606, 2796 594, 2802 583, 2809 581, 2818 587, 2813 662, 2801 676, 2790 680, 2775 681, 2768 678, 2760 670, 2755 656, 2746 647, 2719 642, 2701 621, 2672 611, 2656 614, 2651 621, 2643 623, 2634 648, 2627 652, 2620 651, 2594 628, 2591 608, 2581 594, 2560 593, 2541 578, 2529 581, 2509 580, 2497 570, 2495 554, 2488 549, 2480 551, 2460 568, 2461 617, 2451 628, 2436 631, 2414 629, 2375 593, 2374 577, 2380 569, 2388 543, 2397 533, 2423 520, 2438 517, 2440 513, 2452 511, 2457 504, 2465 500, 2475 498, 2484 502, 2488 542, 2495 547, 2504 545, 2511 538, 2537 534, 2550 521, 2552 504, 2548 495, 2542 491, 2538 478, 2523 463, 2515 460, 2500 442, 2501 416, 2511 401, 2506 373, 2515 360, 2525 361, 2540 372, 2560 371, 2570 378, 2573 384, 2572 419, 2576 430, 2576 447, 2579 451, 2588 451, 2597 440, 2616 434, 2633 438, 2644 449, 2650 457, 2647 483, 2652 496, 2653 511, 2657 517, 2671 517, 2681 513, 2711 478, 2728 479, 2729 497, 2720 519, 2726 529, 2728 544, 2735 552, 2746 550, 2749 539, 2756 529, 2757 517, 2765 501, 2768 486, 2799 453, 2801 442, 2809 432, 2827 428, 2841 430, 2849 441, 2848 467)), ((2692 685, 2686 696, 2672 695, 2662 683, 2664 668, 2674 660, 2689 664, 2693 674, 2692 685)), ((2706 440, 2698 451, 2682 450, 2675 440, 2676 428, 2683 421, 2698 422, 2707 431, 2706 440)))

You might get better luck reporting this on the GEOS tracker, as Shapely is using that implementation of buffer.

@wikusbrinkNearmap
Copy link
Author

Thanks @tomplex, is it possible that the problem is with is_valid rather than buffer? Just trying to find the root of the problem.

@tomplex
Copy link
Contributor

tomplex commented Jan 26, 2021

Hi @wikusbrinkNearmap,

This geometry is valid according to GEOS and JTS. Buffering this polygon in JTS did not drop rings, so I suspect this is a GEOS defect.

@tomplex
Copy link
Contributor

tomplex commented Jan 26, 2021

Can you post the result of:

python -c "import shapely.geos; print(shapely.geos.geos_version)"

@wikusbrinkNearmap
Copy link
Author

Can you post the result of:

python -c "import shapely.geos; print(shapely.geos.geos_version)"

I get (3, 8, 1)

@tomplex
Copy link
Contributor

tomplex commented Jan 26, 2021

Thanks. I submitted a ticket to the GEOS tracker regarding this issue.

For now, I would suggest only buffering invalid geometries, or you could install Shapely from source using the master branch and access the validation.make_valid function, if that is your end goal.

@wikusbrinkNearmap
Copy link
Author

Thanks. I submitted a ticket to the GEOS tracker regarding this issue.

For now, I would suggest only buffering invalid geometries, or you could install Shapely from source using the master branch and access the validation.make_valid function, if that is your end goal.

Thanks Tom, appreciate it.

@dr-jts
Copy link

dr-jts commented Jan 26, 2021

This does look like an issue with the GEOS buffer algorithm. And it's a tricky one.

@jorisvandenbossche
Copy link
Member

This seems to work with the latest GEOS version (3.11.1):

In [31]: import shapely.wkt
    ...: 
    ...: with open('../Downloads/shape.txt', 'r') as f:
    ...:     geom = shapely.wkt.loads(f.read())
    ...: 

In [32]: geom
Out[32]: <POLYGON ((4096 1824, 4095 1825, 4083 1836, 4075 1839, 4052 1837, 4042 1823,...>

In [33]: geom.is_valid
Out[33]: True

In [34]: geom.equals(geom.buffer(0))
Out[34]: True

In [35]: shapely.geos_version
Out[35]: (3, 11, 1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants