Passed
Push — master ( 48e916...a64be8 )
by Doug
13:11
created

CompoundPoint   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Test Coverage

Coverage 94.37%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 66
dl 0
loc 180
ccs 67
cts 71
cp 0.9437
rs 10
c 2
b 0
f 0
wmc 24

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getCRS() 0 3 1
B convert() 0 31 10
A getVerticalPoint() 0 3 1
A __toString() 0 3 1
A geographic3DTo2DPlusGravityHeightOSGM15() 0 20 1
A getCoordinateEpoch() 0 3 1
A calculateDistance() 0 13 3
A create() 0 3 1
A geographic3DTo2DPlusGravityHeightFromGrid() 0 10 1
A getHorizontalPoint() 0 3 1
A geographic2DWithHeightOffsets() 0 11 1
A __construct() 0 10 2
1
<?php
2
/**
3
 * PHPCoord.
4
 *
5
 * @author Doug Wright
6
 */
7
declare(strict_types=1);
8
9
namespace PHPCoord;
10
11
use DateTime;
12
use DateTimeImmutable;
13
use DateTimeInterface;
14
use PHPCoord\CoordinateOperation\AutoConversion;
15
use PHPCoord\CoordinateOperation\ConvertiblePoint;
16
use PHPCoord\CoordinateOperation\GeographicGeoidHeightGrid;
17
use PHPCoord\CoordinateOperation\OSTNOSGM15Grid;
18
use PHPCoord\CoordinateReferenceSystem\Compound;
19
use PHPCoord\CoordinateReferenceSystem\CoordinateReferenceSystem;
20
use PHPCoord\CoordinateReferenceSystem\Geographic2D;
21
use PHPCoord\CoordinateReferenceSystem\Geographic3D;
22
use PHPCoord\CoordinateReferenceSystem\Projected;
0 ignored issues
show
Bug introduced by
The type PHPCoord\CoordinateReferenceSystem\Projected was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use PHPCoord\CoordinateReferenceSystem\Vertical;
24
use PHPCoord\CoordinateSystem\Cartesian;
25
use PHPCoord\Datum\Datum;
26
use PHPCoord\Exception\InvalidCoordinateReferenceSystemException;
27
use PHPCoord\Exception\UnknownConversionException;
28
use PHPCoord\UnitOfMeasure\Angle\Angle;
29
use PHPCoord\UnitOfMeasure\Angle\Degree;
30
use PHPCoord\UnitOfMeasure\Length\Length;
31
use PHPCoord\UnitOfMeasure\Length\Metre;
32
use PHPCoord\UnitOfMeasure\Scale\Unity;
33
34
/**
35
 * Coordinate representing a point expressed in 2 different CRSs (2D horizontal + 1D Vertical).
36
 */
