Skip to main content

Simplicity validators

View Markdown versionEdit this page on GitHub

Next version — This documentation describes a major version that is not yet the current stable release of spatial-validator.

View the library roadmap

A geometry can pass ordinary validity while crossing itself, retracing an arc or repeating a position. Simplicity validators add an explicit contract to an already valid geometry; Spatial does not automatically apply these optional checks. They first validate the corresponding geometry type, preserving its diagnostics when ordinary validity fails. See geometry type validators for those prerequisites.

Each section starts with a passing example, then shows a violation with its path, code and default English message. Use this setup for all examples:

<?php

require 'vendor/autoload.php';

use LongitudeOne\SpatialTypes\Types\Dimension2\Geometry\Point;
use LongitudeOne\SpatialTypes\Types\Dimension2\Geometry\LineString;
use LongitudeOne\SpatialTypes\Types\Dimension2\Geometry\CircularString;
use LongitudeOne\SpatialTypes\Types\Dimension2\Geometry\CompoundCurve;
use LongitudeOne\SpatialTypes\Types\Dimension2\Geometry\MultiPoint;
use LongitudeOne\SpatialTypes\Types\Dimension2\Geometry\MultiLineString;
use LongitudeOne\SpatialValidator\Validator\Constraints as SpatialAssert;
use Symfony\Component\Validator\Validation;

$validator = Validation::createValidator();
$showViolations = static function ($spatialObject, $constraint = null) use ($validator): void {
$violations = $validator->validate($spatialObject, $constraint ?? new SpatialAssert\Spatial());
foreach ($violations as $violation) {
echo ($violation->getPropertyPath() ?: '[root]') . ' | '
. $violation->getCode() . ': ' . $violation->getMessage() . PHP_EOL;
}
};

SimpleLineString​

A simple LineString​

A LineString joins positions in order with straight segments. A non-empty valid line contains at least two valid non-EMPTY Points, with matching metadata and no consecutive duplicate positions. It need not be closed or simple; an EMPTY line is also valid. This open route A → B → C passes ordinary validity and explicit simplicity.

Three distinct Points form an open, valid and simple LineString A–B–C.

$spatialObject = new LineString([new Point(0, 0), new Point(2, 1), new Point(4, 0)]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleLineString()); // No violations.

A non-simple LineString​

This crossing line passes ordinary Spatial validation: its Points and consecutive positions are valid. Apply SimpleLineString explicitly to reject the self-intersection.

A crossing LineString is valid under ordinary checks but fails explicit simplicity.

$spatialObject = new LineString([
new Point(0, 0), new Point(2, 2), new Point(0, 2), new Point(2, 0),
]);
$showViolations($spatialObject); // No violations.

$violations = $validator->validate($spatialObject, new SpatialAssert\SimpleLineString());
foreach ($violations as $violation) {
echo ($violation->getPropertyPath() ?: '[root]') . ' | '
. $violation->getCode() . ': ' . $violation->getMessage() . PHP_EOL;
}
[root] | simplelinestring-invalid: The line has a forbidden segment intersection or overlap.

SimpleCircularString​

A simple CircularString​

A CircularString joins circular arcs. A non-empty valid curve has an odd number of at least three valid non-EMPTY defining Points, matching metadata, and each arc's intermediate position differs from both endpoints. EMPTY is also valid. This upper semicircle uses A (-1, 0), B (0, 1) and C (1, 0); it is open, valid and simple.

A circular arc passes through A (-1, 0), B (0, 1) and C (1, 0), forming a valid simple upper semicircle.

$spatialObject = new CircularString([new Point(-1, 0), new Point(0, 1), new Point(1, 0)]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleCircularString()); // No violations.

A non-simple CircularString​

SimpleCircularString first checks ordinary CircularString validity, then looks for forbidden arc intersections and overlaps. This example follows the upper semicircle from A through B to C, then retraces the same semicircle through B back to A. It passes ordinary Spatial validation but fails explicit simplicity. The two arrows show opposite travel directions on the same arc.

The upper semicircle is traversed from A to C and then retraced from C to A; the overlapping arcs fail simplicity.

$spatialObject = new CircularString([
new Point(-1, 0), new Point(0, 1), new Point(1, 0), new Point(0, 1), new Point(-1, 0),
]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleCircularString());
arcs[1] | simplecircularstring-invalid: Circular arcs have a forbidden intersection or overlap.

arcs[1] identifies the second arc. EMPTY passes ordinary validity and simplicity. Collinear defining Points represent the segment between the endpoints, rather than two chords. Spatial checks use XYZ when Z exists and otherwise XY; M is ignored spatially but must remain finite. Non-empty Geography simplicity is unavailable. A single XYZ full circle encoded A → B → A is simple, but comparisons with other arcs cannot determine its plane and report an unavailable-check warning. The warning code is simplecircularstring-unavailable, with the message Circular simplicity is unavailable for this spatial model or arc plane. and default payload ['severity' => 'warning'].

SimpleCompoundCurve​

A simple CompoundCurve​

A valid non-empty CompoundCurve is an ordered chain of valid non-EMPTY line or circular components with matching metadata and consecutive endpoints. It may be open; simplicity is checked separately. This road follows an upper semicircle from A (-1, 0) through B (0, 1) to C (1, 0), then a straight section to D (2, 0). Both components meet at C, so the compound curve is valid. It also passes explicit simplicity.

An upper semicircle joins a straight segment at C, forming a valid simple compound curve.

$spatialObject = new CompoundCurve([
new CircularString([new Point(-1, 0), new Point(0, 1), new Point(1, 0)]),
new LineString([new Point(1, 0), new Point(2, 0)]),
]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleCompoundCurve()); // No violations.

