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

KMSGrid::__construct()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 66
Code Lines 46

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 11.6614

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 46
nc 8
nop 1
dl 0
loc 66
ccs 25
cts 46
cp 0.5435
crap 11.6614
rs 8.2448
c 1
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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