37
class CompoundPoint extends Point implements ConvertiblePoint
38
{
39
    use AutoConversion {
40
        convert as protected autoConvert;
41
    }
42
43
    /**
44
     * Horizontal point.
45
     */
46
    protected GeographicPoint|ProjectedPoint $horizontalPoint;
47
48
    /**
49
     * Vertical point.
50
     */
51
    protected VerticalPoint $verticalPoint;
52
53
    /**
54
     * Coordinate reference system.
55
     */
56
    protected Compound $crs;
57
58
    /**
59
     * Coordinate epoch (date for which the specified coordinates represented this point).
60
     */
61
    protected ?DateTimeImmutable $epoch;
62
63 78
    protected function __construct(Compound $crs, GeographicPoint|ProjectedPoint $horizontalPoint, VerticalPoint $verticalPoint, ?DateTimeInterface $epoch = null)
64
    {
65 78
        $this->horizontalPoint = $horizontalPoint;
66 78
        $this->verticalPoint = $verticalPoint;
67 78
        $this->crs = $crs;
68
69 78
        if ($epoch instanceof DateTime) {
70 9
            $epoch = DateTimeImmutable::createFromMutable($epoch);
71
        }
72 78
        $this->epoch = $epoch;
73 78
    }
74
75 78
    public static function create(Compound $crs, GeographicPoint|ProjectedPoint $horizontalPoint, VerticalPoint $verticalPoint, ?DateTimeInterface $epoch = null): self
76
    {
77 78
        return new static($crs, $horizontalPoint, $verticalPoint, $epoch);
78
    }
79
80 67
    public function getHorizontalPoint(): Point
81
    {
82 67
        return $this->horizontalPoint;
83
    }
84
85 57
    public function getVerticalPoint(): VerticalPoint
86
    {
87 57
        return $this->verticalPoint;
88
    }
89
90 73
    public function getCRS(): Compound
91
    {
92 73
        return $this->crs;
93
    }
94
95 30
    public function getCoordinateEpoch(): ?DateTimeImmutable
96
    {
97 30
        return $this->epoch;
98
    }
99
100
    /**
101
     * Calculate distance between two points.
102
     */
103 18
    public function calculateDistance(Point $to): Length
104
    {
105
        try {
106 18
            if ($to instanceof ConvertiblePoint) {
107 18
                $to = $to->convert($this->crs);
108
            }
109
        } finally {
110 18
            if ($to->getCRS()->getSRID() !== $this->crs->getSRID()) {
111 9
                throw new InvalidCoordinateReferenceSystemException('Can only calculate distances between two points in the same CRS');
112
            }
113
114
            /* @var CompoundPoint $to */
115 9
            return $this->horizontalPoint->calculateDistance($to->horizontalPoint);
116
        }
117
    }
118
119 46
    public function convert(CoordinateReferenceSystem $to, bool $ignoreBoundaryRestrictions = false): Point
120
    {
121
        try {
122 46
            return $this->autoConvert($to, $ignoreBoundaryRestrictions);
123 18
        } catch (UnknownConversionException $e) {
124 18
            if ($this->getHorizontalPoint() instanceof ConvertiblePoint) {
0 ignored issues
show
introduced by
$this->getHorizontalPoint() is always a sub-type of PHPCoord\CoordinateOperation\ConvertiblePoint.
Loading history...
125
                // if 2D target, try again with just the horizontal component
126 18
                if (($to instanceof Geographic2D || $to instanceof Projected)) {
127
                    return $this->getHorizontalPoint()->convert($to, $ignoreBoundaryRestrictions);
128
                }
129
130
                // try separate horizontal + vertical conversions and stitch results together
131 18
                if ($to instanceof Compound) {
132 18
                    $newHorizontalPoint = $this->getHorizontalPoint()->convert($to->getHorizontal());
133
134 18
                    if ($this->getCRS()->getVertical()->getSRID() !== $to->getVertical()->getSRID()) {
135 18
                        $path = $this->findOperationPath($this->getCRS()->getVertical(), $to->getVertical(), $ignoreBoundaryRestrictions);
136
137 9
                        if ($path) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $path of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
138 9
                            $newVerticalPoint = $this->getVerticalPoint();
139 9
                            foreach ($path as $step) {
140 9
                                $target = CoordinateReferenceSystem::fromSRID($step['in_reverse'] ? $step['source_crs'] : $step['target_crs']);
141 9
                                $newVerticalPoint = $newVerticalPoint->performOperation($step['operation'], $target, $step['in_reverse'], ['horizontalPoint' => $newHorizontalPoint]);
142
                            }
143
144 9
                            return static::create($to, $newHorizontalPoint, $newVerticalPoint, $this->epoch);
145
                        }
146
                    }
147
                }
148
            }
149
            throw $e;
150
        }
151
    }
152
153 27
    public function __toString(): string
154
    {
155 27
        return "({$this->horizontalPoint}, {$this->verticalPoint})";
156
    }
157
158
    /**
159
     * Geographic2D with Height Offsets.
160
     * This transformation allows calculation of coordinates in the target system by adding the parameter value to the
161
     * coordinate values of the point in the source system.
162
     */
