Passed
Push — master ( b9eab5...b4d4f4 )
by Doug
20:40 queued 03:54
created

IrishGridPoint::gridReference()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 3.0123

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 17
nc 3
nop 1
dl 0
loc 27
ccs 16
cts 18
cp 0.8889
crap 3.0123
rs 9.7
c 2
b 0
f 0
1
<?php
2
/**
3
 * PHPCoord.
4
 *
5
 * @author Doug Wright
6
 */
7
declare(strict_types=1);
8
9
namespace PHPCoord\Point;
10
11
use DateTimeInterface;
12
use PHPCoord\CoordinateReferenceSystem\Projected;
13
use PHPCoord\Exception\InvalidCoordinateException;
14
use PHPCoord\UnitOfMeasure\Length\Length;
15
use PHPCoord\UnitOfMeasure\Length\Metre;
16
17
use function floor;
18
use function implode;
19
use function str_pad;
20
use function str_replace;
21
use function strlen;
22
use function strpos;
23
use function substr;
24
25
use const STR_PAD_LEFT;
26
27
/**
28
 * N.B. This is the older 1975 system, not the current ITM system for which @see IrishTransverseMercatorPoint.
29
 */
30
class IrishGridPoint extends ProjectedPoint
31
{
32
    private const GRID_LETTERS = 'VWXYZQRSTULMNOPFGHJKABCDE';
33
34 81
    public function __construct(Length $easting, Length $northing, ?DateTimeInterface $epoch = null)
35
    {
36 81
        parent::__construct(Projected::fromSRID(Projected::EPSG_TM75_IRISH_GRID), $easting, $northing, null, null, $epoch, null);
37
    }
38
39
    /**
40
     * @param string $reference Irish grid reference (e.g. "T514131")
41
     */
42 27
    public static function fromGridReference(string $reference, ?DateTimeInterface $epoch = null): self
43
    {
44 27
        $reference = str_replace(' ', '', $reference);
45
46 27
        if (strlen($reference) % 2 === 0) {
47
            throw new InvalidCoordinateException('Grid ref must be an even number of characters');
48
        }
49
50
        // Letter is 100km grid sq, origin at 0,0 of this square
51 27
        $minorEasting = strpos(self::GRID_LETTERS, $reference[0]) % 5 * 100000;
52 27
        $minorNorthing = floor(strpos(self::GRID_LETTERS, $reference[0]) / 5) * 100000;
53
54
        // numbers are a division of that square into smaller and smaller pieces
55 27
        $numericPortion = substr($reference, 1);
56 27
        $numericPortionSize = strlen($numericPortion) / 2;
57 27
        $gridSizeInMetres = 1 * (10 ** (5 - $numericPortionSize));
58
59 27
        $easting = $minorEasting + ((int) substr($numericPortion, 0, (int) $numericPortionSize) * $gridSizeInMetres);
60 27
        $northing = $minorNorthing + ((int) substr($numericPortion, -(int) $numericPortionSize, (int) $numericPortionSize) * $gridSizeInMetres);
61
62 27
        return new self(new Metre($easting), new Metre($northing), $epoch);
63
    }
64
65
    /**
66
     * Grid reference without spaces. e.g. T514131.
67
     */
68 9
    public function asGridReference(int $length): string
69
    {
70 9
        return implode('', $this->gridReference($length));
71
    }
72
73
    /**
74
     * Grid reference with spaces. e.g. T 514 131.
75
     */
76 18
    public function asGridReferenceWithSpaces(int $length): string
77
    {
78 18
        return implode(' ', $this->gridReference($length));
79
    }
80
81
    /**
82
     * Convert this grid reference into a grid reference string of a
83
     * given length (2, 4, 6, 8 or 10) including the character
84
     * designation for the 100km square. e.g. T514131.
85
     *
86
     * @return array{0: string, 1: string, 2: string}
87
     */
88 27
    protected function gridReference(int $length): array
89
    {
90 27
        if ($length % 2 !== 0) {
91
            throw new InvalidCoordinateException('Chosen length must be an even number');
92
        }
93
94 27
        if ($length > 10) {
95
            throw new InvalidCoordinateException('Grid squares are 100,000m wide, max precision is 10');
96
        }
97
98 27
        $halfLength = $length / 2;
99
100 27
        $x = $this->easting->asMetres()->getValue();
101 27
        $y = $this->northing->asMetres()->getValue();
102 27
        $easting = str_pad((string) (int) $x, $halfLength + 1, '0', STR_PAD_LEFT);
103 27
        $northing = str_pad((string) (int) $y, $halfLength + 1, '0', STR_PAD_LEFT);
104
105
        // second (minor) letter is 100km grid sq, origin at 0,0 of this square
106 27
        $minorSquaresEast = (int) $easting[0] % 5;
107 27
        $minorSquaresNorth = (int) $northing[0] % 5;
108 27
        $minorLetterIndex = (5 * $minorSquaresNorth + $minorSquaresEast);
109 27
        $minorLetter = substr(self::GRID_LETTERS, $minorLetterIndex, 1);
110
111 27
        return [
112 27
            $minorLetter,
113 27
            substr($easting, 1, $halfLength),
114 27
            substr($northing, 1, $halfLength),
115 27
        ];
116
    }
117
}
118