Passed
Push — master ( 0baa7a...f13449 )
by Doug
50:51
created

BYNHeightGrid::__construct()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 36
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 6.1169

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 26
nc 6
nop 1
dl 0
loc 36
ccs 23
cts 27
cp 0.8519
crap 6.1169
rs 8.8817
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 PHPCoord\UnitOfMeasure\Angle\ArcSecond;
12
use PHPCoord\UnitOfMeasure\Length\Metre;
13
14
use function assert;
15
use function in_array;
16
use function is_int;
17
18
class BYNHeightGrid extends GeographicGeoidHeightGrid
19
{
20
    use BiquadraticInterpolation;
21
22
    private string $shortFormatChar = 'v';
23
    private string $longFormatChar = 'V';
24
    private string $doubleFormatChar = 'e';
25
    private string $floatFormatChar = 'g';
26
    private int $dataSize;
27
    private float $conversionFactor;
28
29 4
    public function __construct(string $filename)
30
    {
31 4
        $this->storageOrder = self::STORAGE_ORDER_INCREASING_LONGITUDE_DECREASING_LATIITUDE;
32 4
        $this->gridFile = new GridFile($filename);
33
34 4
        $this->gridFile->fseek(0);
35 4
        $rawData = $this->gridFile->fread(80);
36 4
        if ($this->unpack('vByteOrder', $rawData, 50)['ByteOrder'] === 1) {
37
            $this->shortFormatChar = 'n';
38
            $this->longFormatChar = 'N';
39
            $this->doubleFormatChar = 'E';
40
            $this->floatFormatChar = 'G';
41
        }
42
43 4
        $data = $this->unpack("{$this->longFormatChar}SOUTH/{$this->longFormatChar}NORTH/{$this->longFormatChar}WEST/{$this->longFormatChar}EAST/{$this->shortFormatChar}DLAT/{$this->shortFormatChar}DLON/{$this->shortFormatChar}GLOBAL/{$this->shortFormatChar}TYPE/{$this->doubleFormatChar}FACTOR/{$this->shortFormatChar}SIZEOF/x4/{$this->shortFormatChar}DATA/{$this->shortFormatChar}SUBTYPE/{$this->shortFormatChar}DATUM/{$this->shortFormatChar}ELLIPSOID/{$this->shortFormatChar}BYTEORDER/{$this->shortFormatChar}SCALE/{$this->doubleFormatChar}WO/{$this->doubleFormatChar}GM/{$this->shortFormatChar}TIDESYSTEM/{$this->shortFormatChar}REFREALISATION/{$this->floatFormatChar}EPOCH/{$this->shortFormatChar}PTTYPE/x2", $rawData);
44
45 4
        foreach ($data as $key => $value) {
46 4
            if (in_array($key, ['SOUTH', 'EAST', 'NORTH', 'WEST']) && $value > 2147483647) {
47 4
                $data[$key] -= 4294967295; // @phpstan-ignore assignOp.invalid
48
            }
49
        }
50
51 4
        assert($data['DATA'] === 0); // actual values, not errors
52 4
        assert($data['TYPE'] === 1); // ellipsoid separation
53 4
        assert($data['SIZEOF'] === 2 || $data['SIZEOF'] === 4); // sensible data type
54
55 4
        $this->startX = (new ArcSecond($data['WEST']))->asDegrees()->getValue();
56 4
        $this->startY = (new ArcSecond($data['SOUTH']))->asDegrees()->getValue();
57 4
        $this->endX = (new ArcSecond($data['EAST']))->asDegrees()->getValue();
58 4
        $this->endY = (new ArcSecond($data['NORTH']))->asDegrees()->getValue();
59 4
        $this->columnGridInterval = (new ArcSecond($data['DLON']))->asDegrees()->getValue();
60 4
        $this->rowGridInterval = (new ArcSecond($data['DLAT']))->asDegrees()->getValue();
61 4
        $this->numberOfColumns = (int) (string) (($this->endX - $this->startX) / $this->columnGridInterval) + 1;
62 4
        $this->numberOfRows = (int) (string) (($this->endY - $this->startY) / $this->rowGridInterval) + 1;
63 4
        $this->dataSize = $data['SIZEOF'];
64 4
        $this->conversionFactor = $data['FACTOR'];
65
    }
66
67
    /**
68
     * @return Metre[]
69
     */
70 4
    public function getValues(float $x, float $y): array
71
    {
72 4
        $shift = $this->interpolate($x, $y)[0];
73
74 4
        return [new Metre($shift)];
75
    }
76
77 4
    protected function getRecord(int $longitudeIndex, int $latitudeIndex): GridValues
78
    {
79 4
        $recordId = ($this->numberOfRows - $latitudeIndex - 1) * $this->numberOfColumns + $longitudeIndex;
80
81 4
        $offset = 80 + $recordId * $this->dataSize;
82 4
        $this->gridFile->fseek($offset);
83 4
        $rawRow = $this->gridFile->fread($this->dataSize);
84 4
        $dataType = $this->dataSize === 2 ? $this->shortFormatChar : $this->longFormatChar;
85 4
        $shift = $this->unpack("{$dataType}shift", $rawRow)['shift'];
86 4
        assert(is_int($shift));
87 4
        if ($this->dataSize === 4 && $shift > 2147483647) {
88 3
            $shift -= 4294967295;
89 1
        } elseif ($this->dataSize === 2 && $shift > 32767) {
90
            $shift -= 65535;
91
        }
92 4
        $shift /= $this->conversionFactor;
93
94 4
        return new GridValues(
95 4
            $longitudeIndex * $this->columnGridInterval + $this->startX,
96 4
            $latitudeIndex * $this->rowGridInterval + $this->startY,
97 4
            [$shift]
98 4
        );
99
    }
100
}
101