Passed
Push — master ( 529ab0...0df16e )
by Doug
92:30 queued 27:53
created

IrishGridPoint::asGridReferenceWithSpaces()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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