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

CompoundPoint::convert()   B

Complexity

Conditions 10
Paths 8

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 10.1626

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 17
c 1
b 0
f 0
nc 8
nop 2
dl 0
loc 31
ccs 15
cts 17
cp 0.8824
crap 10.1626
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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