dvdoug /
PHPCoord
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * PHPCoord. |
||
| 5 | * |
||
| 6 | * @author Doug Wright |
||
| 7 | */ |
||
| 8 | declare(strict_types=1); |
||
| 9 | |||
| 10 | namespace PHPCoord\Point; |
||
| 11 | |||
| 12 | use DateTimeImmutable; |
||
| 13 | use PHPCoord\CoordinateOperation\CoordinateOperationMethods; |
||
| 14 | use PHPCoord\CoordinateOperation\CoordinateOperations; |
||
|
0 ignored issues
–
show
|
|||
| 15 | use PHPCoord\CoordinateOperation\Grid; |
||
| 16 | use PHPCoord\CoordinateOperation\GridProvider; |
||
| 17 | use PHPCoord\CoordinateReferenceSystem\Compound; |
||
| 18 | use PHPCoord\CoordinateReferenceSystem\CoordinateReferenceSystem; |
||
| 19 | use PHPCoord\CoordinateReferenceSystem\Geocentric; |
||
| 20 | use PHPCoord\CoordinateReferenceSystem\Geographic2D; |
||
| 21 | use PHPCoord\CoordinateReferenceSystem\Geographic3D; |
||
| 22 | use PHPCoord\CoordinateReferenceSystem\Projected; |
||
| 23 | use PHPCoord\CoordinateReferenceSystem\Vertical; |
||
| 24 | use PHPCoord\UnitOfMeasure\Angle\Angle; |
||
| 25 | use PHPCoord\UnitOfMeasure\Length\Length; |
||
| 26 | use PHPCoord\UnitOfMeasure\Scale\Coefficient; |
||
| 27 | use PHPCoord\UnitOfMeasure\Scale\Scale; |
||
| 28 | use PHPCoord\UnitOfMeasure\UnitOfMeasure; |
||
| 29 | use Stringable; |
||
| 30 | |||
| 31 | use function acos; |
||
| 32 | use function asin; |
||
| 33 | use function assert; |
||
| 34 | use function atan; |
||
| 35 | use function atan2; |
||
| 36 | use function class_exists; |
||
| 37 | use function is_string; |
||
| 38 | use function property_exists; |
||
| 39 | use function sscanf; |
||
| 40 | use function str_ends_with; |
||
| 41 | use function str_starts_with; |
||
| 42 | |||
| 43 | abstract class Point implements Stringable |
||
| 44 | { |
||
| 45 | protected const ITERATION_CONVERGENCE_FORMULA = 1e-10; |
||
| 46 | protected const ITERATION_CONVERGENCE_GRID = 0.0001; |
||
| 47 | protected const METHODS_REQUIRING_HORIZONTAL_POINT = [ |
||
| 48 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_AND_SLOPE => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_AND_SLOPE, |
||
| 49 | CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019 => CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019, |
||
| 50 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX, |
||
| 51 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT, |
||
| 52 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT, |
||
| 53 | CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN => CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN, |
||
| 54 | ]; |
||
| 55 | protected const METHODS_THAT_REQUIRE_DIRECTION = [ |
||
| 56 | CoordinateOperationMethods::EPSG_SIMILARITY_TRANSFORMATION => CoordinateOperationMethods::EPSG_SIMILARITY_TRANSFORMATION, |
||
| 57 | CoordinateOperationMethods::EPSG_AFFINE_PARAMETRIC_TRANSFORMATION => CoordinateOperationMethods::EPSG_AFFINE_PARAMETRIC_TRANSFORMATION, |
||
| 58 | CoordinateOperationMethods::EPSG_NADCON5_2D => CoordinateOperationMethods::EPSG_NADCON5_2D, |
||
| 59 | CoordinateOperationMethods::EPSG_NADCON5_3D => CoordinateOperationMethods::EPSG_NADCON5_3D, |
||
| 60 | CoordinateOperationMethods::EPSG_NTV2 => CoordinateOperationMethods::EPSG_NTV2, |
||
| 61 | CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019 => CoordinateOperationMethods::EPSG_ZERO_TIDE_HEIGHT_TO_MEAN_TIDE_HEIGHT_EVRF2019, |
||
| 62 | CoordinateOperationMethods::EPSG_GEOCENTRIC_TRANSLATION_BY_GRID_INTERPOLATION_IGN => CoordinateOperationMethods::EPSG_GEOCENTRIC_TRANSLATION_BY_GRID_INTERPOLATION_IGN, |
||
| 63 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_GTX, |
||
| 64 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_PL_TXT, |
||
| 65 | CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT => CoordinateOperationMethods::EPSG_VERTICAL_OFFSET_BY_GRID_INTERPOLATION_BEV_AT, |
||
| 66 | CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN => CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN, |
||
| 67 | CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_2 => CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_2, |
||
| 68 | CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_6 => CoordinateOperationMethods::EPSG_GENERAL_POLYNOMIAL_OF_DEGREE_6, |
||
| 69 | ]; |
||
| 70 | |||
| 71 | /** |
||
| 72 | * @var array<class-string, Grid> |
||
|
0 ignored issues
–
show
|
|||
| 73 | */ |
||
| 74 | protected static array $gridCache = []; |
||
| 75 | |||
| 76 | /** |
||
| 77 | * @internal |
||
| 78 | * @param array{horizontalPoint?: Point} $additionalParams |
||
| 79 | 373 | */ |
|
| 80 | public function performOperation(string $srid, Compound|Geocentric|Geographic2D|Geographic3D|Projected|Vertical $to, bool $inReverse, array $additionalParams = []): self |
||
| 81 | 373 | { |
|
| 82 | $operation = CoordinateOperations::getOperationData($srid); |
||
| 83 | 373 | ||
| 84 | if ($operation['method'] === CoordinateOperationMethods::EPSG_ALIAS) { |
||
| 85 | $point = clone $this; |
||
| 86 | assert(property_exists($point, 'crs')); |
||
| 87 | $point->crs = $to; |
||
|
0 ignored issues
–
show
|
|||
| 88 | |||
| 89 | return $point; |
||
| 90 | 373 | } else { |
|
| 91 | 373 | $method = CoordinateOperationMethods::getFunctionName($operation['method']); |
|
| 92 | $params = self::resolveParamsByOperation($srid, $operation['method'], $inReverse); |
||
| 93 | 373 | ||
| 94 | 9 | if (isset(self::METHODS_REQUIRING_HORIZONTAL_POINT[$operation['method']]) && isset($additionalParams['horizontalPoint'])) { |
|
| 95 | $params['horizontalPoint'] = $additionalParams['horizontalPoint']; |
||
| 96 | } |
||
| 97 | 373 | ||
| 98 | return $this->$method($to, ...$params); |
||
| 99 | } |
||
| 100 | } |
||
| 101 | |||
| 102 | /** |
||
| 103 | * @return array<string, mixed> |
||
| 104 | 373 | */ |
|
| 105 | protected static function resolveParamsByOperation(string $operationSrid, string $methodSrid, bool $inReverse): array |
||
| 106 | 373 | { |
|
| 107 | $methodData = CoordinateOperationMethods::getMethodData($methodSrid); |
||
| 108 | 373 | ||
| 109 | 373 | $params = []; |
|
| 110 | 373 | $powerCoefficients = []; |
|
| 111 | 373 | foreach (CoordinateOperations::getParamData($operationSrid) as $paramName => $paramValue) { |
|
| 112 | 6 | if (str_ends_with($paramName, 'File') && is_string($paramValue) && class_exists($paramValue) && new $paramValue() instanceof GridProvider) { |
|
| 113 | $params[$paramName] = static::$gridCache[$paramValue] ??= (new $paramValue())->provideGrid(); |
||
| 114 | 373 | } else { |
|
| 115 | 162 | if ($inReverse && isset($methodData['paramData'][$paramName]) && $methodData['paramData'][$paramName]['reverses']) { |
|
| 116 | $paramValue = $paramValue->multiply(-1); |
||
| 117 | 373 | } |
|
| 118 | 54 | if (str_starts_with($paramName, 'Au') || str_starts_with($paramName, 'Bu')) { |
|
| 119 | $powerCoefficients[$paramName] = $paramValue; |
||
| 120 | 373 | } else { |
|
| 121 | $params[$paramName] = $paramValue; |
||
| 122 | } |
||
| 123 | } |
||
| 124 | 373 | } |
|
| 125 | 54 | if ($powerCoefficients) { |
|
| 126 | $params['powerCoefficients'] = $powerCoefficients; |
||
| 127 | 373 | } |
|
| 128 | 39 | if (isset(self::METHODS_THAT_REQUIRE_DIRECTION[$methodSrid])) { |
|
| 129 | if ($methodSrid === CoordinateOperationMethods::EPSG_VERTICAL_CHANGE_BY_GEOID_GRID_DIFFERENCE_NRCAN) { |
||
| 130 | $inReverse = !$inReverse; // has a reversed grid |
||
| 131 | 39 | } |
|
| 132 | $params['inReverse'] = $inReverse; |
||
| 133 | } |
||
| 134 | 373 | ||
| 135 | return $params; |
||
| 136 | } |
||
| 137 | 100 | ||
| 138 | protected static function sign(float $number): int |
||
| 139 | 100 | { |
|
| 140 | if ($number < 0) { |
||
| 141 | return -1; |
||
| 142 | } |
||
| 143 | 100 | ||
| 144 | return 1; |
||
| 145 | } |
||
| 146 | |||
| 147 | /** |
||
| 148 | * General polynomial. |
||
| 149 | * @param array<string, Coefficient> $powerCoefficients |
||
| 150 | * @return array{xt: float, yt: float} |
||
| 151 | 45 | */ |
|
| 152 | protected function generalPolynomialUnitless( |
||
| 153 | float $xs, |
||
| 154 | float $ys, |
||
| 155 | UnitOfMeasure $ordinate1OfEvaluationPointInSourceCRS, |
||
| 156 | UnitOfMeasure $ordinate2OfEvaluationPointInSourceCRS, |
||
| 157 | UnitOfMeasure $ordinate1OfEvaluationPointInTargetCRS, |
||
| 158 | UnitOfMeasure $ordinate2OfEvaluationPointInTargetCRS, |
||
| 159 | Scale $scalingFactorForSourceCRSCoordDifferences, |
||
| 160 | Scale $scalingFactorForTargetCRSCoordDifferences, |
||
| 161 | Scale $A0, |
||
| 162 | Scale $B0, |
||
| 163 | array $powerCoefficients, |
||
| 164 | bool $inReverse |
||
| 165 | 45 | ): array { |
|
| 166 | 18 | if (!$inReverse) { |
|
| 167 | 18 | return $this->generalPolynomialUnitlessForward( |
|
| 168 | 18 | $xs, |
|
| 169 | 18 | $ys, |
|
| 170 | 18 | $ordinate1OfEvaluationPointInSourceCRS, |
|
| 171 | 18 | $ordinate2OfEvaluationPointInSourceCRS, |
|
| 172 | 18 | $ordinate1OfEvaluationPointInTargetCRS, |
|
| 173 | 18 | $ordinate2OfEvaluationPointInTargetCRS, |
|
| 174 | 18 | $scalingFactorForSourceCRSCoordDifferences, |
|
| 175 | 18 | $scalingFactorForTargetCRSCoordDifferences, |
|
| 176 | 18 | $A0, |
|
| 177 | 18 | $B0, |
|
| 178 | 18 | $powerCoefficients, |
|
| 179 | ); |
||
| 180 | 27 | } else { |
|
| 181 | 27 | return $this->generalPolynomialUnitlessReverse( |
|
| 182 | 27 | $xs, |
|
| 183 | 27 | $ys, |
|
| 184 | 27 | $ordinate1OfEvaluationPointInSourceCRS, |
|
| 185 | 27 | $ordinate2OfEvaluationPointInSourceCRS, |
|
| 186 | 27 | $ordinate1OfEvaluationPointInTargetCRS, |
|
| 187 | 27 | $ordinate2OfEvaluationPointInTargetCRS, |
|
| 188 | 27 | $scalingFactorForSourceCRSCoordDifferences, |
|
| 189 | 27 | $scalingFactorForTargetCRSCoordDifferences, |
|
| 190 | 27 | $A0, |
|
| 191 | 27 | $B0, |
|
| 192 | 27 | $powerCoefficients, |
|
| 193 | ); |
||
| 194 | } |
||
| 195 | } |
||
| 196 | 45 | ||
| 197 | protected function generalPolynomialUnitlessForward( |
||
| 198 | float $xs, |
||
| 199 | float $ys, |
||
| 200 | UnitOfMeasure $ordinate1OfEvaluationPointInSourceCRS, |
||
| 201 | UnitOfMeasure $ordinate2OfEvaluationPointInSourceCRS, |
||
| 202 | UnitOfMeasure $ordinate1OfEvaluationPointInTargetCRS, |
||
| 203 | UnitOfMeasure $ordinate2OfEvaluationPointInTargetCRS, |
||
| 204 | Scale $scalingFactorForSourceCRSCoordDifferences, |
||
| 205 | Scale $scalingFactorForTargetCRSCoordDifferences, |
||
| 206 | Scale $A0, |
||
| 207 | Scale $B0, |
||
| 208 | array $powerCoefficients |
||
| 209 | 45 | ): array { |
|
| 210 | 45 | $xso = $ordinate1OfEvaluationPointInSourceCRS->getValue(); |
|
| 211 | 45 | $yso = $ordinate2OfEvaluationPointInSourceCRS->getValue(); |
|
| 212 | 45 | $xto = $ordinate1OfEvaluationPointInTargetCRS->getValue(); |
|
| 213 | $yto = $ordinate2OfEvaluationPointInTargetCRS->getValue(); |
||
| 214 | 45 | ||
| 215 | 45 | $U = $scalingFactorForSourceCRSCoordDifferences->asUnity()->getValue() * ($xs - $xso); |
|
| 216 | $V = $scalingFactorForSourceCRSCoordDifferences->asUnity()->getValue() * ($ys - $yso); |
||
| 217 | 45 | ||
| 218 | 45 | $mTdX = $A0->getValue(); |
|
| 219 | 45 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
|
| 220 | 45 | if ($coefficientName[0] === 'A') { |
|
| 221 | 45 | sscanf($coefficientName, 'Au%dv%d', $uPower, $vPower); |
|
| 222 | $mTdX += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
| 223 | } |
||
| 224 | } |
||
| 225 | 45 | ||
| 226 | 45 | $mTdY = $B0->getValue(); |
|
| 227 | 45 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
|
| 228 | 45 | if ($coefficientName[0] === 'B') { |
|
| 229 | 45 | sscanf($coefficientName, 'Bu%dv%d', $uPower, $vPower); |
|
| 230 | $mTdY += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
| 231 | } |
||
| 232 | } |
||
| 233 | 45 | ||
| 234 | 45 | $xt = $xs - $xso + $xto + $mTdX / $scalingFactorForTargetCRSCoordDifferences->asUnity()->getValue(); |
|
| 235 | $yt = $ys - $yso + $yto + $mTdY / $scalingFactorForTargetCRSCoordDifferences->asUnity()->getValue(); |
||
| 236 | 45 | ||
| 237 | return ['xt' => $xt, 'yt' => $yt]; |
||
| 238 | } |
||
| 239 | 27 | ||
| 240 | protected function generalPolynomialUnitlessReverse( |
||
| 241 | float $xs, |
||
| 242 | float $ys, |
||
| 243 | UnitOfMeasure $ordinate1OfEvaluationPointInSourceCRS, |
||
| 244 | UnitOfMeasure $ordinate2OfEvaluationPointInSourceCRS, |
||
| 245 | UnitOfMeasure $ordinate1OfEvaluationPointInTargetCRS, |
||
| 246 | UnitOfMeasure $ordinate2OfEvaluationPointInTargetCRS, |
||
| 247 | Scale $scalingFactorForSourceCRSCoordDifferences, |
||
| 248 | Scale $scalingFactorForTargetCRSCoordDifferences, |
||
| 249 | Scale $A0, |
||
| 250 | Scale $B0, |
||
| 251 | array $powerCoefficients |
||
| 252 | 27 | ): array { |
|
| 253 | 27 | $result = ['xt' => $xs, 'yt' => $ys]; |
|
| 254 | 27 | for ($i = 0; $i <= 15; ++$i) { |
|
| 255 | 27 | $forwardShiftedCoordinates = $this->generalPolynomialUnitlessForward( |
|
| 256 | 27 | $result['xt'], |
|
| 257 | 27 | $result['yt'], |
|
| 258 | 27 | $ordinate1OfEvaluationPointInSourceCRS, |
|
| 259 | 27 | $ordinate2OfEvaluationPointInSourceCRS, |
|
| 260 | 27 | $ordinate1OfEvaluationPointInTargetCRS, |
|
| 261 | 27 | $ordinate2OfEvaluationPointInTargetCRS, |
|
| 262 | 27 | $scalingFactorForSourceCRSCoordDifferences, |
|
| 263 | 27 | $scalingFactorForTargetCRSCoordDifferences, |
|
| 264 | 27 | $A0, |
|
| 265 | 27 | $B0, |
|
| 266 | 27 | $powerCoefficients |
|
| 267 | 27 | ); |
|
| 268 | 27 | $deltaError = [ |
|
| 269 | 27 | 'xt' => $forwardShiftedCoordinates['xt'] - $xs, |
|
| 270 | 27 | 'yt' => $forwardShiftedCoordinates['yt'] - $ys, |
|
| 271 | 27 | ]; |
|
| 272 | 27 | $result['xt'] -= $deltaError['xt']; |
|
| 273 | $result['yt'] -= $deltaError['yt']; |
||
| 274 | } |
||
| 275 | 27 | ||
| 276 | return $result; |
||
| 277 | } |
||
| 278 | |||
| 279 | /** |
||
| 280 | * Reversible polynomial. |
||
| 281 | * @param array<string, Coefficient> $powerCoefficients |
||
| 282 | * @return array{xt: float, yt: float} |
||
| 283 | 36 | */ |
|
| 284 | protected function reversiblePolynomialUnitless( |
||
| 285 | float $xs, |
||
| 286 | float $ys, |
||
| 287 | Angle $ordinate1OfEvaluationPoint, |
||
| 288 | Angle $ordinate2OfEvaluationPoint, |
||
| 289 | Scale $scalingFactorForCoordDifferences, |
||
| 290 | Scale $A0, |
||
| 291 | Scale $B0, |
||
| 292 | array $powerCoefficients |
||
| 293 | 36 | ): array { |
|
| 294 | 36 | $xo = $ordinate1OfEvaluationPoint->getValue(); |
|
| 295 | $yo = $ordinate2OfEvaluationPoint->getValue(); |
||
| 296 | 36 | ||
| 297 | 36 | $U = $scalingFactorForCoordDifferences->asUnity()->getValue() * ($xs - $xo); |
|
| 298 | $V = $scalingFactorForCoordDifferences->asUnity()->getValue() * ($ys - $yo); |
||
| 299 | 36 | ||
| 300 | 36 | $mTdX = $A0->getValue(); |
|
| 301 | 36 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
|
| 302 | 36 | if ($coefficientName[0] === 'A') { |
|
| 303 | 36 | sscanf($coefficientName, 'Au%dv%d', $uPower, $vPower); |
|
| 304 | $mTdX += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
| 305 | } |
||
| 306 | } |
||
| 307 | 36 | ||
| 308 | 36 | $mTdY = $B0->getValue(); |
|
| 309 | 36 | foreach ($powerCoefficients as $coefficientName => $coefficientValue) { |
|
| 310 | 36 | if ($coefficientName[0] === 'B') { |
|
| 311 | 36 | sscanf($coefficientName, 'Bu%dv%d', $uPower, $vPower); |
|
| 312 | $mTdY += $coefficientValue->getValue() * $U ** $uPower * $V ** $vPower; |
||
| 313 | } |
||
| 314 | } |
||
| 315 | 36 | ||
| 316 | 36 | $xt = $xs + $mTdX * $scalingFactorForCoordDifferences->asUnity()->getValue(); |
|
| 317 | $yt = $ys + $mTdY * $scalingFactorForCoordDifferences->asUnity()->getValue(); |
||
| 318 | 36 | ||
| 319 | return ['xt' => $xt, 'yt' => $yt]; |
||
| 320 | } |
||
| 321 | |||
| 322 | /** |
||
| 323 | * Floating point vagaries mean that it's possible for inputs to be e.g. 1.00000000000001 which makes PHP give a |
||
| 324 | * silent NaN as output so inputs need to be capped. atan/atan2 are not affected, they seem to cap internally. |
||
| 325 | */ |
||
| 326 | protected static function acos(float $num): float |
||
| 327 | { |
||
| 328 | if ($num > 1.0) { |
||
| 329 | $num = 1.0; |
||
| 330 | } elseif ($num < -1) { |
||
| 331 | $num = -1.0; |
||
| 332 | } |
||
| 333 | |||
| 334 | return acos($num); |
||
| 335 | } |
||
| 336 | |||
| 337 | /** |
||
| 338 | * Floating point vagaries mean that it's possible for inputs to be e.g. 1.00000000000001 which makes PHP give a |
||
| 339 | * silent NaN as output so inputs need to be capped. atan/atan2 are not affected, they seem to cap internally. |
||
| 340 | 540 | */ |
|
| 341 | protected static function asin(float $num): float |
||
| 342 | 540 | { |
|
| 343 | if ($num > 1.0) { |
||
| 344 | 540 | $num = 1.0; |
|
| 345 | } elseif ($num < -1.0) { |
||
| 346 | $num = -1.0; |
||
| 347 | } |
||
| 348 | 540 | ||
| 349 | return asin($num); |
||
| 350 | } |
||
| 351 | |||
| 352 | abstract public function getCRS(): CoordinateReferenceSystem; |
||
| 353 | |||
| 354 | abstract public function getCoordinateEpoch(): ?DateTimeImmutable; |
||
| 355 | |||
| 356 | abstract public function calculateDistance(self $to): Length; |
||
| 357 | } |
||
| 358 |
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