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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ assert(!$dimension->isCurve());

`GeometryTypeEnum` names the concrete shape of a spatial value, such as a point, line string, polygon,
or geometry collection. It exposes a type's homogeneous component and topological dimension, making
it useful for creating and validating multipart geometries.
it useful for creating and validating multipart geometries. Its `isInstantiable()` method returns
`false` for the abstract `SURFACE` type and `true` for every other enum case, including `MULTICURVE`
and `MULTISURFACE`. The classification considers OGC Simple Feature Access 1.2.1 sections 6.1.2,
6.1.10, and 6.1.13, and ISO/IEC CD 13249-3:201x(E) sections 4.2, 4.2.8, 4.2.16, and 4.2.18; the ISO
source is a Committee Draft, not a published ISO edition.

```php
use LongitudeOne\Core\Enum\GeometryTypeEnum;
Expand All @@ -160,6 +164,7 @@ use LongitudeOne\Core\Enum\TopologicalDimensionEnum;
$type = GeometryTypeEnum::MULTIPOLYGON;

assert($type->isMulti());
assert($type->isInstantiable());
assert(GeometryTypeEnum::POLYGON === $type->componentType());
assert(TopologicalDimensionEnum::SURFACE === $type->topologicalDimension());
```
Expand Down
42 changes: 34 additions & 8 deletions lib/LongitudeOne/Core/Enum/GeometryTypeEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,15 @@ enum GeometryTypeEnum: string
/** A route made of linear and circular segments. */
case COMPOUNDCURVE = 'CompoundCurve';

/** An abstract curve geometry type. */
case CURVE = 'Curve';

/** An area bounded by curves. */
case CURVEPOLYGON = 'CurvePolygon';

/** The generic geometry type. */
case GEOMETRY = 'Geometry';

/** A geometry collection. */
case GEOMETRYCOLLECTION = 'GeometryCollection';

Expand Down Expand Up @@ -57,6 +63,12 @@ enum GeometryTypeEnum: string
/** A surface composed of polygon patches. */
case POLYHEDRALSURFACE = 'PolyhedralSurface';

/** A solid geometry type. */
case SOLID = 'Solid';

/** An abstract surface geometry type. */
case SURFACE = 'Surface';

/** A triangulated irregular network. */
case TIN = 'TIN';

Expand All @@ -73,12 +85,7 @@ public function componentType(): ?self
self::POLYGON, self::MULTILINESTRING, self::TRIANGLE => self::LINESTRING,
self::MULTIPOLYGON, self::POLYHEDRALSURFACE => self::POLYGON,
self::TIN => self::TRIANGLE,
self::COMPOUNDCURVE,
self::CURVEPOLYGON,
self::GEOMETRYCOLLECTION,
self::MULTICURVE,
self::MULTISURFACE,
self::POINT => null,
default => null,
};
}

Expand All @@ -90,6 +97,22 @@ public function isCollection(): bool
return self::GEOMETRYCOLLECTION === $this;
}

/**
* Return whether this geometry type can be instantiated.
*
* GEOMETRY, CURVE, SOLID, and SURFACE are non-instantiable; every other enum case is instantiable.
*/
public function isInstantiable(): bool
{
return match ($this) {
self::CURVE,
self::GEOMETRY,
self::SOLID,
self::SURFACE => false,
default => true,
};
}

/**
* Return whether this is a multi-geometry type.
*/
Expand All @@ -106,14 +129,15 @@ public function isMulti(): bool
}

/**
* Return the topological dimension, or null for a heterogeneous collection.
* Return the topological dimension, or null when it is not specific to a geometry type.
*/
public function topologicalDimension(): ?TopologicalDimensionEnum
{
return match ($this) {
self::POINT, self::MULTIPOINT => TopologicalDimensionEnum::POINT,
self::CIRCULARSTRING,
self::COMPOUNDCURVE,
self::CURVE,
self::LINESTRING,
self::MULTICURVE,
self::MULTILINESTRING => TopologicalDimensionEnum::CURVE,
Expand All @@ -122,9 +146,11 @@ public function topologicalDimension(): ?TopologicalDimensionEnum
self::MULTISURFACE,
self::POLYGON,
self::POLYHEDRALSURFACE,
self::SURFACE,
self::TIN,
self::TRIANGLE => TopologicalDimensionEnum::SURFACE,
self::GEOMETRYCOLLECTION => null,
self::SOLID => TopologicalDimensionEnum::VOLUME,
default => null,
};
}
}
11 changes: 11 additions & 0 deletions lib/LongitudeOne/Core/Enum/TopologicalDimensionEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ enum TopologicalDimensionEnum: int
/** The dimension of a surface geometry. */
case SURFACE = 2;

