Passed
Push — 4.x ( 2e43a7...18aaa8 )
by Doug
06:37
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\OSTNOSGM15Grid;
17
use PHPCoord\CoordinateReferenceSystem\Compound;
18
use PHPCoord\CoordinateReferenceSystem\CoordinateReferenceSystem;
19
use PHPCoord\CoordinateReferenceSystem\Geographic2D;
20
use PHPCoord\CoordinateReferenceSystem\Geographic3D;
21
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...
22
use PHPCoord\CoordinateReferenceSystem\Vertical;
23
use PHPCoord\CoordinateSystem\Cartesian;
24
use PHPCoord\Datum\Datum;
25
use PHPCoord\Exception\InvalidCoordinateReferenceSystemException;
26
use PHPCoord\Exception\UnknownConversionException;
27
use PHPCoord\UnitOfMeasure\Angle\Angle;
28
use PHPCoord\UnitOfMeasure\Angle\Degree;
29
use PHPCoord\UnitOfMeasure\Length\Length;
30
use PHPCoord\UnitOfMeasure\Length\Metre;
31
use PHPCoord\UnitOfMeasure\Scale\Unity;
32
33
/**
34
 * Coordinate representing a point expressed in 2 different CRSs (2D horizontal + 1D Vertical).
35
 */
36
class CompoundPoint extends Point implements ConvertiblePoint
37
{
38
    use AutoConversion {
39
        convert as protected autoConvert;
40
    }
41
42
    /**
43
     * Horizontal point.
44
     * @var GeographicPoint|ProjectedPoint
45
     */
46
    protected Point $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
    /**
64
     * Constructor.
65
     * @param GeographicPoint|ProjectedPoint $horizontalPoint
66
     */
67 74
    protected function __construct(Point $horizontalPoint, VerticalPoint $verticalPoint, Compound $crs, ?DateTimeInterface $epoch = null)
68
    {
69 74
        $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...
70 74
        $this->verticalPoint = $verticalPoint;
71 74
        $this->crs = $crs;
72
73 74
        if ($epoch instanceof DateTime) {
74 9
            $epoch = DateTimeImmutable::createFromMutable($epoch);
75
        }
76 74
        $this->epoch = $epoch;
77
    }
78
79
    /**
80
     * @param GeographicPoint|ProjectedPoint $horizontalPoint
81
     */
82 74
    public static function create(Point $horizontalPoint, VerticalPoint $verticalPoint, Compound $crs, ?DateTimeInterface $epoch = null)
83
    {
84 74
        return new static($horizontalPoint, $verticalPoint, $crs, $epoch);
85
    }
86
87 64
    public function getHorizontalPoint(): Point
88
    {
89 64
        return $this->horizontalPoint;
90
    }
91
92 55
    public function getVerticalPoint(): VerticalPoint
93
    {
94 55
        return $this->verticalPoint;
95
    }
96
97 72
    public function getCRS(): Compound
98
    {
99 72
        return $this->crs;
100
    }
101
102 28
    public function getCoordinateEpoch(): ?DateTimeImmutable
103
    {
104 28
        return $this->epoch;
105
    }
106
107
    /**
108
     * Calculate distance between two points.
109
     */
110 18
    public function calculateDistance(Point $to): Length
111
    {
112
        try {
113 18
            if ($to instanceof ConvertiblePoint) {
114 18
                $to = $to->convert($this->crs);
115
            }
116
        } finally {
117 18
            if ($to->getCRS()->getSRID() !== $this->crs->getSRID()) {
118 9
                throw new InvalidCoordinateReferenceSystemException('Can only calculate distances between two points in the same CRS');
119
            }
120
121
            /* @var CompoundPoint $to */
122 9
            return $this->horizontalPoint->calculateDistance($to->horizontalPoint);
123
        }
124
    }
125
126 45
    public function convert(CoordinateReferenceSystem $to, bool $ignoreBoundaryRestrictions = false): Point
127
    {
128
        try {
129 45
            return $this->autoConvert($to, $ignoreBoundaryRestrictions);
130 18
        } catch (UnknownConversionException $e) {
131 18
            if ($this->getHorizontalPoint() instanceof ConvertiblePoint) {
132
                // if 2D target, try again with just the horizontal component
133 18
                if (($to instanceof Geographic2D || $to instanceof Projected)) {
134
                    return $this->getHorizontalPoint()->convert($to, $ignoreBoundaryRestrictions);
135
                }
136
137
                // try separate horizontal + vertical conversions and stitch results together
138 18
                if ($to instanceof Compound) {
139 18
                    $newHorizontalPoint = $this->getHorizontalPoint()->convert($to->getHorizontal());
140
141 18
                    if ($this->getCRS()->getVertical()->getSRID() !== $to->getVertical()->getSRID()) {
142 18
                        $path = $this->findOperationPath($this->getCRS()->getVertical(), $to->getVertical(), $ignoreBoundaryRestrictions);
143
144 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...
145 9
                            $newVerticalPoint = $this->getVerticalPoint();
146 9
                            foreach ($path as $step) {
147 9
                                $target = CoordinateReferenceSystem::fromSRID($step['in_reverse'] ? $step['source_crs'] : $step['target_crs']);
148 9
                                $newVerticalPoint = $newVerticalPoint->performOperation($step['operation'], $target, $step['in_reverse'], ['horizontalPoint' => $newHorizontalPoint]);
149
                            }
150
151 9
                            return static::create($newHorizontalPoint, $newVerticalPoint, $to, $this->epoch);
152
                        }
153
                    }
154
                }
155
            }
156
            throw $e;
157
        }
158
    }
