Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/immutability.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ and in-place operations. Mutable `LineString` and `CircularString` classes are a
each dimension namespace. See [mutable paths](instantiable-spatial-types.md#mutable-line-strings-and-circular-strings)
for point ownership, propagation and arc indexing. Mutable `CompoundCurve` classes also retain mutable curves and their points by
reference. See [mutable compound curves](instantiable-spatial-types.md#mutable-compound-curves)
for component editing and recursive context propagation. Other mutable aggregates
for component editing and recursive context propagation. Mutable `Polygon` and `Triangle` classes retain rings and points by reference;
see [mutable surfaces](instantiable-spatial-types.md#mutable-polygons-and-triangles)
for ring editing and failure atomicity. Other mutable aggregates
arrive in later Stories of Epic #38.
Existing factories create the explicitly named immutable classes and expose
immutable return contracts.
Expand Down
79 changes: 79 additions & 0 deletions docs/instantiable-spatial-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -777,3 +777,82 @@ owners. Passing `false` updates only the receiver. No context setter recalculate
coordinates, changes the compound's spatial type or changes its layout.
`toArray()` preserves component boundaries and their defining point tuples;
circular components are never flattened or approximated.

## Mutable polygons and triangles

`Polygon` and `Triangle` are available directly in `Types\Dimension2`,
`Types\Dimension3z`, `Types\Dimension3m`, and `Types\Dimension4zm`.
Both constructors have the same signature:

```php
new Polygon(array $rings, int|SpatialReference $srid = 0,
SpatialModelEnum $family = SpatialModelEnum::GEOMETRY);
new Triangle(array $rings, int|SpatialReference $srid = 0,
SpatialModelEnum $family = SpatialModelEnum::GEOMETRY);
```

Each input ring is a `MutableLineStringInterface` or an array of mutable points
and coordinate tuples. Arrays create mutable line strings and points using the
surface's current layout, family and reference. Supplied spatial objects retain
their own context and identity. Immutable rings and points are rejected without
conversion, including immutable points exposed by a custom ring.

| Operation | Effect |
| --- | --- |
| `addRing($ring)` | Append a mutable line string |
| `insertRing($index, $ring)` | Insert before the addressed ring; the count appends |
| `removeRing($index)` | Remove an existing ring and reindex |
| `setRing($index, $ring)` | Replace one existing ring |
| `setPoint($ringIndex, $pointIndex, $point)` | Replace one point in an existing ring |
| `setArrayOfCoordinates($coordinates)` | Replace all rings from coordinate tuples |
| `getRing($index)`, `getRings()`, `getElements()` | Return the retained mutable ring instances |

All successful mutations return the receiver as `static`. Ring and point indices
start at zero; access, replacement and removal require an existing element.
Negative indices and indices beyond the addressed sequence are rejected with
`OutOfBoundsException`. Failed addressing or technical input checks leave the
surface and shared descendants unchanged, including a late invalid tuple during
complete coordinate replacement.

```php
use LongitudeOne\Core\Enum\SpatialModelEnum;
use LongitudeOne\SpatialTypes\Types\Dimension2\LineString;
use LongitudeOne\SpatialTypes\Types\Dimension2\Point;
use LongitudeOne\SpatialTypes\Types\Dimension2\Polygon;
use LongitudeOne\SpatialTypes\Types\Dimension2\Triangle;

$ring = new LineString([[0, null], [3, 4]], 4326);
$polygon = new Polygon([$ring], 4326);
$triangle = new Triangle([$ring], 4326);
$replacement = new Point(7, null, 4326);

$samePolygon = $polygon->setPoint(0, 0, $replacement);
assert($polygon === $samePolygon);
assert($replacement === $triangle->getRing(0)->getPoint(0));
assert([[[7, null], [3, 4]]] === $triangle->toArray());

$polygon->setFamily(SpatialModelEnum::GEOGRAPHY, false); // Only the polygon.
$polygon->setSrid(2154); // Polygon, shared ring and every point.
assert(2154 === $triangle->getRing(0)->getSrid());
assert(4326 === $triangle->getSrid());
```

Construction, insertion and replacement retain supplied components without
cloning; getters expose the same instances. Changes to a shared ring or point
are visible in every surface containing it. Coordinate replacement creates new
descendants and leaves the previous rings and their other owners unchanged.

`setSrid()`, `setSpatialReference()` and `setFamily()` each accept a second
`bool $propagate = true` parameter. By default they update all rings and points
recursively; `false` updates only the receiver. No ordinates are recalculated.
The instance's spatial type and coordinate layout remain fixed. Mutable surfaces
have no `with…` methods or dimension setter; immutable counterparts keep their
copy-producing operations and reject mutable components.

Open or incomplete rings, EMPTY rings, null ordinates and triangles with extra
points or rings remain representable. Editing preserves the supplied order and
does not close rings, synchronize endpoints, enforce orientation or validate
geometry. ISO/IEC CD 13249-3:201x(E), clauses 8.3.1 and 8.4.1, define the polygon
and triangle models; representing a value does not establish conformity with
those geometric definitions. Request functional checks through
[spatial-validator](validators.md) explicitly.
3 changes: 2 additions & 1 deletion docs/spatial-reference-systems.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ The Lambert 93 ordinates in this example must be calculated by that external
transformer; this library intentionally does not provide it.

Mutable line strings and circular strings propagate declared-context updates to
contained points by default. Mutable compound curves recursively propagate to
contained points by default. Mutable polygons and triangles recursively propagate to rings and their points.
Mutable compound curves recursively propagate to
all components and their points. Their `setSrid()`, `setSpatialReference()` and
`setFamily()` methods accept `$propagate = false` to change only the receiver.
Propagation changes shared instances, so their other owners observe the updated
Expand Down
15 changes: 15 additions & 0 deletions docs/type-class-hierarchy.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,18 @@ AbstractSpatialType
`MutableCompoundCurveInterface` refines component getters to mutable line-string
or circular-string interfaces and exposes ordered membership editing. Immutable
compound curves retain their separate immutable base and copy contracts.

Mutable polygons and triangles share ring storage and editing in a separate base:

```text
AbstractSpatialType
└── AbstractPolygon
├── Dimension{2,3m,3z,4zm}\Polygon
└── Dimension{2,3m,3z,4zm}\Triangle
```

`MutablePolygonInterface` refines ring accessors to `MutableLineStringInterface`
and provides ring editing and ring-then-point replacement. `MutableTriangleInterface`
adds the distinct triangle type contract. Both concrete variants retain mutable
rings and points by reference and expose no immutable copy operations. Their
layout and spatial type are fixed; geometric validity remains explicit.
59 changes: 58 additions & 1 deletion lib/Interfaces/MutablePolygonInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,64 @@

namespace LongitudeOne\SpatialTypes\Interfaces;

/** Mutable polygon contract; type-specific editing is introduced separately. */
use LongitudeOne\Core\Enum\SpatialModelEnum;
use LongitudeOne\SpatialTypes\Reference\SpatialReference;

/** In-place ordered ring and point editing without geometric validation. */
interface MutablePolygonInterface extends MutableSpatialInterface, PolygonInterface
{
/** @param MutableLineStringInterface $ring Ring retained without cloning */
public function addRing(MutableLineStringInterface $ring): static;

/** @return list<MutableLineStringInterface> */
public function getElements(): array;

/** @param int $index Existing zero-based ring index */
public function getRing(int $index): MutableLineStringInterface;

/** @return list<MutableLineStringInterface> */
public function getRings(): array;

/**
* @param int $index Zero-based insertion index, up to the ring count
* @param MutableLineStringInterface $ring Ring retained without cloning
*/
public function insertRing(int $index, MutableLineStringInterface $ring): static;

/** @param int $index Existing zero-based ring index */
public function removeRing(int $index): static;

/** @param array<array<array{0: null|float|int|string, 1: null|float|int|string, 2 ?: null|float|int, 3 ?: null|float|int}>> $coordinates Complete replacement ring coordinates */
public function setArrayOfCoordinates(array $coordinates): static;

/**
* @param SpatialModelEnum $family Declared family
* @param bool $propagate Whether to update rings and their points
*/
public function setFamily(SpatialModelEnum $family, bool $propagate = true): static;

/**
* @param int $ringIndex Existing zero-based ring index
* @param int $pointIndex Existing zero-based point index within the ring
* @param MutablePointInterface $point Point retained without cloning
*/
public function setPoint(int $ringIndex, int $pointIndex, MutablePointInterface $point): static;

/**
* @param int $index Existing zero-based ring index
* @param MutableLineStringInterface $ring Ring retained without cloning
*/
public function setRing(int $index, MutableLineStringInterface $ring): static;

/**
* @param SpatialReference $reference Declared reference
* @param bool $propagate Whether to update rings and their points
*/
public function setSpatialReference(SpatialReference $reference, bool $propagate = true): static;

/**
* @param int $srid Declared reference identifier
* @param bool $propagate Whether to update rings and their points
*/
public function setSrid(int $srid, bool $propagate = true): static;
}
2 changes: 1 addition & 1 deletion lib/Interfaces/MutableTriangleInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace LongitudeOne\SpatialTypes\Interfaces;

/** Mutable triangle contract; type-specific editing is introduced separately. */
/** Mutable triangle contract with inherited ordered ring and point editing. */
interface MutableTriangleInterface extends MutablePolygonInterface, TriangleInterface
{
}
Loading
Loading