-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgridref.js
121 lines (106 loc) · 3.25 KB
/
gridref.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* Converts OSGB grid reference (`'SU387148'`) to bounding box in
* British National Grid (`[438700, 114800, 438800, 114900]`)
*
* @param {String} gridref Standard format OSGB grid reference (e.g. SU387148)
* @returns {Array<Number>} Bounding box of grid reference in metres
*/
function parse(gridref) {
// Must be a string
if (typeof gridref !== 'string') {
throw new InvalidGridRef('Grid reference must be a string');
}
// Strip out any whitespace
gridref = gridref.replace(/ /g, '').toUpperCase();
// Must be at least 2 characters long
if (gridref.length < 2) {
throw new InvalidGridRef(
'Grid reference must be at least 2 characters long'
);
}
// Must contain an even number of characters
if (gridref.length % 2) {
throw new InvalidGridRef(
'Grid reference must contain an even number of characters'
);
}
// First two characters must be A-H J-Z
if (!/^[A-HJ-Z]{2}/.test(gridref)) {
throw new InvalidGridRef('First two characters must be A-H or J-Z');
}
// Any remaining characters must be numeric
if (gridref.length > 2) {
if (!/^\d+$/.test(gridref.substr(2))) {
throw new InvalidGridRef('All characters after the grid letters must be numeric');
}
}
// Get numeric values of letter references, mapping A->0, B->1, C->2, etc:
var l1 = gridref.charCodeAt(0) - 'A'.charCodeAt(0);
var l2 = gridref.charCodeAt(1) - 'A'.charCodeAt(0);
// shuffle down letters after 'I' since 'I' is not used in grid:
if (l1 > 7) l1--;
if (l2 > 7) l2--;
// convert grid letters into 100km-square indexes from false origin (grid square SV):
var e = ((l1 - 2) % 5) * 5 + (l2 % 5);
var n = 19 - Math.floor(l1 / 5) * 5 - Math.floor(l2 / 5);
if (e < 0 || e > 6 || n < 0 || n > 12) {
throw new InvalidGridRef('Out of bounds');
}
// skip grid letters to get numeric part of ref, stripping any spaces:
gridref = gridref.slice(2);
// append numeric part of references to grid index:
e += gridref.slice(0, gridref.length / 2);
n += gridref.slice(gridref.length / 2);
var x2, y2;
// normalise to 1m grid, rounding up to centre of grid square:
// TODO Clean this up...
switch (gridref.length) {
case 0:
e = parseInt(e + '00000', 10);
n = parseInt(n + '00000', 10);
x2 = e + 100000;
y2 = n + 100000;
break;
case 2:
e = parseInt(e + '0000', 10);
n = parseInt(n + '0000', 10);
x2 = e + 10000;
y2 = n + 10000;
break;
case 4:
e = parseInt(e + '000', 10);
n = parseInt(n + '000', 10);
x2 = e + 1000;
y2 = n + 1000;
break;
case 6:
e = parseInt(e + '00', 10);
n = parseInt(n + '00', 10);
x2 = e + 100;
y2 = n + 100;
break;
case 8:
e = parseInt(e + '0', 10);
n = parseInt(n + '0', 10);
x2 = e + 10;
y2 = n + 10;
break;
case 10:
e = parseInt(e, 10);
n = parseInt(n, 10);
x2 = e + 1;
y2 = n + 1;
break;
default:
// TODO Should we support sub-meter grid references?
throw new InvalidGridRef('Too many digits');
}
return [e, n, x2, y2];
}
class InvalidGridRef extends Error {
constructor(message) {
super(message);
this.name = 'InvalidGridRef';
}
}
export { parse, InvalidGridRef };