159
160 27
    public function __toString(): string
161
    {
162 27
        return "({$this->horizontalPoint}, {$this->verticalPoint})";
163
    }
164
165
    /**
166
     * Geographic2D with Height Offsets.
167
     * This transformation allows calculation of coordinates in the target system by adding the parameter value to the
168
     * coordinate values of the point in the source system.
169
     */
170 18
    public function geographic2DWithHeightOffsets(
171
        Geographic3D $to,
172
        Angle $latitudeOffset,
173
        Angle $longitudeOffset,
174
        Length $geoidUndulation
175
    ): GeographicPoint {
176 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

176
        $toLatitude = $this->getHorizontalPoint()->/** @scrutinizer ignore-call */ getLatitude()->add($latitudeOffset);
Loading history...
177 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

177
        $toLongitude = $this->getHorizontalPoint()->/** @scrutinizer ignore-call */ getLongitude()->add($longitudeOffset);
Loading history...
178 18
        $toHeight = $this->getVerticalPoint()->getHeight()->add($geoidUndulation);
179
180 18
        return GeographicPoint::create($toLatitude, $toLongitude, $toHeight, $to, $this->epoch);
181
    }
182
183
    /**
184
     * Geog3D to Geog2D+GravityRelatedHeight (OSGM-GB).
185
     * Uses ETRS89 / National Grid as an intermediate coordinate system for bi-linear interpolation of gridded grid
186
     * coordinate differences.
187
     */
188 1
    public function geographic3DTo2DPlusGravityHeightOSGM15(
189
        Geographic3D $to,
190
        OSTNOSGM15Grid $geoidHeightCorrectionModelFile,
191
        string $EPSGCodeForInterpolationCRS
0 ignored issues
show
Unused Code introduced by
The parameter $EPSGCodeForInterpolationCRS is not used and could be removed. ( Ignorable by Annotation )

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

191
        /** @scrutinizer ignore-unused */ string $EPSGCodeForInterpolationCRS

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 1
        );
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->getVerticalAdjustment($projected)),
207 1
            $to,
208 1
            $this->getCoordinateEpoch()
209 1
        );
210
    }
211
}
212