Passed
Push — 4.x ( 1d49e7...4cde9a )
by Doug
06:44
created

Point::asin()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 9
ccs 4
cts 6
cp 0.6667
crap 3.3332
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 DateTimeImmutable;
12
use PHPCoord\CoordinateOperation\CoordinateOperationMethods;
13
use PHPCoord\CoordinateOperation\CoordinateOperationParams;
0 ignored issues
show
Bug introduced by
The type PHPCoord\CoordinateOpera...ordinateOperationParams 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...
14
use PHPCoord\CoordinateOperation\CoordinateOperations;
0 ignored issues
show
Bug introduced by
The type PHPCoord\CoordinateOperation\CoordinateOperations 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...
15
use PHPCoord\CoordinateReferenceSystem\CoordinateReferenceSystem;
16
use PHPCoord\CoordinateSystem\Axis;
17
use PHPCoord\UnitOfMeasure\Angle\Angle;
18
use PHPCoord\UnitOfMeasure\Length\Length;
19
use PHPCoord\UnitOfMeasure\Scale\Coefficient;
20
use PHPCoord\UnitOfMeasure\Scale\Scale;
21
use PHPCoord\UnitOfMeasure\UnitOfMeasure;
22
use PHPCoord\UnitOfMeasure\UnitOfMeasureFactory;
23
use Stringable;
24
25
abstract class Point implements Stringable
26
{
27
    protected const NEWTON_RAPHSON_CONVERGENCE = 1e-15;
28
29
    /**
30
     * @internal
31
     */
32 8
    public function performOperation(string $srid, CoordinateReferenceSystem $to, bool $inReverse): self
33
    {
34 8
        $operations = [];
35 8
        $operation = CoordinateOperations::getOperationData($srid);
36 8
        if (isset($operation['operations'])) {
37
            foreach ($operation['operations'] as $subOperation) {
38
                $subOperationData = CoordinateOperations::getOperationData($subOperation['operation']);
39
                $subOperationData['source_crs'] = $subOperation['source_crs'];
40
                $subOperationData['target_crs'] = $subOperation['target_crs'];
41
                $operations[$subOperation['operation']] = $subOperationData;
42
            }
43
        } else {
44 8
            $operations[$srid] = $operation;
45
        }
46
47 8
        if ($inReverse) {
48 1
            $operations = array_reverse($operations, true);
49
        }
50
51 8
        $point = $this;
52 8
        foreach ($operations as $operationSrid => $operation) {
53 8
            $method = CoordinateOperationMethods::getFunctionName($operation['method']);
54 8
            if (isset($operation['source_crs']) && $operation['target_crs']) {
55
                $destCRS = CoordinateReferenceSystem::fromSRID($inReverse ? $operation['source_crs'] : $operation['target_crs']);
56
            } else {
57 8
                $destCRS = $to;
58
            }
59
60 8
            $params = [];
61 8
            $powerCoefficients = [];
62 8
            foreach (CoordinateOperationParams::getParamData($operationSrid) as $paramName => $paramData) {
63 8
                $value = $paramData['value'];
64 8
                if ($inReverse && $paramData['reverses']) {
65
                    $value *= -1;
66
                }
67 8
                if ($paramData['uom']) {
68 8
                    $param = UnitOfMeasureFactory::makeUnit($value, $paramData['uom']);
69
                } else {
70
                    $param = $paramData['value'];
71
                }
72 8
                $paramName = static::camelCase($paramName);
73 8
                if (strpos($paramName, 'Au') === 0 || strpos($paramName, 'Bu') === 0) {
74
                    $powerCoefficients[$paramName] = $param;
75
                } else {
76 8
                    $params[$paramName] = $param;
77
                }
78
            }
79 8
            if ($powerCoefficients) {
80
                $params['powerCoefficients'] = $powerCoefficients;
81
            }
82 8
            if (in_array($operation['method'], [
83 8
                CoordinateOperationMethods::EPSG_SIMILARITY_TRANSFORMATION,
84
                CoordinateOperationMethods::EPSG_AFFINE_PARAMETRIC_TRANSFORMATION,
85 8
            ], true)) {
86
                $params['inReverse'] = $inReverse;
87
            }
88
89 8
            if (PHP_MAJOR_VERSION >= 8) {
90
                $point = $point->$method($destCRS, ...$params);
91
            } else {
92 8
                $point = $point->$method($destCRS, ...array_values($params));
93
            }
94
        }
95
96 8
        $point->crs = $to; //some operations are reused across CRSses (e.g. ETRS89 and WGS84), so the $destCRS of the final suboperation might not be the intended target
97
98 8
        return $point;
99
    }
100
101 8
    protected static function camelCase(string $string): string
102
    {
103 8
        $string = str_replace([' ', '-'], '', ucwords($string, ' -'));
104 8
        if (!preg_match('/[ABC][uv\d]/', $string)) {
105 8
            $string = lcfirst($string);
106
        }
107
108 8
        return $string;
109
    }
110
111 195
    protected function getAxisByName(string $name): ?Axis
112
    {
113 195
        foreach ($this->getCRS()->getCoordinateSystem()->getAxes() as $axis) {
114 195
            if ($axis->getName() === $name) {
115 195
                return $axis;
116
            }
117
        }
118
119 135
        return null;
120
    }
121
122 10
    protected static function sign(float $number): int
123
    {
124 10
        if ($number < 0) {
125
            return -1;
126
        }
127
128 10
        return 1;
129
    }
130
131
    /**
132
     * General polynomial.
133
     * @param Coefficient[] $powerCoefficients
134
     */
135 1
    protected function generalPolynomialUnitless(
136
        float $xs,
137
        float $ys,
138
        UnitOfMeasure $ordinate1OfEvaluationPointInSourceCRS,
139
        UnitOfMeasure $ordinate2OfEvaluationPointInSourceCRS,
140
        UnitOfMeasure $ordinate1OfEvaluationPointInTargetCRS,
141
        UnitOfMeasure $ordinate2OfEvaluationPointInTargetCRS,
142
        Scale $scalingFactorForSourceCRSCoordDifferences,
143
        Scale $scalingFactorForTargetCRSCoordDifferences,
144
        Scale $A0,
145
        Scale $B0,
146
        array $powerCoefficients
147
    ): array {
148 1
        $xso = $ordinate1OfEvaluationPointInSourceCRS->getValue();
149 1
        $yso = $ordinate2OfEvaluationPointInSourceCRS->getValue();
150 1
        $xto = $ordinate1OfEvaluationPointInTargetCRS->getValue();
151 1
        $yto = $ordinate2OfEvaluationPointInTargetCRS->getValue();
152
153 1
        $U = $scalingFactorForSourceCRSCoordDifferences->asUnity()->getValue() * ($xs - $xso);
154 1
        $V = $scalingFactorForSourceCRSCoordDifferences->asUnity()->getValue() * ($ys - $yso);
155
156 1
        $mTdX = $A0->getValue();
157 1
        foreach ($powerCoefficients as $coefficientName => $coefficientValue) {
158 1
            if ($coefficientName[0] === 'A') {
159 1
                sscanf($coefficientName, 'Au%dv%d', $uPower, $vPower);
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable $vPower seems to be never defined.
Loading history...
160 1
                $mTdX += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower;
161
            }
162
        }
163
164 1
        $mTdY = $B0->getValue();
165 1
        foreach ($powerCoefficients as $coefficientName => $coefficientValue) {
166 1
            if ($coefficientName[0] === 'B') {
167 1
                sscanf($coefficientName, 'Bu%dv%d', $uPower, $vPower);
168 1
                $mTdY += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower;
169
            }
170
        }
171
172 1
        $xt = $xs - $xso + $xto + $mTdX / $scalingFactorForTargetCRSCoordDifferences->asUnity()->getValue();
173 1
        $yt = $ys - $yso + $yto + $mTdY / $scalingFactorForTargetCRSCoordDifferences->asUnity()->getValue();
174
175 1
        return ['xt' => $xt, 'yt' => $yt];
176
    }
177
178
    /**
179
     * Reversible polynomial.
180
     */
181 2
    protected function reversiblePolynomialUnitless(
182
        float $xs,
183
        float $ys,
184
        Angle $ordinate1OfEvaluationPoint,
185
        Angle $ordinate2OfEvaluationPoint,
186
        Scale $scalingFactorForCoordDifferences,
187
        Scale $A0,
188
        Scale $B0,
189
        array $powerCoefficients
190
    ): array {
191 2
        $xo = $ordinate1OfEvaluationPoint->getValue();
192 2
        $yo = $ordinate2OfEvaluationPoint->getValue();
193
194 2
        $U = $scalingFactorForCoordDifferences->asUnity()->getValue() * ($xs - $xo);
195 2
        $V = $scalingFactorForCoordDifferences->asUnity()->getValue() * ($ys - $yo);
196
197 2
        $mTdX = $A0->getValue();
198 2
        foreach ($powerCoefficients as $coefficientName => $coefficientValue) {
199 2
            if ($coefficientName[0] === 'A') {
200 2
                sscanf($coefficientName, 'Au%dv%d', $uPower, $vPower);
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable $vPower seems to be never defined.
Loading history...
201 2
                $mTdX += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower;
202
            }
203
        }
204
205 2
        $mTdY = $B0->getValue();
206 2
        foreach ($powerCoefficients as $coefficientName => $coefficientValue) {
207 2
            if ($coefficientName[0] === 'B') {
208 2
                sscanf($coefficientName, 'Bu%dv%d', $uPower, $vPower);
209 2
                $mTdY += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower;
210
            }
211
        }
212
213 2
        $xt = $xs + $mTdX * $scalingFactorForCoordDifferences->asUnity()->getValue();
214 2
        $yt = $ys + $mTdY * $scalingFactorForCoordDifferences->asUnity()->getValue();
215
216 2
        return ['xt' => $xt, 'yt' => $yt];
217
    }
218
219
    /**
220
     * Floating point vagaries mean that it's possible for inputs to be e.g. 1.00000000000001 which makes PHP give a
221
     * silent NaN as output so inputs need to be capped. atan/atan2 are not affected, they seem to cap internally.
222
     */
223 1
    protected static function acos(float $num): float
224
    {
225 1
        if ($num > 1.0) {
226
            $num = 1.0;
227 1
        } elseif ($num < -1) {
228
            $num = -1.0;
229
        }
230
231 1
        return acos($num);
232
    }
233
234
    /**
235
     * Floating point vagaries mean that it's possible for inputs to be e.g. 1.00000000000001 which makes PHP give a
236
     * silent NaN as output so inputs need to be capped. atan/atan2 are not affected, they seem to cap internally.
237
     */
238 39
    protected static function asin(float $num): float
239
    {
240 39
        if ($num > 1.0) {
241
            $num = 1.0;
242 39
        } elseif ($num < -1.0) {
243
            $num = -1.0;
244
        }
245
246 39
        return asin($num);
247
    }
248
249
    abstract public function getCRS(): CoordinateReferenceSystem;
250
251
    abstract public function getCoordinateEpoch(): ?DateTimeImmutable;
252
253
    abstract public function calculateDistance(self $to): Length;
254
}
255