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

GTXGrid   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Test Coverage

Coverage 89.58%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 46
dl 0
loc 80
ccs 43
cts 48
cp 0.8958
rs 10
c 1
b 0
f 0
wmc 8

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getRecord() 0 17 1
A getHeader() 0 20 3
A __construct() 0 15 2
A getValues() 0 10 2
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 in_array;
12
use PHPCoord\UnitOfMeasure\Length\Metre;
13
use SplFileObject;
14
use function substr;
15
use function unpack;
16
17
/**
18
 * @see https://vdatum.noaa.gov/docs/gtx_info.html for documentation
19
 */
20
class GTXGrid extends GeographicGeoidHeightGrid
21
{
22
    use BilinearInterpolation;
23
24
    private int $headerLength;
25
    private string $shiftDataType;
26
27 8
    public function __construct($filename)
28
    {
29 8
        $this->gridFile = new SplFileObject($filename);
30 8
        $this->storageOrder = self::STORAGE_ORDER_INCREASING_LONGITUDE_INCREASING_LATIITUDE;
31
32 8
        $header = $this->getHeader();
33 8
        $this->startX = $header['xlonsw'];
34 8
        $this->startY = $header['xlatsw'];
35 8
        $this->numberOfColumns = $header['nlon'];
36 8
        $this->numberOfRows = $header['nlat'];
37 8
        $this->columnGridInterval = $header['dlon'];
38 8
        $this->rowGridInterval = $header['dlat'];
39
40 8
        if ($this->startX > 180) { // normalise if necessary
41 1
            $this->startX -= 360;
42
        }
43 8
    }
44
45
    /**
46
     * @return Metre[]
47
     */
48 8
    public function getValues($x, $y): array
49
    {
50 8
        $shift = $this->interpolate($x, $y)[0];
51
52
        // These are in millimeters for some reason... :/
53 8
        if (in_array($this->gridFile->getBasename(), ['vertconc.gtx', 'vertcone.gtx', 'vertconw.gtx'], true)) {
54
            $shift /= 1000;
55
        }
56
57 8
        return [new Metre($shift)];
58
    }
59
60 8
    private function getRecord(int $longitudeIndex, int $latitudeIndex): GridValues
0 ignored issues
show
Unused Code introduced by
The method getRecord() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
61
    {
62 8
        $recordId = match ($this->storageOrder) {
63 8
            self::STORAGE_ORDER_INCREASING_LATITUDE_INCREASING_LONGITUDE => $longitudeIndex * $this->numberOfRows + $latitudeIndex,
64 8
            self::STORAGE_ORDER_INCREASING_LONGITUDE_DECREASING_LATIITUDE => ($this->numberOfRows - $latitudeIndex - 1) * $this->numberOfColumns + $longitudeIndex,
65 8
            self::STORAGE_ORDER_INCREASING_LONGITUDE_INCREASING_LATIITUDE => $latitudeIndex * $this->numberOfColumns + $longitudeIndex,
66
        };
67
68 8
        $offset = $this->headerLength + $recordId * 4;
69 8
        $this->gridFile->fseek($offset);
70 8
        $rawRow = $this->gridFile->fread(4);
71 8
        $data = unpack("{$this->shiftDataType}shift", $rawRow);
72
73 8
        return new GridValues(
74 8
            $longitudeIndex * $this->columnGridInterval + $this->startX,
75 8
            $latitudeIndex * $this->rowGridInterval + $this->startY,
76 8
            [$data['shift']]
77
        );
78
    }
79
80 8
    private function getHeader(): array
81
    {
82 8
        $this->gridFile->fseek(0);
83 8
        $rawHeader = $this->gridFile->fread(44);
84 8
        $ikind = substr($rawHeader, 40, 4);
85 8
        if (unpack('Nikind', $ikind)['ikind'] === 1) { // big endian
86
            $this->headerLength = 44;
87
            $this->shiftDataType = 'G';
88
            $data = unpack('Exlatsw/Exlonsw/Edlat/Edlon/Nnlat/Nnlon', $rawHeader);
89 8
        } elseif (unpack('Vikind', $ikind)['ikind'] === 1) { // little endian
90 1
            $this->headerLength = 44;
91 1
            $this->shiftDataType = 'g';
92 1
            $data = unpack('exlatsw/exlonsw/edlat/edlon/Vnlat/Vnlon', $rawHeader);
93
        } else { // not all files (e.g. NZ) have this endian check column, assume big endian
94 7
            $this->headerLength = 40;
95 7
            $this->shiftDataType = 'G';
96 7
            $data = unpack('Exlatsw/Exlonsw/Edlat/Edlon/Nnlat/Nnlon', $rawHeader);
97
        }
98
99 8
        return $data;
100
    }
101
}
102