Test Failed
Pull Request — master (#21)
by
unknown
01:52
created

OSRef   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 175
Duplicated Lines 0 %

Test Coverage

Coverage 92.31%

Importance

Changes 0
Metric Value
eloc 56
dl 0
loc 175
ccs 60
cts 65
cp 0.9231
rs 10
c 0
b 0
f 0
wmc 16

11 Methods

Rating   Name   Duplication   Size   Complexity  
A toGridReference() 0 25 2
A getOriginLatitude() 0 3 1
A __construct() 0 3 1
A getOriginLongitude() 0 3 1
A getReferenceEllipsoid() 0 3 1
A toLatLng() 0 10 1
A getOriginNorthing() 0 3 1
A __toString() 0 3 1
A getOriginEasting() 0 3 1
A getScaleFactor() 0 3 1
A fromGridReference() 0 38 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PHPCoord;
6
7
use function floor;
8
use LengthException;
9
use function str_pad;
10
use function strpos;
11
12
/**
13
 * Ordnance Survey grid reference
14
 * References are accurate to 1m.
15
 *
16
 * @author Jonathan Stott
17
 * @author Doug Wright
18
 */
19
class OSRef extends TransverseMercator
20
{
21
    private const GRID_LETTERS = 'VWXYZQRSTULMNOPFGHJKABCDE';
22
    private const GRID_LETTERS_TETRAD = 'AFKQVBGLRWCHMSXDINTYEJPUZ';
23
24
    /**
25
     * @return RefEll
26
     */
27 4
    public function getReferenceEllipsoid(): RefEll
28
    {
29 4
        return RefEll::airy1830();
30
    }
31
32
    /**
33
     * @return float
34
     */
35 5
    public function getScaleFactor(): float
36
    {
37 5
        return 0.9996012717;
38
    }
39
40
    /**
41
     * @return int
42
     */
43 5
    public function getOriginNorthing(): int
44
    {
45 5
        return -100000;
46
    }
47
48
    /**
49
     * @return int
50
     */
51 5
    public function getOriginEasting(): int
52
    {
53 5
        return 400000;
54
    }
55
56
    /**
57
     * @return float
58
     */
59 5
    public function getOriginLatitude(): float
60
    {
61 5
        return 49;
62
    }
63
64
    /**
65
     * @return float
66
     */
67 5
    public function getOriginLongitude(): float
68
    {
69 5
        return -2;
70
    }
71
72
    /**
73
     * Create a new object representing a OSGB reference.
74
     *
75
     * @param int $x
76
     * @param int $y
77
     * @param int $z
78
     */
79 16
    public function __construct($x, $y, $z = 0)
80
    {
81 16
        parent::__construct($x, $y, $z, RefEll::airy1830());
82 16
    }
83
84
    /**
85
     * Take a string formatted as a OS grid reference (e.g.
86
     * "TG514131") and return a reference to an OSRef object that represents
87
     * that grid reference.
88
     *
89
     * @param string $ref
90
     *
91
     * @return static
92
     */
93 1
    public static function fromGridReference(string $ref): self
94
    {
95 1
        if(strlen($ref) === 5) {
96 1
            $tetrad = TRUE;
97
        } elseif (strlen($ref) % 2 !== 0) {
98
            throw new LengthException('Grid ref must be an even number of characters');
99
        }
100
101
        //first (major) letter is the 500km grid sq, origin at -1000000, -500000
102 1
        $majorEasting = strpos(self::GRID_LETTERS, $ref[0]) % 5 * 500000 - 1000000;
103 1
        $majorNorthing = (floor(strpos(self::GRID_LETTERS, $ref[0]) / 5)) * 500000 - 500000;
104
105
        //second (minor) letter is 100km grid sq, origin at 0,0 of this square
106 1
        $minorEasting = strpos(self::GRID_LETTERS, $ref[1]) % 5 * 100000;
107 1
        $minorNorthing = (floor(strpos(self::GRID_LETTERS, $ref[1]) / 5)) * 100000;
108
109
        //tetrad letter is 2km grid sq. THE GRID HAS A DIFFERENT ORIENTATION - starts botom left and runs bottom to top. Includes I but no O.
110 1
        $tetradEasting = 0;
111 1
        $tetradNorthing = 0;
112 1
        if($tetrad) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tetrad does not seem to be defined for all execution paths leading up to this point.
Loading history...
113 1
            $tetradEasting = strpos(self::GRID_LETTERS_TETRAD, $ref[4]) % 5 * 2000;
114 1
            $tetradNorthing = (floor(strpos(self::GRID_LETTERS_TETRAD, $ref[4]) / 5)) * 2000;
115
        }
116
117
        //numbers are a division of that square into smaller and smaller pieces
118 1
        if($tetrad) {
119 1
            $numericPortion = substr($ref, 2, 2);
120 1
            $numericPortionSize = strlen($numericPortion) / 2;
121
        } else {
122
            $numericPortion = substr($ref, 2);
123
            $numericPortionSize = strlen($numericPortion) / 2;
124
        }
125 1
        $gridSizeInMetres = 1 * (10 ** (5 - $numericPortionSize));
126
127 1
        $easting = $majorEasting + $minorEasting + $tetradEasting + (substr($numericPortion, 0, $numericPortionSize) * $gridSizeInMetres);
128 1
        $northing = $majorNorthing + $minorNorthing + $tetradNorthing + (substr($numericPortion, -$numericPortionSize, $numericPortionSize) * $gridSizeInMetres);
129
130 1
        return new static((int) $easting, (int) $northing);
131
    }
132
133
    /**
134
     * Convert this grid reference into a grid reference string of a
135
     * given length (2, 4, 6, 8 or 10) including the two-character
136
     * designation for the 100km square. e.g. TG514131.
137
     *
138
     * @param int $length
139
     *
140
     * @return string
141
     */
142 7
    public function toGridReference(int $length): string
143
    {
144 7
        if ($length % 2 !== 0) {
145
            throw new LengthException('Chosen length must be an even number');
146
        }
147
148 7
        $halfLength = $length / 2;
149
150 7
        $easting = str_pad((string) $this->x, 6, '0', STR_PAD_LEFT);
151 7
        $northing = str_pad((string) $this->y, 6, '0', STR_PAD_LEFT);
152
153 7
        $adjustedX = $this->x + 1000000;
154 7
        $adjustedY = $this->y + 500000;
155 7
        $majorSquaresEast = floor($adjustedX / 500000);
156 7
        $majorSquaresNorth = floor($adjustedY / 500000);
157 7
        $majorLetterIndex = (int) (5 * $majorSquaresNorth + $majorSquaresEast);
158 7
        $majorLetter = substr(self::GRID_LETTERS, $majorLetterIndex, 1);
159
160
        //second (minor) letter is 100km grid sq, origin at 0,0 of this square
161 7
        $minorSquaresEast = $easting[0] % 5;
162 7
        $minorSquaresNorth = $northing[0] % 5;
163 7
        $minorLetterIndex = (5 * $minorSquaresNorth + $minorSquaresEast);
164 7
        $minorLetter = substr(self::GRID_LETTERS, $minorLetterIndex, 1);
165
166 7
        return $majorLetter . $minorLetter . substr($easting, 1, $halfLength) . substr($northing, 1, $halfLength);
167
    }
168
169
    /**
170
     * Convert this grid reference into a latitude and longitude.
171
     *
172
     * @return LatLng
173
     */
174 3
    public function toLatLng(): LatLng
175
    {
176 3
        $N = $this->y;
177 3
        $E = $this->x;
178 3
        $N0 = $this->getOriginNorthing();
179 3
        $E0 = $this->getOriginEasting();
180 3
        $phi0 = $this->getOriginLatitude();
181 3
        $lambda0 = $this->getOriginLongitude();
182
183 3
        return $this->convertToLatitudeLongitude($N, $E, $N0, $E0, $phi0, $lambda0);
184
    }
185
186
    /**
187
     * String version of coordinate.
188
     *
189
     * @return string
190
     */
191 4
    public function __toString(): string
192
    {
193 4
        return "({$this->x}, {$this->y})";
194
    }
195
}
196