Passed
Push — master ( 0e0534...3c9843 )
by Doug
02:42
created

CompoundPoint::geographic2DWithHeightOffsets()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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

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

171
        $toLongitude = $this->getHorizontalPoint()->/** @scrutinizer ignore-call */ getLongitude()->add($longitudeOffset);
Loading history...
172 2
        $toHeight = $this->getVerticalPoint()->getHeight()->add($geoidUndulation);
173
174 2
        return GeographicPoint::create($toLatitude, $toLongitude, $toHeight, $to, $this->epoch);
175
    }
176
}
177