Passed
Pull Request — master (#21)
by
unknown
02:18 queued 30s
created

OSRef::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 22
    public function __construct($x, $y, $z = 0)
80
    {
81 22
        parent::__construct($x, $y, $z, RefEll::airy1830());
82 22
    }
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 7
    public static function fromGridReference(string $ref): self
94
    {
95 7
        $tetrad = FALSE;
96 7
        if(strlen($ref) === 5) {
97 1
            $tetrad = TRUE;
98 6
        } elseif (strlen($ref) % 2 !== 0) {
99
            throw new LengthException('Grid ref must be an even number of characters');
100
        }
101
102
        //first (major) letter is the 500km grid sq, origin at -1000000, -500000
103 7
        $majorEasting = strpos(self::GRID_LETTERS, $ref[0]) % 5 * 500000 - 1000000;
104 7
        $majorNorthing = (floor(strpos(self::GRID_LETTERS, $ref[0]) / 5)) * 500000 - 500000;
105
106
        //second (minor) letter is 100km grid sq, origin at 0,0 of this square
107 7
        $minorEasting = strpos(self::GRID_LETTERS, $ref[1]) % 5 * 100000;
108 7
        $minorNorthing = (floor(strpos(self::GRID_LETTERS, $ref[1]) / 5)) * 100000;
109
110
        //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.
111 7
        $tetradEasting = 0;
112 7
        $tetradNorthing = 0;
113 7
        if($tetrad) {
114 1
            $tetradEasting = strpos(self::GRID_LETTERS_TETRAD, $ref[4]) % 5 * 2000;
115 1
            $tetradNorthing = (floor(strpos(self::GRID_LETTERS_TETRAD, $ref[4]) / 5)) * 2000;
116
        }
117
118
        //numbers are a division of that square into smaller and smaller pieces
119 7
        if($tetrad) {
120 1
            $numericPortion = substr($ref, 2, 2);
121 1
            $numericPortionSize = strlen($numericPortion) / 2;
122
        } else {
123 6
            $numericPortion = substr($ref, 2);
124 6
            $numericPortionSize = strlen($numericPortion) / 2;
125
        }
126 7
        $gridSizeInMetres = 1 * (10 ** (5 - $numericPortionSize));
127
128 7
        $easting = $majorEasting + $minorEasting + $tetradEasting + (substr($numericPortion, 0, $numericPortionSize) * $gridSizeInMetres);
129 7
        $northing = $majorNorthing + $minorNorthing + $tetradNorthing + (substr($numericPortion, -$numericPortionSize, $numericPortionSize) * $gridSizeInMetres);
130
131 7
        return new static((int) $easting, (int) $northing);
132
    }
133
134
    /**
135
     * Convert this grid reference into a grid reference string of a
136
     * given length (2, 4, 6, 8 or 10) including the two-character
137
     * designation for the 100km square. e.g. TG514131.
138
     *
139
     * @param int $length
140
     *
141
     * @return string
142
     */
143 7
    public function toGridReference(int $length): string
144
    {
145 7
        if ($length % 2 !== 0) {
146
            throw new LengthException('Chosen length must be an even number');
147
        }
148
149 7
        $halfLength = $length / 2;
150
151 7
        $easting = str_pad((string) $this->x, 6, '0', STR_PAD_LEFT);
152 7
        $northing = str_pad((string) $this->y, 6, '0', STR_PAD_LEFT);
153
154 7
        $adjustedX = $this->x + 1000000;
155 7
        $adjustedY = $this->y + 500000;
156 7
        $majorSquaresEast = floor($adjustedX / 500000);
157 7
        $majorSquaresNorth = floor($adjustedY / 500000);
158 7
        $majorLetterIndex = (int) (5 * $majorSquaresNorth + $majorSquaresEast);
159 7
        $majorLetter = substr(self::GRID_LETTERS, $majorLetterIndex, 1);
160
161
        //second (minor) letter is 100km grid sq, origin at 0,0 of this square
162 7
        $minorSquaresEast = $easting[0] % 5;
163 7
        $minorSquaresNorth = $northing[0] % 5;
164 7
        $minorLetterIndex = (5 * $minorSquaresNorth + $minorSquaresEast);
165 7
        $minorLetter = substr(self::GRID_LETTERS, $minorLetterIndex, 1);
166
167 7
        return $majorLetter . $minorLetter . substr($easting, 1, $halfLength) . substr($northing, 1, $halfLength);
168
    }
169
170
    /**
171
     * Convert this grid reference into a latitude and longitude.
172
     *
173
     * @return LatLng
174
     */
175 3
    public function toLatLng(): LatLng
176
    {
177 3
        $N = $this->y;
178 3
        $E = $this->x;
179 3
        $N0 = $this->getOriginNorthing();
180 3
        $E0 = $this->getOriginEasting();
181 3
        $phi0 = $this->getOriginLatitude();
182 3
        $lambda0 = $this->getOriginLongitude();
183
184 3
        return $this->convertToLatitudeLongitude($N, $E, $N0, $E0, $phi0, $lambda0);
185
    }
186
187
    /**
188
     * String version of coordinate.
189
     *
190
     * @return string
191
     */
192 10
    public function __toString(): string
193
    {
194 10
        return "({$this->x}, {$this->y})";
195
    }
196
}
197