Skip to content

Commit 647df27

Browse files
committed
Reveal accuracy of converted points #74
1 parent 6784df3 commit 647df27

19 files changed

+136
-54
lines changed

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# Changelog
22

33
## [Unreleased]
4+
5+
## [5.11.0] - 2025-06-15
6+
### Added
7+
- Added new parameter `accuracy` and new method `getAccuracy()` on `Point` objects to store/reveal accuracy of the point.
8+
You can use this either to store the data for your own use (e.g. survey errors) and/or PHPCoord will update this with
9+
the accuracy of the calculated point after a conversion
410
### Changed
511
- Updates to data for BES Islands, Brazil, Canada, Finland, ITRF, Latvia, Liberia, Mayotte, Uganda and Uzbekistan
612

@@ -361,8 +367,9 @@ Initial release of this fork (based off of v2.3 of original)
361367
- Eastings and northings are rounded to 1m, and lat/long to 5dp (approx 1m) to avoid any misconceptions that precision is the same thing as accuracy.
362368
- When calculating surface distances, a more accurate mean radius is now used rather than that derived from historical definitions of a nautical mile
363369

364-
[Unreleased]: https://github.com/dvdoug/PHPCoord/compare/v5.10.3..master
370+
[Unreleased]: https://github.com/dvdoug/PHPCoord/compare/v5.11.0..master
365371

372+
[5.11.0]: https://github.com/dvdoug/PHPCoord/compare/v5.10.3..v5.11.0
366373
[5.10.3]: https://github.com/dvdoug/PHPCoord/compare/v5.10.2..v5.10.3
367374
[5.10.2]: https://github.com/dvdoug/PHPCoord/compare/v5.10.1..v5.10.2
368375
[5.10.1]: https://github.com/dvdoug/PHPCoord/compare/v5.10.0..v5.10.1

docs/coordinate_conversions_easy.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ between almost any two CRSs is possible as long as they have a common link.
7878
the target CRS). If you know a transformation should be possible, but PHPCoord cannot find it you may wish
7979
to try an explicit 2-stage conversion (e.g. source to WGS84, WGS84 to target).
8080

81+
Accuracy
82+
--------
83+
After a conversion, you can call ``getAccuracy()`` on the new point to reveal the fidelity of the conversion. PHPCoord
84+
will always pick the most accurate conversion it knows how to do.
85+
86+
8187
Boundaries
8288
----------
8389
Every CRS has a defined area ("extent") over which it operates. Some are worldwide (e.g. WGS84), but most are regional

src/CoordinateOperation/AutoConversion.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
use PHPCoord\Geometry\RegionMap;
2727
use PHPCoord\Point\Point;
2828
use PHPCoord\Point\ProjectedPoint;
29+
use PHPCoord\UnitOfMeasure\Length\Length;
30+
use PHPCoord\UnitOfMeasure\Length\Metre;
2931
use PHPCoord\UnitOfMeasure\Time\Time;
3032
use PHPCoord\UnitOfMeasure\Time\Year;
3133
use PHPCoord\Point\VerticalPoint;
@@ -69,7 +71,7 @@ public function convert(Compound|Geocentric|Geographic2D|Geographic3D|Projected|
6971

7072
foreach ($path as $step) {
7173
$target = CoordinateReferenceSystem::fromSRID($step['in_reverse'] ? $step['source_crs'] : $step['target_crs']);
72-
$point = $point->performOperation($step['operation'], $target, $step['in_reverse']);
74+
$point = $point->performOperation($step['operation'], $target, $step['in_reverse'], new Metre($step['accuracy']));
7375
}
7476

7577
return $point;
@@ -383,5 +385,5 @@ abstract public function getCRS(): CoordinateReferenceSystem;
383385

384386
abstract public function getCoordinateEpoch(): ?DateTimeImmutable;
385387

386-
abstract protected function performOperation(string $srid, Compound|Geocentric|Geographic2D|Geographic3D|Projected|Vertical $to, bool $inReverse): Point;
388+
abstract protected function performOperation(string $srid, Compound|Geocentric|Geographic2D|Geographic3D|Projected|Vertical $to, bool $inReverse, Length $accuracy): Point;
387389
}

