KMSGrid   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Test Coverage

Coverage 56.52%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 73
c 1
b 0
f 0
dl 0
loc 119
ccs 39
cts 69
cp 0.5652
rs 10
wmc 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getValues() 0 5 1
B __construct() 0 66 7
A getRecord() 0 25 2
1
<?php
2
3
/**
4
 * PHPCoord.
5
 *
6
 * @author Doug Wright
7
 */
8
declare(strict_types=1);
9
10
namespace PHPCoord\CoordinateOperation;
11
12
use Composer\Pcre\Preg;
13
use PHPCoord\UnitOfMeasure\Length\Metre;
14
use SplFixedArray;
15
16
use function assert;
17
use function explode;
18
use function intdiv;
19
use function trim;
20
21
/**
22
 * @see https://github.com/Kortforsyningen/kmsgrid/blob/master/kmsgrid.py for documentation
23
 */
24
class KMSGrid extends GeographicGeoidHeightGrid
25
{
26
    use BilinearInterpolation;
27
28
    private int $binaryHeaderLength = 64;
29
    private bool $isBinary;
30
    private string $integerFormatChar = 'V';
31
    private string $doubleFormatChar = 'e';
32
    private string $floatFormatChar = 'g';
33
34
    /**
35
     * @var SplFixedArray<float>
36
     */
37
    private SplFixedArray $textData;
38 1
39
    public function __construct(string $filename)
40 1
    {
41 1
        $this->gridFile = new GridFile($filename);
42
        $this->storageOrder = self::STORAGE_ORDER_INCREASING_LONGITUDE_DECREASING_LATIITUDE;
43 1
44
        $firstChar = $this->gridFile->fread(1);
45 1
46 1
        if ($firstChar === ' ') {
47
            $this->isBinary = false;
48 1
49
            $header = Preg::split('/\s+/', trim($this->gridFile->fgets()));
50 1
51 1
            $this->startX = (float) $header[2];
52 1
            $this->startY = (float) $header[0];
53 1
            $this->endX = (float) $header[3];
54 1
            $this->endY = (float) $header[1];
55 1
            $this->columnGridInterval = (float) $header[5];
56 1
            $this->rowGridInterval = (float) $header[4];
57 1
            $this->numberOfColumns = (int) (string) (($this->endX - $this->startX) / $this->columnGridInterval) + 1;
58
            $this->numberOfRows = (int) (string) (($this->endY - $this->startY) / $this->rowGridInterval) + 1;
59 1
60 1
            $this->textData = new SplFixedArray($this->numberOfColumns * $this->numberOfRows);
61 1
            $index = 0;
62 1
            while (!$this->gridFile->eof()) {
63 1
                $rawData = trim($this->gridFile->fgets());
64 1
                if ($rawData) {
65 1
                    $values = explode(' ', trim(Preg::replace('/\s+/', ' ', $rawData)));
66 1
                    foreach ($values as $value) {
67 1
                        $this->textData[$index] = (float) $value;
68
                        ++$index;
69
                    }
70
                }
71
            }
72
        } else {
73
            $this->isBinary = true;
74
75
            $this->gridFile->fseek(0);
76
            $rawHeader = $this->gridFile->fread($this->binaryHeaderLength);
77
            $icode = $this->unpack("{$this->integerFormatChar}ICODE", $rawHeader)['ICODE'];
78
79
            if ($icode !== 777) {
80
                $this->integerFormatChar = 'N';
81
                $this->doubleFormatChar = 'E';
82
                $this->floatFormatChar = 'G';
83
                $icode = $this->unpack("{$this->integerFormatChar}ICODE", $rawHeader)['ICODE'];
84
            }
85
86
            assert($icode === 777);
87
88
            /** @var array{ICODE: int, BMIN: float, BMAX: float, LMIN: float, LMAX: float, DB: float, DL: float, DATUM: int, CSTM: int, MODE: int} $header */
89
            $header = $this->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
            $this->startX = $header['LMIN'];
91
            $this->startY = $header['BMIN'];
92
            $this->endX = $header['LMAX'];
93
            $this->endY = $header['BMAX'];
94
            $this->columnGridInterval = $header['DL'];
95
            $this->rowGridInterval = $header['DB'];
96
            $this->numberOfColumns = (int) (string) (($this->endX - $this->startX) / $this->columnGridInterval) + 1;
97
            $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
            $this->numberOfColumns = intdiv($this->numberOfColumns + 16 - 1, 16) * 16;
101
        }
102 1
103
        if ($this->startX > 180) { // normalise if necessary
104
            $this->startX -= 360;
105
        }
106
    }
107
108
    /**
109
     * @return Metre[]
110 1
     */
111
    public function getValues(float $x, float $y): array
112 1
    {
113
        $shift = $this->interpolate($x, $y)[0];
114 1
115
        return [new Metre($shift)];
116
    }
117 1
118
    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 1
    {
120 1
        $recordId = ($this->numberOfRows - $latitudeIndex - 1) * $this->numberOfColumns + $longitudeIndex;
121 1
        $longitude = $longitudeIndex * $this->columnGridInterval + $this->startX;
122
        $latitude = $latitudeIndex * $this->rowGridInterval + $this->startY;
123 1
124
        if ($this->isBinary) {
125
            $offset = $this->binaryHeaderLength + $recordId * 4;
126
            $this->gridFile->fseek($offset);
127
            $rawRow = $this->gridFile->fread(4);
128
            /** @var array{shift: float} $data */
129
            $data = $this->unpack("{$this->floatFormatChar}shift", $rawRow);
130
131
            return new GridValues(
132
                $longitude,
133
                $latitude,
134
                [$data['shift']]
135
            );
136 1
        } else {
137
            assert($this->textData[$recordId] !== null);
138 1
139 1
            return new GridValues(
140 1
                $longitude,
141 1
                $latitude,
142 1
                [$this->textData[$recordId]]
143
            );
144
        }
145
    }
146
}
147