@flumens/bigu
Version:
British Isles GridRef Utils
382 lines (327 loc) • 11.6 kB
JavaScript
import GridRefParser from './GridRefParser';
import OSRef from '../OSRef';
/**
* @constructor
*/
let GridRefParserGB = function() {};
GridRefParserGB.prototype = new GridRefParser();
GridRefParserGB.prototype.constructor = GridRefParserGB;
GridRefParserGB.prototype.country = 'GB';
GridRefParserGB.prototype.NationalRef = OSRef;
/**
* gridref known to have correct syntax
* may have tetrad or quadrant suffix
*
* @param {string} rawGridRef
* @throws GridRefException
*/
GridRefParserGB.prototype.parse_well_formed = function(rawGridRef) {
if (rawGridRef.length >= 5 && /^[A-Z]/.test(rawGridRef.charAt(4))) {
// tetrad or quadrant
if (GridRefParser.quadrantOffsets.hasOwnProperty(rawGridRef.substr(rawGridRef.length - 2))) {
this.quadrantCode = rawGridRef.substr(rawGridRef.length - 2);
} else {
this.tetradLetter = rawGridRef.charAt(4);
}
rawGridRef = rawGridRef.substr(0, 4);
}
//this sets easting/northing, length and hectad
this.parse_wellformed_gb_gr_string_no_tetrads(rawGridRef);
if (this.tetradLetter || this.quadrantCode) {
// tetrad or quadrant suffix
if (this.tetradLetter) {
this.preciseGridRef = this.tetrad = this.hectad + this.tetradLetter;
this.length = 2000; // 2km square
this.osRef.x += GridRefParser.tetradOffsets[this.tetradLetter][0];
this.osRef.y += GridRefParser.tetradOffsets[this.tetradLetter][1];
} else {
// quadrant
this.preciseGridRef = this.quadrant = rawGridRef + this.quadrantCode;
this.length = 5000; // 5km square
this.osRef.x += GridRefParser.quadrantOffsets[this.quadrantCode][0];
this.osRef.y += GridRefParser.quadrantOffsets[this.quadrantCode][1];
}
} else {
this.preciseGridRef = rawGridRef;
if (this.length <= 1000) {
// calculate tetrad for precise gridref
this.set_tetrad();
}
}
};
/**
*
* @param {string} rawGridRef
* @throws Error
*/
GridRefParserGB.prototype.parse = function(rawGridRef) {
// grid ref may not be in canonical format
var trimmedLocality = rawGridRef.replace(/[\[\]\s\t\.-]+/g, '').toUpperCase();
var tetradCode = '';
var ref;
if (/[ABCDEFGHIJKLMNPQRSTUVWXYZ]$/.test(trimmedLocality)) {
// tetrad or quadrant
if (GridRefParser.quadrantOffsets.hasOwnProperty(trimmedLocality.substr(trimmedLocality.length - 2))) {
this.quadrantCode = trimmedLocality.substr(trimmedLocality.length - 2);
trimmedLocality = trimmedLocality.substr(0, trimmedLocality.length - 2);
} else {
tetradCode = trimmedLocality.substr(trimmedLocality.length - 1);
trimmedLocality = trimmedLocality.substr(0, trimmedLocality.length - 1);
}
}
// if all numeric gridref, e.g. 38517462 then
// split with '/', i.e. 38/517462
if (trimmedLocality === parseInt(trimmedLocality, 10).toString()) {
trimmedLocality = trimmedLocality.substr(0, 2) + '/' + trimmedLocality.substr(2);
} else if (trimmedLocality.length > 3 && trimmedLocality.charAt(2) === '/' && /^[A-Z]{2}$/.test(trimmedLocality.substr(0, 2))) {
// preprocess refs of form SD/59 to SD59
// but at this stage want to retain old-style nn/nnnn gridrefs
trimmedLocality = trimmedLocality.replace('/', '');
}
if (trimmedLocality.substr(0, 2) === 'VC') {
// special case error, VC number entered in the wrong field
this.error = true;
this.errorMessage = "Misplaced vice-county code in grid-reference field. ('" + trimmedLocality + "')";
this.osRef = null;
this.length = 0;
} else if ((ref = trimmedLocality.match(/^([A-Z]{2}(?:\d\d){1,5})$/)) !== null) {
trimmedLocality = ref[0]; //grid reference
//this.parse_gr_string_without_tetrads(trimmedLocality);
this.parse_wellformed_gb_gr_string_no_tetrads(trimmedLocality);
if (this.length > 0) {
//this.hectad = this.osRef.to_gridref(10000);
if (this.length === 10000 && (tetradCode || this.quadrantCode)) {
// tetrad or quadrant suffix
if (tetradCode) {
this.preciseGridRef = trimmedLocality + tetradCode;
this.tetradLetter = tetradCode;
this.tetrad = this.hectad + tetradCode;
this.length = 2000; // 2km square
this.osRef.x += GridRefParser.tetradKmOffsets[tetradCode][0];
this.osRef.y += GridRefParser.tetradKmOffsets[tetradCode][1];
} else {
// quadrant
this.preciseGridRef = trimmedLocality + this.quadrantCode;
this.tetradLetter = '';
this.tetrad = '';
this.quadrant = this.preciseGridRef;
this.length = 5000; // 5km square
this.osRef.x += GridRefParser.quadrantOffsets[this.quadrantCode][0];
this.osRef.y += GridRefParser.quadrantOffsets[this.quadrantCode][1];
}
} else {
this.preciseGridRef = trimmedLocality;
if (this.length <= 1000) {
// calculate tetrad for precise gridref
this.set_tetrad();
}
}
} else {
this.error = true;
this.errorMessage = 'GB grid reference format not understood (strange length).';
}
} else if (/^([\d]{2})\/((?:\d\d){1,5})$/.test(trimmedLocality)) {
// matching old-style nn/nnnn gridrefs
// where second-part must have even-number of digits
this.parse_gr_string_without_tetrads(trimmedLocality);
switch (this.length) {
case 10000:
trimmedLocality = this.osRef.to_gridref(10000);
this.hectad = trimmedLocality;
if (tetradCode) {
trimmedLocality += tetradCode;
this.tetradLetter = tetradCode;
this.tetrad = this.hectad + tetradCode;
this.length = 2000; // 2km square
this.osRef.x += GridRefParser.tetradOffsets[tetradCode][0];
this.osRef.y += GridRefParser.tetradOffsets[tetradCode][1];
} else if (this.quadrantCode) {
trimmedLocality += this.quadrantCode;
this.quadrant = trimmedLocality;
this.length = 5000; // 5km square
this.osRef.x += GridRefParser.quadrantOffsets[this.quadrantCode][0];
this.osRef.y += GridRefParser.quadrantOffsets[this.quadrantCode][1];
}
break;
case 1000:
case 100:
case 10:
case 1:
trimmedLocality = this.osRef.to_gridref(this.length);
this.hectad = this.osRef.to_gridref(10000);
this.set_tetrad();
break;
default:
this.error = true;
this.errorMessage = 'Bad grid square dimension (' + this.length + ' m).';
this.osRef = null;
this.length = 0;
}
this.preciseGridRef = trimmedLocality;
} else {
// no match
this.osRef = null;
this.length = 0;
this.error = true;
this.errorMessage = "Grid reference format not understood. ('" + rawGridRef + "')";
}
};
/**
* sets easting, northing and length (in km)
* source grid-reference need not be well-formed
*
* @param {string} gridRef either nn/nn... or aann...
*/
GridRefParserGB.prototype.parse_gr_string_without_tetrads = function(gridRef) {
var matches, x, y, ref;
if ((matches = gridRef.match(/^(\d{2})\/((?:\d\d){1,5})$/)) !== null) {
// old style numerical sheet ref XY/nnnnnn
// nnnn part must have even length
// northern scottish islands have eccentric numbering
switch (matches[1]) {
case '57':
x = 300000;
y = 1000000;
break;
case '67':
x = 400000;
y = 1000000;
break;
case '58':
x = 300000;
y = 1100000;
break;
case '68':
x = 400000;
y = 1100000;
break;
case '69':
x = 400000;
y = 1200000;
break;
default:
x = gridRef.charAt(0) * 100000;
y = gridRef.charAt(1) * 100000;
}
ref = matches[2];
} else {
// modern alphabetical sheet ref
if (!GridRefParser.letterMapping.hasOwnProperty(gridRef.charAt(0)) || !GridRefParser.letterMapping.hasOwnProperty(gridRef.charAt(1))) {
// invalid
this.length = 0;
this.osRef = null;
return;
}
var char1 = GridRefParser.letterMapping[gridRef.charAt(0)];
var char2 = GridRefParser.letterMapping[gridRef.charAt(1)];
ref = gridRef.substr(2);
x = ((char1 % 5) * 500000) + ((char2 % 5) * 100000) - 1000000;
y = (-Math.floor(char1 / 5) * 500000) - (Math.floor(char2 / 5) * 100000) + 1900000;
}
switch (ref.length) {
case 2:
this.osRef = new OSRef(
x + ref.charAt(0) * 10000, // use first digit of ref
y + ref.charAt(1) * 10000 // use second digit of ref
);
this.length = 10000; //10 km square
break;
case 4:
this.osRef = new OSRef(
x + Math.floor(ref / 100) * 1000,
y + (ref % 100) * 1000
);
this.length = 1000; //1 km square
break;
case 6:
this.osRef = new OSRef(
x + Math.floor(ref / 1000) * 100,
y + (ref % 1000) * 100
);
this.length = 100; //100m square
break;
case 8:
this.osRef = new OSRef(
x + Math.floor(ref / 10000) * 10,
y + (ref % 10000) * 10
);
this.length = 10; //10m square
break;
case 10:
this.osRef = new OSRef(
x + Math.floor(ref / 100000),
y + (ref % 100000)
);
this.length = 1; //1m square
break;
default:
Logger('Bad grid ref length, ref=' + gridRef);
this.osRef = null;
this.length = 0;
}
};
/**
* gridRef must be a correctly formed OS GB gridref
*
* sets self::osRef
* sets self::length
* sets self::hectad
*
* @param {string} gridRef modern alpha-numeric format with no suffixes
* @throws Error
*/
GridRefParserGB.prototype.parse_wellformed_gb_gr_string_no_tetrads = function(gridRef) {
var char1, char2, ref, x, y;
// modern alphabetical sheet refs only
char1 = GridRefParser.letterMapping[gridRef.charAt(0)];
char2 = GridRefParser.letterMapping[gridRef.charAt(1)];
ref = gridRef.substr(2);
x = ((char1 % 5) * 500000) + ((char2 % 5) * 100000) - 1000000;
y = (-Math.floor(char1 / 5) * 500000) - (Math.floor(char2 / 5) * 100000) + 1900000;
switch (ref.length) {
case 2:
this.osRef = new OSRef(
x + ref.charAt(0) * 10000, // use first digit of ref
y + ref.charAt(1) * 10000 // use second digit of ref
);
this.length = 10000; //10 km square
this.hectad = gridRef;
break;
case 4:
this.osRef = new OSRef(
x + (Math.floor(ref / 100) * 1000),
y + ((ref % 100) * 1000)
);
this.length = 1000; //1 km square
this.hectad = gridRef.substr(0, 3) + gridRef.substr(4, 1);
break;
case 6:
this.osRef = new OSRef(
x + (Math.floor(ref / 1000)) * 100,
y + (ref % 1000) * 100
);
this.length = 100; //100m square
this.hectad = gridRef.substr(0, 3) + gridRef.substr(5, 1);
break;
case 8:
this.osRef = new OSRef(
x + (Math.floor(ref / 10000)) * 10,
y + (ref % 10000) * 10
);
this.length = 10; //10m square
this.hectad = gridRef.substr(0, 3) + gridRef.substr(6, 1);
break;
case 10:
this.osRef = new OSRef(
x + Math.floor(ref / 100000),
y + (ref % 100000)
);
this.length = 1; //1m square
this.hectad = gridRef.substr(0, 3) + gridRef.substr(7, 1);;
break;
default:
this.osRef = null;
throw new Error("Bad grid ref length when parsing supposedly well-formed ref, ref='" + gridRef + "'");
}
};
export default GridRefParserGB;