Passed
Push — master ( fd93b5...48e916 )
by Doug
40:26 queued 29:39
created

geographic3DTo2DPlusGravityHeightOSGM15()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 13
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 20
ccs 13
cts 13
cp 1
crap 1
rs 9.8333
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
     * @var GeographicPoint|ProjectedPoint
46
     */
47
    protected Point $horizontalPoint;
48
49
    /**
50
     * Vertical point.
51
     */
52
    protected VerticalPoint $verticalPoint;
53
54
    /**
55
     * Coordinate reference system.
56
     */
57
    protected Compound $crs;
58
59
    /**
60
     * Coordinate epoch (date for which the specified coordinates represented this point).
61
     */
62
    protected ?DateTimeImmutable $epoch;
63
64
    /**
65
     * Constructor.
66
     * @param GeographicPoint|ProjectedPoint $horizontalPoint
67
     */
68 78
    protected function __construct(Point $horizontalPoint, VerticalPoint $verticalPoint, Compound $crs, ?DateTimeInterface $epoch = null)
69
    {
70 78
        $this->horizontalPoint = $horizontalPoint;
0 ignored issues
show
Documentation Bug introduced by
$horizontalPoint is of type PHPCoord\Point, but the property $horizontalPoint was declared to be of type PHPCoord\GeographicPoint|PHPCoord\ProjectedPoint. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
71 78
        $this->verticalPoint = $verticalPoint;
72 78
        $this->crs = $crs;
73
74 78
        if ($epoch instanceof DateTime) {
75 9
            $epoch = DateTimeImmutable::createFromMutable($epoch);
76
        }
77 78
        $this->epoch = $epoch;
78 78
    }
79
80
    /**
81
     * @param GeographicPoint|ProjectedPoint $horizontalPoint
82
     */
83 78
    public static function create(Point $horizontalPoint, VerticalPoint $verticalPoint, Compound $crs, ?DateTimeInterface $epoch = null)
84
    {
85 78
        return new static($horizontalPoint, $verticalPoint, $crs, $epoch);
86
    }
87
88 67
    public function getHorizontalPoint(): Point
89
    {
90 67
        return $this->horizontalPoint;
91
    }
92
93 57
    public function getVerticalPoint(): VerticalPoint
94
    {
95 57
        return $this->verticalPoint;
96
    }
97
98 73
    public function getCRS(): Compound
99
    {
100 73
        return $this->crs;
101
    }
102
103 30
    public function getCoordinateEpoch(): ?DateTimeImmutable
104
    {
105 30
        return $this->epoch;
106
    }
107
108
    /**
109
     * Calculate distance between two points.
110
     */
111 18
    public function calculateDistance(Point $to): Length
112
    {
113
        try {
114 18
            if ($to instanceof ConvertiblePoint) {
115 18
                $to = $to->convert($this->crs);
116
            }
117
        } finally {
118 18
            if ($to->getCRS()->getSRID() !== $this->crs->getSRID()) {
119 9
                throw new InvalidCoordinateReferenceSystemException('Can only calculate distances between two points in the same CRS');
120
            }
121
122
            /* @var CompoundPoint $to */
123 9
            return $this->horizontalPoint->calculateDistance($to->horizontalPoint);
124
        }
125
    }
126
127 46
    public function convert(CoordinateReferenceSystem $to, bool $ignoreBoundaryRestrictions = false): Point
128
    {
129
        try {
130 46
            return $this->autoConvert($to, $ignoreBoundaryRestrictions);
131 18
        } catch (UnknownConversionException $e) {
132 18
            if ($this->getHorizontalPoint() instanceof ConvertiblePoint) {
133
                // if 2D target, try again with just the horizontal component
134 18
                if (($to instanceof Geographic2D || $to instanceof Projected)) {
135
                    return $this->getHorizontalPoint()->convert($to, $ignoreBoundaryRestrictions);
136
                }
137
138
                // try separate horizontal + vertical conversions and stitch results together
139 18
                if ($to instanceof Compound) {
140 18
                    $newHorizontalPoint = $this->getHorizontalPoint()->convert($to->getHorizontal());
141
142 18
                    if ($this->getCRS()->getVertical()->getSRID() !== $to->getVertical()->getSRID()) {
143 18
                        $path = $this->findOperationPath($this->getCRS()->getVertical(), $to->getVertical(), $ignoreBoundaryRestrictions);
144
145 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...
146 9
                            $newVerticalPoint = $this->getVerticalPoint();
147 9
                            foreach ($path as $step) {
148 9
                                $target = CoordinateReferenceSystem::fromSRID($step['in_reverse'] ? $step['source_crs'] : $step['target_crs']);
149 9
                                $newVerticalPoint = $newVerticalPoint->performOperation($step['operation'], $target, $step['in_reverse'], ['horizontalPoint' => $newHorizontalPoint]);
150
                            }
151
152 9
                            return static::create($newHorizontalPoint, $newVerticalPoint, $to, $this->epoch);
153
                        }
154
                    }
155
                }
156
            }
157
            throw $e;
158
        }
