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

NTv2Grid::getAdjustment()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 60
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 8.4953

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 47
nc 5
nop 2
dl 0
loc 60
ccs 33
cts 48
cp 0.6875
crap 8.4953
rs 8.223
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 function assert;
12
use PHPCoord\UnitOfMeasure\Angle\ArcSecond;
13
use function round;
14
use SplFileObject;
15
use function unpack;
16
use function usort;
17
18
class NTv2Grid extends GeographicGrid
19
{
20
    private const RECORD_SIZE = 16;
21
    private const FLAG_WITHIN_LIMITS = 1;
22
    private const FLAG_ON_UPPER_LATITUDE = 2;
23
    private const FLAG_ON_UPPER_LONGITUDE = 3;
24
    private const FLAG_ON_UPPER_LATITUDE_AND_LONGITUDE = 4;
25
26
    private string $integerFormatChar = 'V';
27
    private string $doubleFormatChar = 'e';
28
    private string $floatFormatChar = 'g';
29
30
    private array $subFileMetaData = [];
31
32 5
    public function __construct($filename)
33
    {
34 5
        $this->gridFile = new SplFileObject($filename);
35 5
        $this->storageOrder = self::STORAGE_ORDER_INCREASING_LATITUDE_INCREASING_LONGITUDE;
36
37 5
        $this->readHeader();
38 5
    }
39
40
    /**
41
     * @return ArcSecond[]
42
     */
43 5
    public function getValues(float $x, float $y): array
44
    {
45
        // NTv2 is longitude positive *west*
46 5
        $x *= -1;
47
48
        // NTv2 is in seconds, not degrees
49 5
        $x *= 3600;
50 5
        $y *= 3600;
51
52 5
        $gridToUse = $this->determineBestGrid($x, $y);
53
54 5
        return $gridToUse->getValues($x, $y);
55
    }
56
57 5
    private function readHeader(): void
58
    {
59 5
        $this->gridFile->fseek(0);
60 5
        $rawData = $this->gridFile->fread(11 * self::RECORD_SIZE);
61 5
        if (unpack('VNUM_OREC', $rawData, 8)['NUM_OREC'] !== 11) {
62
            $this->integerFormatChar = 'N';
63
            $this->doubleFormatChar = 'E';
64
            $this->floatFormatChar = 'G';
65
        }
66
67 5
        $data = unpack("A8/{$this->integerFormatChar}NUM_OREC/x4/A8/{$this->integerFormatChar}NUM_SREC/x4/A8/{$this->integerFormatChar}NUM_FILE/x4/A8/A8GS_TYPE/A8/A8VERSION/A8/A8SYSTEM_F/A8/A8SYSTEM_T/A8/{$this->doubleFormatChar}MAJOR_F/A8/{$this->doubleFormatChar}MINOR_F/A8/{$this->doubleFormatChar}MAJOR_T/A8/{$this->doubleFormatChar}MINOR_T", $rawData);
68
69 5
        assert($data['GS_TYPE'] === 'SECONDS');
70
71 5
        $subFileStart = 11 * self::RECORD_SIZE;
72 5
        for ($i = 0; $i < $data['NUM_FILE']; ++$i) {
73 5
            $this->gridFile->fseek($subFileStart);
74 5
            $subFileRawData = $this->gridFile->fread(11 * self::RECORD_SIZE);
75 5
            $subFileData = unpack("A8/A8SUB_NAME/A8/A8PARENT/A8/A8CREATED/A8/A8UPDATED/A8/{$this->doubleFormatChar}S_LAT/A8/{$this->doubleFormatChar}N_LAT/A8/{$this->doubleFormatChar}E_LONG/A8/{$this->doubleFormatChar}W_LONG/A8/{$this->doubleFormatChar}LAT_INC/A8/{$this->doubleFormatChar}LONG_INC/A8/{$this->integerFormatChar}GS_COUNT/x4", $subFileRawData);
76 5
            $subFileData['offsetStart'] = $subFileStart;
77
78
            //apply rounding to eliminate fp issues when being deserialized
79 5
            $subFileData['S_LAT'] = round($subFileData['S_LAT'], 5);
80 5
            $subFileData['N_LAT'] = round($subFileData['N_LAT'], 5);
81 5
            $subFileData['E_LONG'] = round($subFileData['E_LONG'], 5);
82 5
            $subFileData['W_LONG'] = round($subFileData['W_LONG'], 5);
83 5
            $this->subFileMetaData[$subFileData['SUB_NAME']] = $subFileData;
84
85 5
            $subFileStart += 11 * self::RECORD_SIZE + $subFileData['GS_COUNT'] * self::RECORD_SIZE;
86
        }
87 5
    }
88
89 5
    private function determineBestGrid(float $longitude, float $latitude): NTv2SubGrid
90
    {
91 5
        $possibleGrids = [];
92 5
        foreach ($this->subFileMetaData as $subFileMetaDatum) {
93 5
            if ($latitude === $subFileMetaDatum['N_LAT'] && $longitude === $subFileMetaDatum['W_LONG']) {
94
                $possibleGrids[] = [self::FLAG_ON_UPPER_LATITUDE_AND_LONGITUDE, $subFileMetaDatum];
95 5
            } elseif ($longitude === $subFileMetaDatum['W_LONG']) {
96
                $possibleGrids[] = [self::FLAG_ON_UPPER_LONGITUDE, $subFileMetaDatum];
97 5
            } elseif ($latitude === $subFileMetaDatum['N_LAT']) {
98
                $possibleGrids[] = [self::FLAG_ON_UPPER_LATITUDE, $subFileMetaDatum];
99 5
            } elseif ($latitude >= $subFileMetaDatum['S_LAT'] && $latitude <= $subFileMetaDatum['N_LAT'] && $longitude >= $subFileMetaDatum['E_LONG'] && $longitude <= $subFileMetaDatum['W_LONG']) {
100 5
                $possibleGrids[] = [self::FLAG_WITHIN_LIMITS, $subFileMetaDatum];
101
            }
102
        }
103
104 5
        usort($possibleGrids, static function ($a, $b) {
105 3
            return $a[0] <=> $b[0] ?: $a[1]['LAT_INC'] <=> $b[1]['LAT_INC'] ?: $a[2]['LONG_INC'] <=> $b[2]['LONG_INC'];
106 5
        });
107
108 5
        $gridToUse = $possibleGrids[0][1];
109
110 5
        return new NTv2SubGrid(
111 5
            $this->gridFile->getPathname(),
112 5
            $gridToUse['offsetStart'],
113 5
            $gridToUse['S_LAT'],
114 5
            $gridToUse['N_LAT'],
115 5
            $gridToUse['E_LONG'],
116 5
            $gridToUse['W_LONG'],
117 5
            $gridToUse['LAT_INC'],
118 5
            $gridToUse['LONG_INC'],
119 5
            $this->floatFormatChar
120
        );
121
    }
122
}
123