has_correct_winding

Geometric functions > has_correct_winding

syntax

  • has_correct_winding(polygon_data_item)

description

has_correct_winding(polygon_data_item) results in a bool data item with the same domain unit as the polygon_data_item, True for each element whose ring order is certifiably correct.

An element is True when all of the following hold:

  1. every ring’s orientation agrees with its nesting — outer rings clockwise, holes counter-clockwise, at any nesting depth;
  2. no ring collapsed to fewer than three distinct points;
  3. the geometry is topologically valid: no self-intersecting ring, no hole outside its shell, no overlapping outer rings.

Anything else gives False, so has_correct_winding(g) == False never under-selects the features that need attention.

This is the test that area(g) < 0 used to stand in for. The area test only catches a feature whose rings are all flipped; a feature where only the lake is flipped has an area that is too large rather than negative, and passes it unnoticed. That is exactly the case has_correct_winding was added for.

An undefined or empty geometry has no rings and therefore no winding to get wrong: it answers True, so selecting on the negation does not drag in every null row.

applies to

conditions

The composition type of the polygon_data_item needs to be polygon.

since version

20.18

example

attribute<bool> winding_ok (district) := has_correct_winding(district/geometry);

unit<uint32> broken := select_with_org_rel(!winding_ok)
{
   attribute<rdc> geometry (poly) := fix_winding_order(district/geometry[org_rel]);
}
district/geometry area(…, m2) winding_ok
outer CW, lake CCW 1003100 True
outer CCW, lake CW -1003100 False
outer CW, lake CW (flipped) 1077800 False

The third row is the one area < 0 misses.

see also