Completed
Pull Request — master (#22)
by Thomas
03:25
created

OSRef::toGridReferenceWithSpaces()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
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
23
    /**
24
     * @return RefEll
25
     */
26 4
    public function getReferenceEllipsoid(): RefEll
27
    {
28 4
        return RefEll::airy1830();
29
    }
30
31
    /**
32
     * @return float
33
     */
34 5
    public function getScaleFactor(): float
35
    {
36 5
        return 0.9996012717;
37
    }
38
39
    /**
40
     * @return int
41
     */
42 5
    public function getOriginNorthing(): int
43
    {
44 5
        return -100000;
45
    }
46
47
    /**
48
     * @return int
49
     */
50 5
    public function getOriginEasting(): int
51
    {
52 5
        return 400000;
53
    }
54
55
    /**
56
     * @return float
57
     */
58 5
    public function getOriginLatitude(): float
59
    {
60 5
        return 49;
61
    }
62
63
    /**
64
     * @return float
65
     */
66 5
    public function getOriginLongitude(): float
67
    {
68 5
        return -2;
69
    }
70
71
    /**
72
     * Create a new object representing a OSGB reference.
73
     *
74
     * @param int $x
75
     * @param int $y
76
     * @param int $z
77
     */
78 22
    public function __construct($x, $y, $z = 0)
79
    {
80 22
        parent::__construct($x, $y, $z, RefEll::airy1830());
81 22
    }
82
83
    /**
84
     * Take a string formatted as a OS grid reference (e.g.
85
     * "TG514131") and return a reference to an OSRef object that represents
86
     * that grid reference.
87
     *
88
     * @param string $ref
89
     *
90
     * @return static
91
     */
92 7
    public static function fromGridReference(string $ref): self
93
    {
94 7
        if (strlen($ref) % 2 !== 0) {
95 1
            throw new LengthException('Grid ref must be an even number of characters');
96
        }
97
98
        //first (major) letter is the 500km grid sq, origin at -1000000, -500000
99 6
        $majorEasting = strpos(self::GRID_LETTERS, $ref[0]) % 5 * 500000 - 1000000;
100 6
        $majorNorthing = (floor(strpos(self::GRID_LETTERS, $ref[0]) / 5)) * 500000 - 500000;
101
102
        //second (minor) letter is 100km grid sq, origin at 0,0 of this square
103 6
        $minorEasting = strpos(self::GRID_LETTERS, $ref[1]) % 5 * 100000;
104 6
        $minorNorthing = (floor(strpos(self::GRID_LETTERS, $ref[1]) / 5)) * 100000;
105
106
        //numbers are a division of that square into smaller and smaller pieces
107 6
        $numericPortion = substr($ref, 2);
108 6
        $numericPortionSize = strlen($numericPortion) / 2;
109 6
        $gridSizeInMetres = 1 * (10 ** (5 - $numericPortionSize));
110
111 6
        $easting = $majorEasting + $minorEasting + (substr($numericPortion, 0, $numericPortionSize) * $gridSizeInMetres);
112 6
        $northing = $majorNorthing + $minorNorthing + (substr($numericPortion, -$numericPortionSize, $numericPortionSize) * $gridSizeInMetres);
113
114 6
        return new static((int) $easting, (int) $northing);
115
    }
116
117
    /**
118
     * Grid reference without spaces. e.g. TG514131.
119
     *
120
     * @param int $length
121
     *
122
     * @return string
123
     */
124 8
    public function toGridReference(int $length): string
125
    {
126 8
        return implode('', $this->gridReference($length));
127
    }
128
    
129
    /**
130
     * Grid reference with spaces. e.g. TG 514 131.
131
     *
132
     * @param int $length
133
     *
134
     * @return string
135
     */
136
    public function toGridReferenceWithSpaces(int $length): string
137
    {
138
        return implode(' ', $this->gridReference($length));
139
    }
140
141
    /**
142
     * Convert this grid reference into a grid reference string of a
143
     * given length (2, 4, 6, 8 or 10) including the two-character
144
     * designation for the 100km square. e.g. TG514131.
145
     *
146
     * @param int $length
147
     *
148
     * @return string
149
     */
150 8
    public function gridReference(int $length): array
151
    {
152 8
        if ($length % 2 !== 0) {
153 1
            throw new LengthException('Chosen length must be an even number');
154
        }
155
156 7
        $halfLength = $length / 2;
157
158 7
        $easting = str_pad((string) $this->x, 6, '0', STR_PAD_LEFT);
159 7
        $northing = str_pad((string) $this->y, 6, '0', STR_PAD_LEFT);
160
161 7
        $adjustedX = $this->x + 1000000;
162 7
        $adjustedY = $this->y + 500000;
163 7
        $majorSquaresEast = floor($adjustedX / 500000);
164 7
        $majorSquaresNorth = floor($adjustedY / 500000);
165 7
        $majorLetterIndex = (int) (5 * $majorSquaresNorth + $majorSquaresEast);
166 7
        $majorLetter = substr(self::GRID_LETTERS, $majorLetterIndex, 1);
167
168
        //second (minor) letter is 100km grid sq, origin at 0,0 of this square
169 7
        $minorSquaresEast = $easting[0] % 5;
170 7
        $minorSquaresNorth = $northing[0] % 5;
171 7
        $minorLetterIndex = (5 * $minorSquaresNorth + $minorSquaresEast);
172 7
        $minorLetter = substr(self::GRID_LETTERS, $minorLetterIndex, 1);
173
174
        return [
175 7
            $majorLetter . $minorLetter,
176 7
            substr($easting, 1, $halfLength),
177 7
            substr($northing, 1, $halfLength),
178
        ];
179
    }
180
181
    /**
182
     * Convert this grid reference into a latitude and longitude.
183
     *
184
     * @return LatLng
185
     */
186 3
    public function toLatLng(): LatLng
187
    {
188 3
        $N = $this->y;
189 3
        $E = $this->x;
190 3
        $N0 = $this->getOriginNorthing();
191 3
        $E0 = $this->getOriginEasting();
192 3
        $phi0 = $this->getOriginLatitude();
193 3
        $lambda0 = $this->getOriginLongitude();
194
195 3
        return $this->convertToLatitudeLongitude($N, $E, $N0, $E0, $phi0, $lambda0);
196
    }
197
198
    /**
199
     * String version of coordinate.
200
     *
201
     * @return string
202
     */
203 9
    public function __toString(): string
204
    {
205 9
        return "({$this->x}, {$this->y})";
206
    }
207
}
208