Passed
Push — master ( fd93b5...48e916 )
by Doug
40:26 queued 29:39
created

BiquadraticInterpolation::interpolateQuadratic()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
nc 1
nop 4
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 2
rs 10
c 1
b 0
f 0
1
<?php
2
/**
3
 * PHPCoord.
4
 *
5
 * @author Doug Wright
6
 */
7
declare(strict_types=1);
8
9
namespace PHPCoord\CoordinateOperation;
10
11
use function assert;
12
use function count;
13
use function max;
14
use function min;
15
16
trait BiquadraticInterpolation
17
{
18
    protected float $startX;
19
    protected float $endX;
20
    protected float $startY;
21
    protected float $endY;
22
    protected int $numberOfColumns;
23
    protected int $numberOfRows;
24
    protected float $columnGridInterval;
25
    protected float $rowGridInterval;
26
27 7
    public function interpolate(
28
        float $x,
29
        float $y
30
    ): array {
31 7
        $corners = $this->getCorners($x, $y);
32
33 7
        $dx = $corners['lowerRight']->getX() === $corners['lowerLeft']->getX() ? 0 : (($x - $corners['lowerLeft']->getX()) / $this->columnGridInterval);
34 7
        $dy = $corners['upperLeft']->getY() === $corners['lowerLeft']->getY() ? 0 : (($y - $corners['lowerLeft']->getY()) / $this->rowGridInterval);
35
36 7
        $interpolations = [];
37 7
        for ($i = 0, $count = count($corners['lowerLeft']->getValues()); $i < $count; ++$i) {
38
            //Interpolate value at lower row
39 7
            $y0 = $this->interpolateQuadratic($dx, $corners['lowerLeft']->getValues()[$i], $corners['lowerCentre']->getValues()[$i], $corners['lowerRight']->getValues()[$i]);
40
            //Interpolate value at middle row
41 7
            $y1 = $this->interpolateQuadratic($dx, $corners['middleLeft']->getValues()[$i], $corners['middleCentre']->getValues()[$i], $corners['middleRight']->getValues()[$i]);
42
            //Interpolate value at upper row
43 7
            $y2 = $this->interpolateQuadratic($dx, $corners['upperLeft']->getValues()[$i], $corners['upperCentre']->getValues()[$i], $corners['upperRight']->getValues()[$i]);
44
45
            //Interpolate between rows
46 7
            $xy = $this->interpolateQuadratic($dy, $y0, $y1, $y2);
47 7
            $interpolations[] = $xy;
48
        }
49
50 7
        return $interpolations;
51
    }
52
53
    /**
54
     * Quadratic interpolation at point p, where p is between 0 and 2.
55
     * Converted from NOAA FORTRAN implementation, this function apparently uses Newton-Gregory forward polynomial.
56
     */
57 7
    private function interpolateQuadratic(float $p, float $valueAt0, float $valueAt1, float $valueAt2): float
58
    {
59 7
        assert($p >= 0 && $p <= 2);
60
61 7
        $difference1 = $valueAt1 - $valueAt0;
62 7
        $difference2 = $valueAt2 - $valueAt1;
63 7
        $differenceDifferences = $difference2 - $difference1;
64
65 7
        return $valueAt0 + $p * $difference1 + $p / 2 * ($p - 1) * $differenceDifferences;
66
    }
67
68
    /**
69
     * @return GridValues[]
70
     */
71 7
    private function getCorners(float $x, float $y): array
72
    {
73
        // Method:
74
        // Fit a 3x3 window over the random point. The closest
75
        // 2x2 points will surround the point. But based on which
76
        // quadrant of that 2x2 cell in which the point falls, the
77
        // 3x3 window could extend NW, NE, SW or SE from the 2x2 cell.
78
79
        // Find which column should be LEAST when fitting
80
        // a 3x3 window around $longitude.
81 7
        $xIndex = (int) (($x - $this->startX) / $this->columnGridInterval - 0.5);
82
83
        // Find which row should be LEAST when fitting
84
        // a 3x3 window around $latitude.
85 7
        $yIndex = (int) (($y - $this->startY) / $this->rowGridInterval - 0.5);
86
87 7
        $xIndex = min(max($xIndex, 0), $this->numberOfColumns - 3);
88 7
        $yIndex = min(max($yIndex, 0), $this->numberOfRows - 3);
89
90
        return [
91 7
            'lowerLeft' => $this->getRecord($xIndex, $yIndex),
0 ignored issues
show
Bug introduced by
It seems like getRecord() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

91
            'lowerLeft' => $this->/** @scrutinizer ignore-call */ getRecord($xIndex, $yIndex),
Loading history...
92 7
            'lowerCentre' => $this->getRecord($xIndex + 1, $yIndex),
93 7
            'lowerRight' => $this->getRecord($xIndex + 2, $yIndex),
94 7
            'middleLeft' => $this->getRecord($xIndex, $yIndex + 1),
95 7
            'middleCentre' => $this->getRecord($xIndex + 1, $yIndex + 1),
96 7
            'middleRight' => $this->getRecord($xIndex + 2, $yIndex + 1),
97 7
            'upperLeft' => $this->getRecord($xIndex, $yIndex + 2),
98 7
            'upperCentre' => $this->getRecord($xIndex + 1, $yIndex + 2),
99 7
            'upperRight' => $this->getRecord($xIndex + 2, $yIndex + 2),
100
        ];
101
    }
102
}
103