159
    }
160
161 27
    public function __toString(): string
162
    {
163 27
        return "({$this->horizontalPoint}, {$this->verticalPoint})";
164
    }
165
166
    /**
167
     * Geographic2D with Height Offsets.
168
     * This transformation allows calculation of coordinates in the target system by adding the parameter value to the
169
     * coordinate values of the point in the source system.
170
     */
171 18
    public function geographic2DWithHeightOffsets(
172
        Geographic3D $to,
173
        Angle $latitudeOffset,
174
        Angle $longitudeOffset,
175
        Length $geoidUndulation
176
    ): GeographicPoint {
177 18
        $toLatitude = $this->getHorizontalPoint()->getLatitude()->add($latitudeOffset);
0 ignored issues
show
Bug introduced by
The method getLatitude() does not exist on PHPCoord\Point. It seems like you code against a sub-type of PHPCoord\Point such as PHPCoord\GeographicPoint. ( Ignorable by Annotation )

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

177
        $toLatitude = $this->getHorizontalPoint()->/** @scrutinizer ignore-call */ getLatitude()->add($latitudeOffset);
Loading history...
178 18
        $toLongitude = $this->getHorizontalPoint()->getLongitude()->add($longitudeOffset);
0 ignored issues
show
Bug introduced by
The method getLongitude() does not exist on PHPCoord\Point. It seems like you code against a sub-type of PHPCoord\Point such as PHPCoord\GeographicPoint. ( Ignorable by Annotation )

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

178
        $toLongitude = $this->getHorizontalPoint()->/** @scrutinizer ignore-call */ getLongitude()->add($longitudeOffset);
Loading history...
179 18
        $toHeight = $this->getVerticalPoint()->getHeight()->add($geoidUndulation);
180
181 18
        return GeographicPoint::create($toLatitude, $toLongitude, $toHeight, $to, $this->epoch);
182
    }
183
184
    /**
185
     * Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB).
186
     * Uses ETRS89 / National Grid as an intermediate coordinate system for bi-linear interpolation of gridded grid
187
     * coordinate differences.
188
     */
189 1
    public function geographic3DTo2DPlusGravityHeightOSGM15(
190
        Geographic3D $to,
191
        OSTNOSGM15Grid $geoidHeightCorrectionModelFile
192
    ): GeographicPoint {
193 1
        $osgb36NationalGrid = Projected::fromSRID(Projected::EPSG_OSGB36_BRITISH_NATIONAL_GRID);
194 1
        $etrs89NationalGrid = new Projected(
195 1
            'ETRS89 / National Grid',
196 1
            Cartesian::fromSRID(Cartesian::EPSG_2D_AXES_EASTING_NORTHING_E_N_ORIENTATIONS_EAST_NORTH_UOM_M),
197 1
            Datum::fromSRID(Datum::EPSG_EUROPEAN_TERRESTRIAL_REFERENCE_SYSTEM_1989_ENSEMBLE),
198 1
            $osgb36NationalGrid->getBoundingArea()
199
        );
200
201 1
        $projected = $this->horizontalPoint->transverseMercator($etrs89NationalGrid, new Degree(49), new Degree(-2), new Unity(0.9996012717), new Metre(400000), new Metre(-100000));
0 ignored issues
show
Bug introduced by
The method transverseMercator() does not exist on PHPCoord\Point. It seems like you code against a sub-type of PHPCoord\Point such as PHPCoord\GeographicPoint or 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

201
        /** @scrutinizer ignore-call */ 
202
        $projected = $this->horizontalPoint->transverseMercator($etrs89NationalGrid, new Degree(49), new Degree(-2), new Unity(0.9996012717), new Metre(400000), new Metre(-100000));
Loading history...
202
203 1
        return GeographicPoint::create(
204 1
            $this->horizontalPoint->getLatitude(),
205 1
            $this->horizontalPoint->getLongitude(),
206 1
            $this->verticalPoint->getHeight()->add($geoidHeightCorrectionModelFile->getHeightAdjustment($projected)),
207
            $to,
208 1
            $this->getCoordinateEpoch()
209
        );
210
    }
211
212
    /**
213
     * Geog3D to Geog2D+GravityRelatedHeight.
214
     */
215 2
    public function geographic3DTo2DPlusGravityHeightFromGrid(
216
        Geographic3D $to,
217
        GeographicGeoidHeightGrid $geoidHeightCorrectionModelFile
218
    ): GeographicPoint {
219 2
        return GeographicPoint::create(
220 2
            $this->horizontalPoint->getLatitude(),
221 2
            $this->horizontalPoint->getLongitude(),
222 2
            $this->verticalPoint->getHeight()->add($geoidHeightCorrectionModelFile->getHeightAdjustment($this->horizontalPoint)),
223
            $to,
224 2
            $this->getCoordinateEpoch()
225
        );
226
    }
227
}
228