/** The dimension of a solid geometry. */
case VOLUME = 3;

/**
* Return whether this represents a curve geometry.
*/
Expand Down Expand Up @@ -65,4 +68,12 @@ public function isSurface(): bool
{
return self::SURFACE === $this;
}

/**
* Return whether this represents a volume geometry.
*/
public function isVolume(): bool
{
return self::VOLUME === $this;
}
}
63 changes: 63 additions & 0 deletions tests/LongitudeOne/Core/Tests/Unit/Enum/GeometryTypeEnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,34 @@ public function testCurvedArea(): void
self::assertFalse($type->isMulti());
}

/**
* Generic, abstract, and volumetric types have no homogeneous component.
*/
public function testGenericAndVolumetricTypes(): void
{
$geometry = GeometryTypeEnum::GEOMETRY;
$curve = GeometryTypeEnum::CURVE;
$solid = GeometryTypeEnum::SOLID;

self::assertNull($geometry->componentType());
self::assertNull($geometry->topologicalDimension());
self::assertFalse($geometry->isCollection());
self::assertFalse($geometry->isMulti());
self::assertFalse($geometry->isInstantiable());

self::assertNull($curve->componentType());
self::assertSame(TopologicalDimensionEnum::CURVE, $curve->topologicalDimension());
self::assertFalse($curve->isCollection());
self::assertFalse($curve->isMulti());
self::assertFalse($curve->isInstantiable());

self::assertNull($solid->componentType());
self::assertSame(TopologicalDimensionEnum::VOLUME, $solid->topologicalDimension());
self::assertFalse($solid->isCollection());
self::assertFalse($solid->isMulti());
self::assertFalse($solid->isInstantiable());
}

/**
* Generic multipart curves and surfaces retain their respective dimensions.
*/
Expand All @@ -78,6 +106,41 @@ public function testGenericMultipartGeometries(): void
self::assertTrue($surfaces->isMulti());
}

/**
* Every geometry type has an explicit instantiability classification.
*/
public function testInstantiabilityClassification(): void
{
$expected = [
GeometryTypeEnum::CIRCULARSTRING->name => true,
GeometryTypeEnum::COMPOUNDCURVE->name => true,
GeometryTypeEnum::CURVE->name => false,
GeometryTypeEnum::CURVEPOLYGON->name => true,
GeometryTypeEnum::GEOMETRY->name => false,
GeometryTypeEnum::GEOMETRYCOLLECTION->name => true,
GeometryTypeEnum::LINESTRING->name => true,
GeometryTypeEnum::MULTICURVE->name => true,
GeometryTypeEnum::MULTILINESTRING->name => true,
GeometryTypeEnum::MULTIPOINT->name => true,
GeometryTypeEnum::MULTIPOLYGON->name => true,
GeometryTypeEnum::MULTISURFACE->name => true,
GeometryTypeEnum::POINT->name => true,
GeometryTypeEnum::POLYGON->name => true,
GeometryTypeEnum::POLYHEDRALSURFACE->name => true,
GeometryTypeEnum::SOLID->name => false,
GeometryTypeEnum::SURFACE->name => false,
GeometryTypeEnum::TIN->name => true,
GeometryTypeEnum::TRIANGLE->name => true,
];

self::assertCount(count(GeometryTypeEnum::cases()), $expected);

foreach (GeometryTypeEnum::cases() as $type) {
self::assertArrayHasKey($type->name, $expected);
self::assertSame($expected[$type->name], $type->isInstantiable());
}
}

/**
* A location is a point and has no component geometry.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,18 @@ public function testRoute(): void
self::assertFalse($geometry->isPoint());
self::assertFalse($geometry->isSurface());
}

/**
* A solid is represented by a volume.
*/
public function testVolume(): void
{
$geometry = TopologicalDimensionEnum::VOLUME;

self::assertTrue($geometry->isVolume());
self::assertFalse($geometry->isEmpty());
self::assertFalse($geometry->isPoint());
self::assertFalse($geometry->isCurve());
self::assertFalse($geometry->isSurface());
}
}
Loading