Passed
Push — master ( 0a16ff...1038d1 )
by Doug
28:57
created

KMSGrid::getRecord()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2.0014

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 17
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 22
ccs 13
cts 14
cp 0.9286
crap 2.0014
rs 9.7
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 explode;
13
use function intdiv;
14
15
use PHPCoord\UnitOfMeasure\Length\Metre;
16
17
use function preg_replace;
18
use function preg_split;
19
20
use SplFileObject;
21
use SplFixedArray;
22
23
use function trim;
24
use function unpack;
25
26
/**
27
 * @see https://github.com/Kortforsyningen/kmsgrid/blob/master/kmsgrid.py for documentation
28
 */
29
class KMSGrid extends GeographicGeoidHeightGrid
30
{
31
    use BilinearInterpolation;
32
33
    private int $binaryHeaderLength = 64;
34
    private bool $isBinary;
35
    private string $integerFormatChar = 'V';
36
    private string $doubleFormatChar = 'e';
37
    private string $floatFormatChar = 'g';
38
    private SplFixedArray $textData;
39
40 3
    public function __construct($filename)
41
    {
42 3
        $this->gridFile = new SplFileObject($filename);
43 3
        $this->storageOrder = self::STORAGE_ORDER_INCREASING_LONGITUDE_DECREASING_LATIITUDE;
44
45 3
        $firstChar = $this->gridFile->fread(1);
46
47 3
        if ($firstChar === ' ') {
48 1
            $this->isBinary = false;
49
50 1
            $header = preg_split('/\s+/', trim($this->gridFile->fgets()));
51
52 1
            $this->startX = (float) $header[2];
53 1
            $this->startY = (float) $header[0];
54 1
            $this->endX = (float) $header[3];
55 1
            $this->endY = (float) $header[1];
56 1
            $this->columnGridInterval = (float) $header[5];
57 1
            $this->rowGridInterval = (float) $header[4];
58 1
            $this->numberOfColumns = (int) (string) (($this->endX - $this->startX) / $this->columnGridInterval) + 1;
59 1
            $this->numberOfRows = (int) (string) (($this->endY - $this->startY) / $this->rowGridInterval) + 1;
60
61 1
            $this->textData = new SplFixedArray($this->numberOfColumns * $this->numberOfRows);
62 1
            $index = 0;
63 1
            while (!$this->gridFile->eof()) {
64 1
                $rawData = trim($this->gridFile->fgets());
65 1
                if ($rawData) {
66 1
                    $values = explode(' ', trim(preg_replace('/\s+/', ' ', $rawData)));
67 1
                    foreach ($values as $value) {
68 1
                        $this->textData[$index] = (float) $value;
69 1
                        ++$index;
70
                    }
71
                }
72
            }
73
        } else {
74 2
            $this->isBinary = true;
75
76 2
            $this->gridFile->fseek(0);
77 2
            $rawHeader = $this->gridFile->fread($this->binaryHeaderLength);
78 2
            $icode = unpack("{$this->integerFormatChar}ICODE", $rawHeader)['ICODE'];
79
80 2
            if ($icode !== 777) {
81
                $this->integerFormatChar = 'N';
82
                $this->doubleFormatChar = 'E';
83
                $this->floatFormatChar = 'G';
84
                $icode = unpack("{$this->integerFormatChar}ICODE", $rawHeader)['ICODE'];
85
            }
86
87 2
            assert($icode === 777);
88
89 2
            $header = unpack("{$this->integerFormatChar}ICODE/{$this->doubleFormatChar}BMIN/{$this->doubleFormatChar}BMAX/{$this->doubleFormatChar}LMIN/{$this->doubleFormatChar}LMAX/{$this->doubleFormatChar}DB/{$this->doubleFormatChar}DL/{$this->integerFormatChar}DATUM/{$this->integerFormatChar}CSTM/{$this->integerFormatChar}MODE", $rawHeader);
90 2
            $this->startX = $header['LMIN'];
91 2
            $this->startY = $header['BMIN'];
92 2
            $this->endX = $header['LMAX'];
93 2
            $this->endY = $header['BMAX'];
94 2
            $this->columnGridInterval = $header['DL'];
95 2
            $this->rowGridInterval = $header['DB'];
96 2
            $this->numberOfColumns = (int) (string) (($this->endX - $this->startX) / $this->columnGridInterval) + 1;
97 2
            $this->numberOfRows = (int) (string) (($this->endY - $this->startY) / $this->rowGridInterval) + 1;
98
99
            // there's a weird 16 records per block thing here, columns extend beyond declared boundaries
100 2
            $this->numberOfColumns = intdiv($this->numberOfColumns + 16 - 1, 16) * 16;
101
        }
102
103 3
        if ($this->startX > 180) { // normalise if necessary
104
            $this->startX -= 360;
105
        }
106
    }
107
108
    /**
109
     * @return Metre[]
110
     */
111 3
    public function getValues($x, $y): array
112
    {
113 3
        $shift = $this->interpolate($x, $y)[0];
114
115 3
        return [new Metre($shift)];
116
    }
117
118 3
    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...
119
    {
120 3
        $recordId = ($this->numberOfRows - $latitudeIndex - 1) * $this->numberOfColumns + $longitudeIndex;
121 3
        $longitude = $longitudeIndex * $this->columnGridInterval + $this->startX;
122 3
        $latitude = $latitudeIndex * $this->rowGridInterval + $this->startY;
123
124 3
        if ($this->isBinary) {
125 2
            $offset = $this->binaryHeaderLength + $recordId * 4;
126 2
            $this->gridFile->fseek($offset);
127 2
            $rawRow = $this->gridFile->fread(4);
128 2
            $data = unpack("{$this->floatFormatChar}shift", $rawRow);
129
130 2
            return new GridValues(
131
                $longitude,
132
                $latitude,
133 2
                [$data['shift']]
134
            );
135
        } else {
136 1
            return new GridValues(
137
                $longitude,
138
                $latitude,
139 1
                [$this->textData[$recordId]]
140
            );
141
        }
142
    }
143
}
144