src/Point/BritishNationalGridPoint.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ class BritishNationalGridPoint extends ProjectedPoint
2929
{
3030
private const GRID_LETTERS = 'VWXYZQRSTULMNOPFGHJKABCDE';
3131

32-
public function __construct(Length $easting, Length $northing, ?DateTimeInterface $epoch = null)
32+
public function __construct(Length $easting, Length $northing, ?DateTimeInterface $epoch = null, ?Length $accuracy = null)
3333
{
34-
parent::__construct(Projected::fromSRID(Projected::EPSG_OSGB36_BRITISH_NATIONAL_GRID), $easting, $northing, null, null, $epoch, null);
34+
parent::__construct(Projected::fromSRID(Projected::EPSG_OSGB36_BRITISH_NATIONAL_GRID), $easting, $northing, null, null, $epoch, null, $accuracy);
3535
}
3636

3737
/**
3838
* @param string $reference OS grid reference (e.g. "TG514131")
3939
*/
40-
public static function fromGridReference(string $reference, ?DateTimeInterface $epoch = null): self
40+
public static function fromGridReference(string $reference, ?DateTimeInterface $epoch = null, ?Length $accuracy = null): self
4141
{
4242
$reference = str_replace(' ', '', $reference);
4343

@@ -61,7 +61,7 @@ public static function fromGridReference(string $reference, ?DateTimeInterface $
6161
$easting = $majorEasting + $minorEasting + ((int) substr($numericPortion, 0, (int) $numericPortionSize) * $gridSizeInMetres);
6262
$northing = $majorNorthing + $minorNorthing + ((int) substr($numericPortion, -(int) $numericPortionSize, (int) $numericPortionSize) * $gridSizeInMetres);
6363

64-
return new self(new Metre($easting), new Metre($northing), $epoch);
64+
return new self(new Metre($easting), new Metre($northing), $epoch, $accuracy);
6565
}
6666

6767
/**

src/Point/CompoundPoint.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ class CompoundPoint extends Point implements ConvertiblePoint
6565
*/
6666
protected ?DateTimeImmutable $epoch;
6767

68-
protected function __construct(Compound $crs, GeographicPoint|ProjectedPoint $horizontalPoint, VerticalPoint $verticalPoint, ?DateTimeInterface $epoch = null)
68+
/**
69+
* Accuracy.
70+
*/
71+
protected ?Length $accuracy;
72+
73+
protected function __construct(Compound $crs, GeographicPoint|ProjectedPoint $horizontalPoint, VerticalPoint $verticalPoint, ?DateTimeInterface $epoch = null, ?Length $accuracy = null)
6974
{
7075
$this->horizontalPoint = $horizontalPoint;
7176
$this->verticalPoint = $verticalPoint;
@@ -75,11 +80,12 @@ protected function __construct(Compound $crs, GeographicPoint|ProjectedPoint $ho
7580
$epoch = DateTimeImmutable::createFromMutable($epoch);
7681
}
7782
$this->epoch = $epoch;
83+
$this->accuracy = $accuracy;
7884
}
7985

80-
public static function create(Compound $crs, GeographicPoint|ProjectedPoint $horizontalPoint, VerticalPoint $verticalPoint, ?DateTimeInterface $epoch = null): self
86+
public static function create(Compound $crs, GeographicPoint|ProjectedPoint $horizontalPoint, VerticalPoint $verticalPoint, ?DateTimeInterface $epoch = null, ?Length $accuracy = null): self
8187
{
82-
return new self($crs, $horizontalPoint, $verticalPoint, $epoch);
88+
return new self($crs, $horizontalPoint, $verticalPoint, $epoch, $accuracy);
8389
}
8490

8591
public function getHorizontalPoint(): GeographicPoint|ProjectedPoint
@@ -102,6 +108,11 @@ public function getCoordinateEpoch(): ?DateTimeImmutable
102108
return $this->epoch;
103109
}
104110

111+
public function getAccuracy(): ?Length
112+
{
113+
return $this->accuracy;
114+
}
115+
105116
/**
106117
* Calculate distance between two points.
107118
*/
@@ -144,7 +155,7 @@ public function convert(Compound|Geocentric|Geographic2D|Geographic3D|Projected|
144155
foreach ($path as $step) {
145156
$target = CoordinateReferenceSystem::fromSRID($step['in_reverse'] ? $step['source_crs'] : $step['target_crs']);
146157
/** @var VerticalPoint $newVerticalPoint */
147-
$newVerticalPoint = $newVerticalPoint->performOperation($step['operation'], $target, $step['in_reverse'], ['horizontalPoint' => $newHorizontalPoint]);
158+
$newVerticalPoint = $newVerticalPoint->performOperation($step['operation'], $target, $step['in_reverse'], new Metre($step['accuracy']), ['horizontalPoint' => $newHorizontalPoint]);
148159
}
149160

150161
return static::create($to, $newHorizontalPoint, $newVerticalPoint, $this->epoch);

src/Point/GeocentricPoint.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ class GeocentricPoint extends Point implements ConvertiblePoint
6969
*/
7070
protected ?DateTimeImmutable $epoch;
7171

72-
protected function __construct(Geocentric $crs, Length $x, Length $y, Length $z, ?DateTimeInterface $epoch = null)
72+
/**
73+
* Accuracy.
74+
*/
75+
protected ?Length $accuracy;
76+
77+
protected function __construct(Geocentric $crs, Length $x, Length $y, Length $z, ?DateTimeInterface $epoch = null, ?Length $accuracy = null)
7378
{
7479
$this->crs = $crs;
7580
$this->x = $x::convert($x, $this->crs->getCoordinateSystem()->getAxisByName(Axis::GEOCENTRIC_X)->getUnitOfMeasureId());
@@ -80,6 +85,7 @@ protected function __construct(Geocentric $crs, Length $x, Length $y, Length $z,
8085
$epoch = DateTimeImmutable::createFromMutable($epoch);
8186
}
8287
$this->epoch = $epoch;
88+
$this->accuracy = $accuracy;
8389
}
8490

8591
/**
@@ -117,6 +123,11 @@ public function getCoordinateEpoch(): ?DateTimeImmutable
117123
return $this->epoch;
118124
}
119125

126+
public function getAccuracy(): ?Length
127+
{
128+
return $this->accuracy;
129+
}
130+
120131
/**
121132
* Calculate surface distance between two points.
122133
*/

src/Point/GeographicPoint.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ class GeographicPoint extends Point implements ConvertiblePoint
101101
*/
102102
protected ?DateTimeImmutable $epoch;
103103

104-
protected function __construct(Geographic2D|Geographic3D $crs, Angle $latitude, Angle $longitude, ?Length $height, ?DateTimeInterface $epoch)
104+
/**
105+
* Accuracy.
106+
*/
107+
protected ?Length $accuracy;
108+
109+
protected function __construct(Geographic2D|Geographic3D $crs, Angle $latitude, Angle $longitude, ?Length $height, ?DateTimeInterface $epoch, ?Length $accuracy = null)
105110
{
106111
if ($crs instanceof Geographic2D && $height !== null) {
107112
throw new InvalidCoordinateReferenceSystemException('A 2D geographic point must not include a height');
@@ -129,6 +134,7 @@ protected function __construct(Geographic2D|Geographic3D $crs, Angle $latitude,
129134
$epoch = DateTimeImmutable::createFromMutable($epoch);
130135
}
131136
$this->epoch = $epoch;
137+
$this->accuracy = $accuracy;
132138
}
133139

134140
/**
@@ -166,6 +172,11 @@ public function getCoordinateEpoch(): ?DateTimeImmutable
166172
return $this->epoch;
167173
}
168174

175+
public function getAccuracy(): ?Length
176+
{
177+
return $this->accuracy;
178+
}
179+
169180
protected function normaliseLatitude(Angle $latitude): Angle
170181
{
171182
if ($latitude->asDegrees()->getValue() > 90) {
@@ -2354,7 +2365,7 @@ public function asUTMPoint(): UTMPoint
23542365
);
23552366

23562367
/** @var ProjectedPoint $asProjected */
2357-
$asProjected = $this->performOperation($derivingConversion, $projectedCRS, false);
2368+
$asProjected = $this->performOperation($derivingConversion, $projectedCRS, false, new Metre(0));
23582369

23592370
return new UTMPoint($this->crs, $asProjected->getEasting(), $asProjected->getNorthing(), $zone, $hemisphere, $this->epoch);
23602371
}

src/Point/IrishGridPoint.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ class IrishGridPoint extends ProjectedPoint
3232
{
3333
private const GRID_LETTERS = 'VWXYZQRSTULMNOPFGHJKABCDE';
3434

35-
public function __construct(Length $easting, Length $northing, ?DateTimeInterface $epoch = null)
35+
public function __construct(Length $easting, Length $northing, ?DateTimeInterface $epoch = null, ?Length $accuracy = null)
3636
{
37-
parent::__construct(Projected::fromSRID(Projected::EPSG_TM75_IRISH_GRID), $easting, $northing, null, null, $epoch, null);
37+
parent::__construct(Projected::fromSRID(Projected::EPSG_TM75_IRISH_GRID), $easting, $northing, null, null, $epoch, null, $accuracy);
3838
}
3939

4040
/**
4141
* @param string $reference Irish grid reference (e.g. "T514131")
4242
*/
43-
public static function fromGridReference(string $reference, ?DateTimeInterface $epoch = null): self
43+
public static function fromGridReference(string $reference, ?DateTimeInterface $epoch = null, ?Length $accuracy = null): self
4444
{
4545
$reference = str_replace(' ', '', $reference);
4646

@@ -60,7 +60,7 @@ public static function fromGridReference(string $reference, ?DateTimeInterface $
6060
$easting = $minorEasting + ((int) substr($numericPortion, 0, (int) $numericPortionSize) * $gridSizeInMetres);
6161
$northing = $minorNorthing + ((int) substr($numericPortion, -(int) $numericPortionSize, (int) $numericPortionSize) * $gridSizeInMetres);
6262

63-
return new self(new Metre($easting), new Metre($northing), $epoch);
63+
return new self(new Metre($easting), new Metre($northing), $epoch, $accuracy);
6464
}
6565

6666
/**

src/Point/IrishTransverseMercatorPoint.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
*/
1919
class IrishTransverseMercatorPoint extends ProjectedPoint
2020
{
21-
public function __construct(Length $easting, Length $northing, ?DateTimeInterface $epoch = null)
21+
public function __construct(Length $easting, Length $northing, ?DateTimeInterface $epoch = null, ?Length $accuracy = null)
2222
{
23-
parent::__construct(Projected::fromSRID(Projected::EPSG_IRENET95_IRISH_TRANSVERSE_MERCATOR), $easting, $northing, null, null, $epoch, null);
23+
parent::__construct(Projected::fromSRID(Projected::EPSG_IRENET95_IRISH_TRANSVERSE_MERCATOR), $easting, $northing, null, null, $epoch, null, $accuracy);
2424
}
2525
}

src/Point/Point.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use PHPCoord\CoordinateReferenceSystem\Vertical;
2424
use PHPCoord\UnitOfMeasure\Angle\Angle;
2525
use PHPCoord\UnitOfMeasure\Length\Length;
26+
use PHPCoord\UnitOfMeasure\Length\Metre;
2627
use PHPCoord\UnitOfMeasure\Scale\Coefficient;
2728
use PHPCoord\UnitOfMeasure\Scale\Scale;
2829
use PHPCoord\UnitOfMeasure\UnitOfMeasure;
@@ -77,14 +78,16 @@ abstract class Point implements Stringable
7778
* @internal
7879
* @param array{horizontalPoint?: Point} $additionalParams
7980
*/
80-
public function performOperation(string $srid, Compound|Geocentric|Geographic2D|Geographic3D|Projected|Vertical $to, bool $inReverse, array $additionalParams = []): self
81+
public function performOperation(string $srid, Compound|Geocentric|Geographic2D|Geographic3D|Projected|Vertical $to, bool $inReverse, Length $accuracy, array $additionalParams = []): self
8182
{
8283
$operation = CoordinateOperations::getOperationData($srid);
8384

8485
if ($operation['method'] === CoordinateOperationMethods::EPSG_ALIAS) {
8586
$point = clone $this;
8687
assert(property_exists($point, 'crs'));
8788
$point->crs = $to;
89+
assert(property_exists($point, 'accuracy'));
90+
$point->accuracy ??= new Metre(0);
8891

8992
return $point;
9093
} else {
@@ -95,7 +98,13 @@ public function performOperation(string $srid, Compound|Geocentric|Geographic2D|
9598
$params['horizontalPoint'] = $additionalParams['horizontalPoint'];
9699
}
97100

98-
return $this->$method($to, ...$params);
101+
$point = $this->$method($to, ...$params);
102+
assert(property_exists($this, 'accuracy'));
103+
assert(property_exists($point, 'accuracy'));
104+
$existingAccuracy = $this->accuracy ?? new Metre(0);
105+
$point->accuracy = $existingAccuracy->add($accuracy);
106+
107+
return $point;
99108
}
100109
}
101110

@@ -353,5 +362,7 @@ abstract public function getCRS(): CoordinateReferenceSystem;
353362

354363
abstract public function getCoordinateEpoch(): ?DateTimeImmutable;
355364

365+
abstract public function getAccuracy(): ?Length;
366+
356367
abstract public function calculateDistance(self $to): Length;
357368
}

0 commit comments

Comments
 (0)