A non-simple CompoundCurve​

Two individually simple components can cross when joined. This example joins the same semicircle to a straight segment from C (1, 0) to D (-1, 1). The join at C is allowed, but the segment also crosses the arc at (-0.6, 0.8). Ordinary validity passes; explicit simplicity rejects that additional intersection.

A segment joins the semicircle at C but also crosses its interior; whole-curve simplicity fails at the straight segment.

$spatialObject = new CompoundCurve([
new CircularString([new Point(-1, 0), new Point(0, 1), new Point(1, 0)]),
new LineString([new Point(1, 0), new Point(-1, 1)]),
]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleCompoundCurve());
curves[1].segments[0] | simplecompoundcurve-invalid: Compound-curve parts have a forbidden intersection or overlap.

curves[1].segments[0] identifies the first segment of the second component. Simplicity checks line/line, line/arc and arc/arc interactions, allowing neighbouring endpoint joins and one global closure. Ordinary validity requires neither closure nor simplicity. Geometry joins compare XYZ when Z exists and otherwise XY, ignoring M.

For non-empty Geography, component and Point checks still run, but joins report compoundcurve-unavailable with Geography compound-curve continuity is unavailable. Explicit simplicity reports simplecompoundcurve-unavailable with Compound-curve simplicity is unavailable for this model or arc plane. These warnings default to payload ['severity' => 'warning']. The indeterminate plane of an XYZ full circle encoded A → B → A also prevents simplicity comparisons with other parts; the validator reports a warning rather than choosing a plane.

SimpleMultiPoint​

A simple MultiPoint​

A MultiPoint groups independent Points. It is valid when its members are valid and their metadata match the collection; valid EMPTY members are allowed. This example also passes explicit simplicity because its non-empty positions are distinct.

Two independent distinct Points form a valid and simple MultiPoint.

$spatialObject = new MultiPoint([new Point(0, 0), new Point(2, 0)]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleMultiPoint()); // No violations.

A non-simple MultiPoint​

Ordinary MultiPoint validity checks the Points, without requiring distinct positions. SimpleMultiPoint adds that requirement, ignoring valid EMPTY members because they have no spatial position. Here points[1] and points[3] both occupy A (0, 0); points[0] is EMPTY and remains in the collection.

The first member is EMPTY; the second and fourth members coincide at A, which fails explicit MultiPoint simplicity.

$spatialObject = new MultiPoint([
new Point(), new Point(0, 0), new Point(2, 0), new Point(0, 0),
]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleMultiPoint());
points[3] | simplemultipoint-invalid: The point repeats a spatial position.

SimpleMultiLineString​

A simple MultiLineString​

A MultiLineString groups LineStrings. It is valid when every member is valid and its metadata match the collection; valid EMPTY members are allowed. These two separate routes also pass explicit simplicity: neither crosses itself or the other route.

Two separate simple LineStrings form a valid MultiLineString and pass explicit simplicity.

$spatialObject = new MultiLineString([
new LineString([new Point(0, 0), new Point(2, 1)]),
new LineString([new Point(0, 2), new Point(2, 3)]),
]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleMultiLineString()); // No violations.

A non-simple MultiLineString​

Two crossing routes can form a valid MultiLineString. SimpleMultiLineString additionally requires each member to be simple and restricts contacts between different lines to their shared boundaries. These two simple segments meet at their interiors, so the explicit simplicity check reports the second line.

Two individually simple segments cross at (1, 1); the MultiLineString is valid but fails explicit simplicity.

$spatialObject = new MultiLineString([
new LineString([new Point(0, 0), new Point(2, 2)]),
new LineString([new Point(0, 2), new Point(2, 0)]),
]);
$showViolations($spatialObject); // No violations.
$showViolations($spatialObject, new SpatialAssert\SimpleMultiLineString());
lineStrings[1] | simplemultilinestring-invalid: The lines intersect outside their shared boundaries or overlap.

A contact at the actual endpoints of both open lines is allowed. Crossings, overlaps and an endpoint lying inside another line fail. A closed line has an empty boundary, so another line cannot touch it even at its repeated starting position. A member's own simplicity failure retains its simplelinestring-invalid code at lineStrings[index].

EMPTY values, dimensions and warnings​

Valid EMPTY curves and collections pass simplicity. EMPTY members' absent positions are skipped for spatial comparisons; ordinary metadata and component checks still apply. This does not admit EMPTY Points inside a non-empty line or arc. Geometry calculations use XYZ when Z exists and otherwise XY; M must be finite but never participates spatially. For the shared coordinate equality policy and exact topology predicates, see numerical comparisons.

Non-empty Geography simplicity is unavailable. Warnings remain Symfony violations and default to payload ['severity' => 'warning']; explicit constraint payloads are preserved. Entirely EMPTY collections need no geodetic simplicity calculation.

ConstraintWarning codeDefault message
SimpleLineStringsimplelinestring-unavailableGeography LineString simplicity is not implemented yet.
SimpleCircularStringsimplecircularstring-unavailableCircular simplicity is unavailable for this spatial model or arc plane.
SimpleCompoundCurvesimplecompoundcurve-unavailableCompound-curve simplicity is unavailable for this model or arc plane.
SimpleMultiPointsimplemultipoint-unavailableGeography SimpleMultiPoint validation is not implemented yet.
SimpleMultiLineStringsimplemultilinestring-unavailableGeography SimpleMultiLineString validation is not implemented yet.

Closure is a separate property: a closed geometry need not be simple, and a simple geometry need not be closed. Ring and surface constraints still compose the simplicity rules required by their own validity contracts; those required boundary checks are explained with geometry type validators. See warning payloads to distinguish warnings from errors.