Passed
Push — master ( bbeaaa...9ed6c0 )
by Doug
25:29
created

BYNHeightGrid   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 79
Duplicated Lines 0 %

Test Coverage

Coverage 86.96%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 50
dl 0
loc 79
ccs 40
cts 46
cp 0.8696
rs 10
c 1
b 0
f 0
wmc 13

3 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 36 6
A getValues() 0 5 1
A getRecord() 0 20 6
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
use SplFileObject;
14
15
use function assert;
16
use function in_array;
17
use function unpack;
18
19
class BYNHeightGrid extends GeographicGeoidHeightGrid
20
{
21
    use BiquadraticInterpolation;
22
23
    private string $shortFormatChar = 'v';
24
    private string $longFormatChar = 'V';
25
    private string $doubleFormatChar = 'e';
26
    private string $floatFormatChar = 'g';
27
    private int $dataSize;
28
    private float $conversionFactor;
29
30 3
    public function __construct($filename)
31
    {
32 3
        $this->storageOrder = self::STORAGE_ORDER_INCREASING_LONGITUDE_DECREASING_LATIITUDE;
33 3
        $this->gridFile = new SplFileObject($filename);
34
35 3
        $this->gridFile->fseek(0);
36 3
        $rawData = $this->gridFile->fread(80);
37 3
        if (unpack('vByteOrder', $rawData, 50)['ByteOrder'] === 1) {
38
            $this->shortFormatChar = 'n';
39
            $this->longFormatChar = 'N';
40
            $this->doubleFormatChar = 'E';
41
            $this->floatFormatChar = 'G';
42
        }
43
44 3
        $data = 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);
45
46 3
        foreach ($data as $key => $value) {
47 3
            if (in_array($key, ['SOUTH', 'EAST', 'NORTH', 'WEST']) && $value > 2147483647) {
48 3
                $data[$key] -= 4294967295;
49
            }
50
        }
51
52 3
        assert($data['DATA'] === 0); // actual values, not errors
53 3
        assert($data['TYPE'] === 1); // ellipsoid separation
54 3
        assert($data['SIZEOF'] === 2 || $data['SIZEOF'] === 4); // sensible data type
55
56 3
        $this->startX = (new ArcSecond($data['WEST']))->asDegrees()->getValue();
57 3
        $this->startY = (new ArcSecond($data['SOUTH']))->asDegrees()->getValue();
58 3
        $this->endX = (new ArcSecond($data['EAST']))->asDegrees()->getValue();
59 3
        $this->endY = (new ArcSecond($data['NORTH']))->asDegrees()->getValue();
60 3
        $this->columnGridInterval = (new ArcSecond($data['DLON']))->asDegrees()->getValue();
61 3
        $this->rowGridInterval = (new ArcSecond($data['DLAT']))->asDegrees()->getValue();
62 3
        $this->numberOfColumns = (int) (string) (($this->endX - $this->startX) / $this->columnGridInterval) + 1;
63 3
        $this->numberOfRows = (int) (string) (($this->endY - $this->startY) / $this->rowGridInterval) + 1;
64 3
        $this->dataSize = $data['SIZEOF'];
65 3
        $this->conversionFactor = $data['FACTOR'];
66
    }
67
68
    /**
69
     * @return Metre[]
70
     */
71 3
    public function getValues($x, $y): array
72
    {
73 3
        $shift = $this->interpolate($x, $y)[0];
74
75 3
        return [new Metre($shift)];
76
    }
77
78 3
    protected function getRecord(int $longitudeIndex, int $latitudeIndex): GridValues
79
    {
80 3
        $recordId = ($this->numberOfRows - $latitudeIndex - 1) * $this->numberOfColumns + $longitudeIndex;
81
82 3
        $offset = 80 + $recordId * $this->dataSize;
83 3
        $this->gridFile->fseek($offset);
84 3
        $rawRow = $this->gridFile->fread($this->dataSize);
85 3
        $dataType = $this->dataSize === 2 ? $this->shortFormatChar : $this->longFormatChar;
86 3
        $shift = unpack("{$dataType}shift", $rawRow)['shift'];
87 3
        if ($this->dataSize === 4 && $shift > 2147483647) {
88 3
            $shift -= 4294967295;
89
        } elseif ($this->dataSize === 2 && $shift > 32767) {
90
            $shift -= 65535;
91
        }
92 3
        $shift = $shift / $this->conversionFactor;
93
94 3
        return new GridValues(
95 3
            $longitudeIndex * $this->columnGridInterval + $this->startX,
96 3
            $latitudeIndex * $this->rowGridInterval + $this->startY,
97 3
            [$shift]
98
        );
99
    }
100
}
101