163 18
    public function geographic2DWithHeightOffsets(
164
        Geographic3D $to,
165
        Angle $latitudeOffset,
166
        Angle $longitudeOffset,
167
        Length $geoidUndulation
168
    ): GeographicPoint {
169 18
        $toLatitude = $this->getHorizontalPoint()->getLatitude()->add($latitudeOffset);
0 ignored issues
show
Bug introduced by
The method getLatitude() does not exist on PHPCoord\ProjectedPoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

169
        $toLatitude = $this->getHorizontalPoint()->/** @scrutinizer ignore-call */ getLatitude()->add($latitudeOffset);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
170 18
        $toLongitude = $this->getHorizontalPoint()->getLongitude()->add($longitudeOffset);
0 ignored issues
show
Bug introduced by
The method getLongitude() does not exist on PHPCoord\ProjectedPoint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

170
        $toLongitude = $this->getHorizontalPoint()->/** @scrutinizer ignore-call */ getLongitude()->add($longitudeOffset);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
171 18
        $toHeight = $this->getVerticalPoint()->getHeight()->add($geoidUndulation);
172
173 18
        return GeographicPoint::create($to, $toLatitude, $toLongitude, $toHeight, $this->epoch);
174
    }
175
176
    /**
177
     * Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB).
178
     * Uses ETRS89 / National Grid as an intermediate coordinate system for bi-linear interpolation of gridded grid
179
     * coordinate differences.
180
     */
181 1
    public function geographic3DTo2DPlusGravityHeightOSGM15(
182
        Geographic3D $to,
183
        OSTNOSGM15Grid $geoidHeightCorrectionModelFile
184
    ): GeographicPoint {
185 1
        $osgb36NationalGrid = Projected::fromSRID(Projected::EPSG_OSGB36_BRITISH_NATIONAL_GRID);
186 1
        $etrs89NationalGrid = new Projected(
187 1
            'ETRS89 / National Grid',
188 1
            Cartesian::fromSRID(Cartesian::EPSG_2D_AXES_EASTING_NORTHING_E_N_ORIENTATIONS_EAST_NORTH_UOM_M),
189 1
            Datum::fromSRID(Datum::EPSG_EUROPEAN_TERRESTRIAL_REFERENCE_SYSTEM_1989_ENSEMBLE),
190 1
            $osgb36NationalGrid->getBoundingArea()
191
        );
192
193 1
        $projected = $this->horizontalPoint->transverseMercator($etrs89NationalGrid, new Degree(49), new Degree(-2), new Unity(0.9996012717), new Metre(400000), new Metre(-100000));
194
195 1
        return GeographicPoint::create(
196 1
            $to,
197 1
            $this->horizontalPoint->getLatitude(),
198 1
            $this->horizontalPoint->getLongitude(),
199 1
            $this->verticalPoint->getHeight()->add($geoidHeightCorrectionModelFile->getHeightAdjustment($projected)),
0 ignored issues
show
Bug introduced by
It seems like $projected can also be of type PHPCoord\GeographicPoint; however, parameter $point of PHPCoord\CoordinateOpera...::getHeightAdjustment() does only seem to accept PHPCoord\ProjectedPoint, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

199
            $this->verticalPoint->getHeight()->add($geoidHeightCorrectionModelFile->getHeightAdjustment(/** @scrutinizer ignore-type */ $projected)),
Loading history...
200 1
            $this->getCoordinateEpoch()
201
        );
202
    }
203
204
    /**
205
     * Geog3D to Geog2D+GravityRelatedHeight.
206
     */
207 2
    public function geographic3DTo2DPlusGravityHeightFromGrid(
208
        Geographic3D $to,
209
        GeographicGeoidHeightGrid $geoidHeightCorrectionModelFile
210
    ): GeographicPoint {
211 2
        return GeographicPoint::create(
212 2
            $to,
213 2
            $this->horizontalPoint->getLatitude(),
214 2
            $this->horizontalPoint->getLongitude(),
215 2
            $this->verticalPoint->getHeight()->add($geoidHeightCorrectionModelFile->getHeightAdjustment($this->horizontalPoint)),
0 ignored issues
show
Bug introduced by
It seems like $this->horizontalPoint can also be of type PHPCoord\ProjectedPoint; however, parameter $location of PHPCoord\CoordinateOpera...::getHeightAdjustment() does only seem to accept PHPCoord\GeographicPoint, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

215
            $this->verticalPoint->getHeight()->add($geoidHeightCorrectionModelFile->getHeightAdjustment(/** @scrutinizer ignore-type */ $this->horizontalPoint)),
Loading history...
216 2
            $this->getCoordinateEpoch()
217
        );
218
    }
219
}
220