Total Complexity | 40 |
Total Lines | 314 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Point often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Point, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
42 | abstract class Point implements Stringable |
||
43 | { |
||
44 | protected const ITERATION_CONVERGENCE_FORMULA = 1e-10; |
||
45 | protected const ITERATION_CONVERGENCE_GRID = 0.0001; |
||
46 | protected const METHODS_REQUIRING_HORIZONTAL_POINT = [ |
||
47 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_AND_SLOPE => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_AND_SLOPE, |
||
48 | CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019 => CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019, |
||
49 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX, |
||
50 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT, |
||
51 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT, |
||
52 | CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN => CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN, |
||
53 | ]; |
||
54 | protected const METHODS_THAT_REQUIRE_DIRECTION = [ |
||
55 | CoordinateOperationMethods::EPSG_SIMILARITY_TRANSFORMATION => CoordinateOperationMethods::EPSG_SIMILARITY_TRANSFORMATION, |
||
56 | CoordinateOperationMethods::EPSG_AFFINE_PARAMETRIC_TRANSFORMATION => CoordinateOperationMethods::EPSG_AFFINE_PARAMETRIC_TRANSFORMATION, |
||
57 | CoordinateOperationMethods::EPSG_NADCON5_2D => CoordinateOperationMethods::EPSG_NADCON5_2D, |
||
58 | CoordinateOperationMethods::EPSG_NADCON5_3D => CoordinateOperationMethods::EPSG_NADCON5_3D, |
||
59 | CoordinateOperationMethods::EPSG_NTV2 => CoordinateOperationMethods::EPSG_NTV2, |
||
60 | CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019 => CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019, |
||
61 | CoordinateOperationMethods::EPSG_GEOCENTRIC_TRANSLATION_BY_GRID_INTERPOLATION_IGN => CoordinateOperationMethods::EPSG_GEOCENTRIC_TRANSLATION_BY_GRID_INTERPOLATION_IGN, |
||
62 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX, |
||
63 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT, |
||
64 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT, |
||
65 | CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN => CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN, |
||
66 | CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_2 => CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_2, |
||
67 | CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_6 => CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_6, |
||
68 | ]; |
||
69 | |||
70 | /** |
||
71 | * @var array<class-string, Grid> |
||
72 | */ |
||
73 | protected static array $gridCache = []; |
||
74 | |||
75 | /** |
||
76 | * @internal |
||
77 | * @param array{horizontalPoint?: Point} $additionalParams |
||
78 | */ |
||
79 | public function performOperation(string $srid, Compound|Geocentric|Geographic2D|Geographic3D|Projected|Vertical $to, bool $inReverse, array $additionalParams = []): self |
||
80 | { |
||
81 | $operation = CoordinateOperations::getOperationData($srid); |
||
82 | |||
83 | if ($operation['method'] === CoordinateOperationMethods::EPSG_ALIAS) { |
||
84 | $point = clone $this; |
||
85 | assert(property_exists($point, 'crs')); |
||
86 | $point->crs = $to; |
||
87 | |||
88 | return $point; |
||
89 | } else { |
||
90 | $method = CoordinateOperationMethods::getFunctionName($operation['method']); |
||
91 | $params = self::resolveParamsByOperation($srid, $operation['method'], $inReverse); |
||
92 | |||
93 | if (isset(self::METHODS_REQUIRING_HORIZONTAL_POINT[$operation['method']]) && isset($additionalParams['horizontalPoint'])) { |
||
94 | $params['horizontalPoint'] = $additionalParams['horizontalPoint']; |
||
95 | } |
||
96 | |||
97 | return $this->$method($to, ...$params); |
||
98 | } |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * @return array<string, mixed> |
||
103 | */ |
||
104 | protected static function resolveParamsByOperation(string $operationSrid, string $methodSrid, bool $inReverse): array |
||
105 | { |
||
106 | $methodData = CoordinateOperationMethods::getMethodData($methodSrid); |
||
107 | |||
108 | $params = []; |
||
109 | $powerCoefficients = []; |
||
110 | foreach (CoordinateOperations::getParamData($operationSrid) as $paramName => $paramValue) { |
||
111 | if (str_ends_with($paramName, 'File') && is_string($paramValue) && class_exists($paramValue) && new $paramValue() instanceof GridProvider) { |
||
112 | $params[$paramName] = static::$gridCache[$paramValue] ??= (new $paramValue())->provideGrid(); |
||
113 | } else { |
||
114 | if ($inReverse && isset($methodData['paramData'][$paramName]) && $methodData['paramData'][$paramName]['reverses']) { |
||
115 | $paramValue = $paramValue->multiply(-1); |
||
116 | } |
||
117 | if (str_starts_with($paramName, 'Au') || str_starts_with($paramName, 'Bu')) { |
||
118 | $powerCoefficients[$paramName] = $paramValue; |
||
119 | } else { |
||
120 | $params[$paramName] = $paramValue; |
||
121 | } |
||
122 | } |
||
123 | } |
||
124 | if ($powerCoefficients) { |
||
125 | $params['powerCoefficients'] = $powerCoefficients; |
||
126 | } |
||
127 | if (isset(self::METHODS_THAT_REQUIRE_DIRECTION[$methodSrid])) { |
||
128 | if ($methodSrid === CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN) { |
||
129 | $inReverse = !$inReverse; // has a reversed grid |
||
130 | } |
||
131 | $params['inReverse'] = $inReverse; |
||
132 | } |
||
133 | |||
134 | return $params; |
||
135 | } |
||
136 | |||
137 | protected static function sign(float $number): int |
||
138 | { |
||
139 | if ($number < 0) { |
||
140 | return -1; |
||
141 | } |
||
142 | |||
143 | return 1; |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * General polynomial. |
||
148 | * @param array<string, Coefficient> $powerCoefficients |
||
149 | * @return array{xt: float, yt: float} |
||
150 | */ |
||
151 | protected function generalPolynomialUnitless( |
||
152 | float $xs, |
||
153 | float $ys, |
||
154 | UnitOfMeasure $ordinate1OfEvaluationPointInSourceCRS, |
||
155 | UnitOfMeasure $ordinate2OfEvaluationPointInSourceCRS, |
||
156 | UnitOfMeasure $ordinate1OfEvaluationPointInTargetCRS, |
||
157 | UnitOfMeasure $ordinate2OfEvaluationPointInTargetCRS, |
||
158 | Scale $scalingFactorForSourceCRSCoordDifferences, |
||
159 | Scale $scalingFactorForTargetCRSCoordDifferences, |
||
160 | Scale $A0, |
||
161 | Scale $B0, |
||
162 | array $powerCoefficients, |
||
163 | bool $inReverse |
||
164 | ): array { |
||
165 | if (!$inReverse) { |
||
166 | return $this->generalPolynomialUnitlessForward( |
||
167 | $xs, |
||
168 | $ys, |
||
169 | $ordinate1OfEvaluationPointInSourceCRS, |
||
170 | $ordinate2OfEvaluationPointInSourceCRS, |
||
171 | $ordinate1OfEvaluationPointInTargetCRS, |
||
172 | $ordinate2OfEvaluationPointInTargetCRS, |
||
173 | $scalingFactorForSourceCRSCoordDifferences, |
||
174 | $scalingFactorForTargetCRSCoordDifferences, |
||
175 | $A0, |
||
176 | $B0, |
||
177 | $powerCoefficients, |
||
178 | ); |
||
179 | } else { |
||
180 | return $this->generalPolynomialUnitlessReverse( |
||
181 | $xs, |
||
182 | $ys, |
||
183 | $ordinate1OfEvaluationPointInSourceCRS, |
||
184 | $ordinate2OfEvaluationPointInSourceCRS, |
||
185 | $ordinate1OfEvaluationPointInTargetCRS, |
||
186 | $ordinate2OfEvaluationPointInTargetCRS, |
||
187 | $scalingFactorForSourceCRSCoordDifferences, |
||
188 | $scalingFactorForTargetCRSCoordDifferences, |
||
189 | $A0, |
||
190 | $B0, |
||
191 | $powerCoefficients, |
||
192 | ); |
||
193 | } |
||
194 | } |
||
195 | |||
196 | protected function generalPolynomialUnitlessForward( |
||
197 | float $xs, |
||
198 | float $ys, |
||
199 | UnitOfMeasure $ordinate1OfEvaluationPointInSourceCRS, |
||
200 | UnitOfMeasure $ordinate2OfEvaluationPointInSourceCRS, |
||
201 | UnitOfMeasure $ordinate1OfEvaluationPointInTargetCRS, |
||
202 | UnitOfMeasure $ordinate2OfEvaluationPointInTargetCRS, |
||
203 | Scale $scalingFactorForSourceCRSCoordDifferences, |
||
204 | Scale $scalingFactorForTargetCRSCoordDifferences, |
||
205 | Scale $A0, |
||
206 | Scale $B0, |
||
207 | array $powerCoefficients |
||
208 | ): array { |
||
209 | $xso = $ordinate1OfEvaluationPointInSourceCRS->getValue(); |
||
210 | $yso = $ordinate2OfEvaluationPointInSourceCRS->getValue(); |
||
211 | $xto = $ordinate1OfEvaluationPointInTargetCRS->getValue(); |
||
212 | $yto = $ordinate2OfEvaluationPointInTargetCRS->getValue(); |
||
213 | |||
214 | $U = $scalingFactorForSourceCRSCoordDifferences->asUnity()->getValue() * ($xs - $xso); |
||
215 | $V = $scalingFactorForSourceCRSCoordDifferences->asUnity()->getValue() * ($ys - $yso); |
||
216 | |||
217 | $mTdX = $A0->getValue(); |
||
218 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
||
219 | if ($coefficientName[0] === 'A') { |
||
220 | sscanf($coefficientName, 'Au%dv%d', $uPower, $vPower); |
||
221 | $mTdX += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
222 | } |
||
223 | } |
||
224 | |||
225 | $mTdY = $B0->getValue(); |
||
226 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
||
227 | if ($coefficientName[0] === 'B') { |
||
228 | sscanf($coefficientName, 'Bu%dv%d', $uPower, $vPower); |
||
229 | $mTdY += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
230 | } |
||
231 | } |
||
232 | |||
233 | $xt = $xs - $xso + $xto + $mTdX / $scalingFactorForTargetCRSCoordDifferences->asUnity()->getValue(); |
||
234 | $yt = $ys - $yso + $yto + $mTdY / $scalingFactorForTargetCRSCoordDifferences->asUnity()->getValue(); |
||
235 | |||
236 | return ['xt' => $xt, 'yt' => $yt]; |
||
237 | } |
||
238 | |||
239 | protected function generalPolynomialUnitlessReverse( |
||
240 | float $xs, |
||
241 | float $ys, |
||
242 | UnitOfMeasure $ordinate1OfEvaluationPointInSourceCRS, |
||
243 | UnitOfMeasure $ordinate2OfEvaluationPointInSourceCRS, |
||
244 | UnitOfMeasure $ordinate1OfEvaluationPointInTargetCRS, |
||
245 | UnitOfMeasure $ordinate2OfEvaluationPointInTargetCRS, |
||
246 | Scale $scalingFactorForSourceCRSCoordDifferences, |
||
247 | Scale $scalingFactorForTargetCRSCoordDifferences, |
||
248 | Scale $A0, |
||
249 | Scale $B0, |
||
250 | array $powerCoefficients |
||
251 | ): array { |
||
252 | $result = ['xt' => $xs, 'yt' => $ys]; |
||
253 | for ($i = 0; $i <= 15; ++$i) { |
||
254 | $forwardShiftedCoordinates = $this->generalPolynomialUnitlessForward( |
||
255 | $result['xt'], |
||
256 | $result['yt'], |
||
257 | $ordinate1OfEvaluationPointInSourceCRS, |
||
258 | $ordinate2OfEvaluationPointInSourceCRS, |
||
259 | $ordinate1OfEvaluationPointInTargetCRS, |
||
260 | $ordinate2OfEvaluationPointInTargetCRS, |
||
261 | $scalingFactorForSourceCRSCoordDifferences, |
||
262 | $scalingFactorForTargetCRSCoordDifferences, |
||
263 | $A0, |
||
264 | $B0, |
||
265 | $powerCoefficients |
||
266 | ); |
||
267 | $deltaError = [ |
||
268 | 'xt' => $forwardShiftedCoordinates['xt'] - $xs, |
||
269 | 'yt' => $forwardShiftedCoordinates['yt'] - $ys, |
||
270 | ]; |
||
271 | $result['xt'] -= $deltaError['xt']; |
||
272 | $result['yt'] -= $deltaError['yt']; |
||
273 | } |
||
274 | |||
275 | return $result; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Reversible polynomial. |
||
280 | * @param array<string, Coefficient> $powerCoefficients |
||
281 | * @return array{xt: float, yt: float} |
||
282 | */ |
||
283 | protected function reversiblePolynomialUnitless( |
||
284 | float $xs, |
||
285 | float $ys, |
||
286 | Angle $ordinate1OfEvaluationPoint, |
||
287 | Angle $ordinate2OfEvaluationPoint, |
||
288 | Scale $scalingFactorForCoordDifferences, |
||
289 | Scale $A0, |
||
290 | Scale $B0, |
||
291 | array $powerCoefficients |
||
292 | ): array { |
||
293 | $xo = $ordinate1OfEvaluationPoint->getValue(); |
||
294 | $yo = $ordinate2OfEvaluationPoint->getValue(); |
||
295 | |||
296 | $U = $scalingFactorForCoordDifferences->asUnity()->getValue() * ($xs - $xo); |
||
297 | $V = $scalingFactorForCoordDifferences->asUnity()->getValue() * ($ys - $yo); |
||
298 | |||
299 | $mTdX = $A0->getValue(); |
||
300 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
||
301 | if ($coefficientName[0] === 'A') { |
||
302 | sscanf($coefficientName, 'Au%dv%d', $uPower, $vPower); |
||
303 | $mTdX += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
304 | } |
||
305 | } |
||
306 | |||
307 | $mTdY = $B0->getValue(); |
||
308 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
||
309 | if ($coefficientName[0] === 'B') { |
||
310 | sscanf($coefficientName, 'Bu%dv%d', $uPower, $vPower); |
||
311 | $mTdY += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
312 | } |
||
313 | } |
||
314 | |||
315 | $xt = $xs + $mTdX * $scalingFactorForCoordDifferences->asUnity()->getValue(); |
||
316 | $yt = $ys + $mTdY * $scalingFactorForCoordDifferences->asUnity()->getValue(); |
||
317 | |||
318 | return ['xt' => $xt, 'yt' => $yt]; |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Floating point vagaries mean that it's possible for inputs to be e.g. 1.00000000000001 which makes PHP give a |
||
323 | * silent NaN as output so inputs need to be capped. atan/atan2 are not affected, they seem to cap internally. |
||
324 | */ |
||
325 | protected static function acos(float $num): float |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * Floating point vagaries mean that it's possible for inputs to be e.g. 1.00000000000001 which makes PHP give a |
||
338 | * silent NaN as output so inputs need to be capped. atan/atan2 are not affected, they seem to cap internally. |
||
339 | */ |
||
340 | protected static function asin(float $num): float |
||
349 | } |
||
350 | |||
351 | abstract public function getCRS(): CoordinateReferenceSystem; |
||
352 | |||
353 | abstract public function getCoordinateEpoch(): ?DateTimeImmutable; |
||
354 | |||
355 | abstract public function calculateDistance(self $to): Length; |
||
356 | } |
||
357 |
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths