Skip to content

contours() emits zero-length, invalid LineStrings when corners equal the contour level #2892

@brendancol

Description

@brendancol

Describe the bug

contours() can emit zero-length, invalid LineString geometries when raster corner values land exactly on the contour level.

Corners are classified with >= level (xrspatial/contour.py lines 77-84), and _emit_seg (lines 163-209) interpolates edge crossings even when the "crossing" is really an edge or corner exactly equal to the level. When a corner equals the level, the interpolation parameter t lands on the corner, so both endpoints of a segment collapse to the same point. The result is a zero-length segment.

Reproduce

A 4x4 checkerboard of 0 and 1 at level=1.0 produces 8 zero-length LineStrings such as LINESTRING (0 1, 0 1, 0 1). Each has length 0 and is_valid == False in Shapely, and they propagate into the GeoDataFrame from return_type="geopandas".

import numpy as np
from xrspatial.contour import contours
from xrspatial.tests.general_checks import create_test_raster

data = np.array([[0., 1., 0., 1.], [1., 0., 1., 0.], [0., 1., 0., 1.], [1., 0., 1., 0.]])
agg = create_test_raster(data, backend='numpy')
gdf = contours(agg, levels=[1.0], return_type='geopandas')
for g in gdf.geometry:
    print(g.is_valid, g.length, g.wkt)

Expected behavior

No zero-length or invalid geometry should reach the output. Degenerate segments (zero-length, or collapsed to a single distinct point) and degenerate polylines should be dropped before stitching and GeoDataFrame output, and equality with the level should be handled the same way every time. This should hold for all four backends (numpy, cupy, dask+numpy, dask+cupy).

Additional context

The numpy, cupy, and dask paths all feed through _emit_seg, and the results become LineStrings in _to_geopandas.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions