485 lines
12 KiB
JavaScript
485 lines
12 KiB
JavaScript
'use strict';
|
|
|
|
var NEUTRAL = 1;
|
|
var KEYWORD = 2;
|
|
var NUMBER = 3;
|
|
var QUOTED = 4;
|
|
var AFTERQUOTE = 5;
|
|
var ENDED = -1;
|
|
var whitespace = /\s/;
|
|
var latin = /[A-Za-z]/;
|
|
var keyword = /[A-Za-z84_]/;
|
|
var endThings = /[,\]]/;
|
|
var digets = /[\d\.E\-\+]/;
|
|
// const ignoredChar = /[\s_\-\/\(\)]/g;
|
|
function Parser(text) {
|
|
if (typeof text !== 'string') {
|
|
throw new Error('not a string');
|
|
}
|
|
this.text = text.trim();
|
|
this.level = 0;
|
|
this.place = 0;
|
|
this.root = null;
|
|
this.stack = [];
|
|
this.currentObject = null;
|
|
this.state = NEUTRAL;
|
|
}
|
|
Parser.prototype.readCharicter = function() {
|
|
var char = this.text[this.place++];
|
|
if (this.state !== QUOTED) {
|
|
while (whitespace.test(char)) {
|
|
if (this.place >= this.text.length) {
|
|
return;
|
|
}
|
|
char = this.text[this.place++];
|
|
}
|
|
}
|
|
switch (this.state) {
|
|
case NEUTRAL:
|
|
return this.neutral(char);
|
|
case KEYWORD:
|
|
return this.keyword(char)
|
|
case QUOTED:
|
|
return this.quoted(char);
|
|
case AFTERQUOTE:
|
|
return this.afterquote(char);
|
|
case NUMBER:
|
|
return this.number(char);
|
|
case ENDED:
|
|
return;
|
|
}
|
|
};
|
|
Parser.prototype.afterquote = function(char) {
|
|
if (char === '"') {
|
|
this.word += '"';
|
|
this.state = QUOTED;
|
|
return;
|
|
}
|
|
if (endThings.test(char)) {
|
|
this.word = this.word.trim();
|
|
this.afterItem(char);
|
|
return;
|
|
}
|
|
throw new Error('havn\'t handled "' +char + '" in afterquote yet, index ' + this.place);
|
|
};
|
|
Parser.prototype.afterItem = function(char) {
|
|
if (char === ',') {
|
|
if (this.word !== null) {
|
|
this.currentObject.push(this.word);
|
|
}
|
|
this.word = null;
|
|
this.state = NEUTRAL;
|
|
return;
|
|
}
|
|
if (char === ']') {
|
|
this.level--;
|
|
if (this.word !== null) {
|
|
this.currentObject.push(this.word);
|
|
this.word = null;
|
|
}
|
|
this.state = NEUTRAL;
|
|
this.currentObject = this.stack.pop();
|
|
if (!this.currentObject) {
|
|
this.state = ENDED;
|
|
}
|
|
|
|
return;
|
|
}
|
|
};
|
|
Parser.prototype.number = function(char) {
|
|
if (digets.test(char)) {
|
|
this.word += char;
|
|
return;
|
|
}
|
|
if (endThings.test(char)) {
|
|
this.word = parseFloat(this.word);
|
|
this.afterItem(char);
|
|
return;
|
|
}
|
|
throw new Error('havn\'t handled "' +char + '" in number yet, index ' + this.place);
|
|
};
|
|
Parser.prototype.quoted = function(char) {
|
|
if (char === '"') {
|
|
this.state = AFTERQUOTE;
|
|
return;
|
|
}
|
|
this.word += char;
|
|
return;
|
|
};
|
|
Parser.prototype.keyword = function(char) {
|
|
if (keyword.test(char)) {
|
|
this.word += char;
|
|
return;
|
|
}
|
|
if (char === '[') {
|
|
var newObjects = [];
|
|
newObjects.push(this.word);
|
|
this.level++;
|
|
if (this.root === null) {
|
|
this.root = newObjects;
|
|
} else {
|
|
this.currentObject.push(newObjects);
|
|
}
|
|
this.stack.push(this.currentObject);
|
|
this.currentObject = newObjects;
|
|
this.state = NEUTRAL;
|
|
return;
|
|
}
|
|
if (endThings.test(char)) {
|
|
this.afterItem(char);
|
|
return;
|
|
}
|
|
throw new Error('havn\'t handled "' +char + '" in keyword yet, index ' + this.place);
|
|
};
|
|
Parser.prototype.neutral = function(char) {
|
|
if (latin.test(char)) {
|
|
this.word = char;
|
|
this.state = KEYWORD;
|
|
return;
|
|
}
|
|
if (char === '"') {
|
|
this.word = '';
|
|
this.state = QUOTED;
|
|
return;
|
|
}
|
|
if (digets.test(char)) {
|
|
this.word = char;
|
|
this.state = NUMBER;
|
|
return;
|
|
}
|
|
if (endThings.test(char)) {
|
|
this.afterItem(char);
|
|
return;
|
|
}
|
|
throw new Error('havn\'t handled "' +char + '" in neutral yet, index ' + this.place);
|
|
};
|
|
Parser.prototype.output = function() {
|
|
while (this.place < this.text.length) {
|
|
this.readCharicter();
|
|
}
|
|
if (this.state === ENDED) {
|
|
return this.root;
|
|
}
|
|
throw new Error('unable to parse string "' +this.text + '". State is ' + this.state);
|
|
};
|
|
|
|
function parseString(txt) {
|
|
var parser = new Parser(txt);
|
|
return parser.output();
|
|
}
|
|
|
|
function mapit(obj, key, value) {
|
|
if (Array.isArray(key)) {
|
|
value.unshift(key);
|
|
key = null;
|
|
}
|
|
var thing = key ? {} : obj;
|
|
|
|
var out = value.reduce(function(newObj, item) {
|
|
sExpr(item, newObj);
|
|
return newObj
|
|
}, thing);
|
|
if (key) {
|
|
obj[key] = out;
|
|
}
|
|
}
|
|
|
|
function sExpr(v, obj) {
|
|
if (!Array.isArray(v)) {
|
|
obj[v] = true;
|
|
return;
|
|
}
|
|
var key = v.shift();
|
|
if (key === 'PARAMETER') {
|
|
key = v.shift();
|
|
}
|
|
if (v.length === 1) {
|
|
if (Array.isArray(v[0])) {
|
|
obj[key] = {};
|
|
sExpr(v[0], obj[key]);
|
|
return;
|
|
}
|
|
obj[key] = v[0];
|
|
return;
|
|
}
|
|
if (!v.length) {
|
|
obj[key] = true;
|
|
return;
|
|
}
|
|
if (key === 'TOWGS84') {
|
|
obj[key] = v;
|
|
return;
|
|
}
|
|
if (key === 'AXIS') {
|
|
if (!(key in obj)) {
|
|
obj[key] = [];
|
|
}
|
|
obj[key].push(v);
|
|
return;
|
|
}
|
|
if (!Array.isArray(key)) {
|
|
obj[key] = {};
|
|
}
|
|
|
|
var i;
|
|
switch (key) {
|
|
case 'UNIT':
|
|
case 'PRIMEM':
|
|
case 'VERT_DATUM':
|
|
obj[key] = {
|
|
name: v[0].toLowerCase(),
|
|
convert: v[1]
|
|
};
|
|
if (v.length === 3) {
|
|
sExpr(v[2], obj[key]);
|
|
}
|
|
return;
|
|
case 'SPHEROID':
|
|
case 'ELLIPSOID':
|
|
obj[key] = {
|
|
name: v[0],
|
|
a: v[1],
|
|
rf: v[2]
|
|
};
|
|
if (v.length === 4) {
|
|
sExpr(v[3], obj[key]);
|
|
}
|
|
return;
|
|
case 'PROJECTEDCRS':
|
|
case 'PROJCRS':
|
|
case 'GEOGCS':
|
|
case 'GEOCCS':
|
|
case 'PROJCS':
|
|
case 'LOCAL_CS':
|
|
case 'GEODCRS':
|
|
case 'GEODETICCRS':
|
|
case 'GEODETICDATUM':
|
|
case 'EDATUM':
|
|
case 'ENGINEERINGDATUM':
|
|
case 'VERT_CS':
|
|
case 'VERTCRS':
|
|
case 'VERTICALCRS':
|
|
case 'COMPD_CS':
|
|
case 'COMPOUNDCRS':
|
|
case 'ENGINEERINGCRS':
|
|
case 'ENGCRS':
|
|
case 'FITTED_CS':
|
|
case 'LOCAL_DATUM':
|
|
case 'DATUM':
|
|
v[0] = ['name', v[0]];
|
|
mapit(obj, key, v);
|
|
return;
|
|
default:
|
|
i = -1;
|
|
while (++i < v.length) {
|
|
if (!Array.isArray(v[i])) {
|
|
return sExpr(v, obj[key]);
|
|
}
|
|
}
|
|
return mapit(obj, key, v);
|
|
}
|
|
}
|
|
|
|
var D2R = 0.01745329251994329577;
|
|
function rename(obj, params) {
|
|
var outName = params[0];
|
|
var inName = params[1];
|
|
if (!(outName in obj) && (inName in obj)) {
|
|
obj[outName] = obj[inName];
|
|
if (params.length === 3) {
|
|
obj[outName] = params[2](obj[outName]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function d2r(input) {
|
|
return input * D2R;
|
|
}
|
|
|
|
function cleanWKT(wkt) {
|
|
if (wkt.type === 'GEOGCS') {
|
|
wkt.projName = 'longlat';
|
|
} else if (wkt.type === 'LOCAL_CS') {
|
|
wkt.projName = 'identity';
|
|
wkt.local = true;
|
|
} else {
|
|
if (typeof wkt.PROJECTION === 'object') {
|
|
wkt.projName = Object.keys(wkt.PROJECTION)[0];
|
|
} else {
|
|
wkt.projName = wkt.PROJECTION;
|
|
}
|
|
}
|
|
if (wkt.AXIS) {
|
|
var axisOrder = '';
|
|
for (var i = 0, ii = wkt.AXIS.length; i < ii; ++i) {
|
|
var axis = [wkt.AXIS[i][0].toLowerCase(), wkt.AXIS[i][1].toLowerCase()];
|
|
if (axis[0].indexOf('north') !== -1 || ((axis[0] === 'y' || axis[0] === 'lat') && axis[1] === 'north')) {
|
|
axisOrder += 'n';
|
|
} else if (axis[0].indexOf('south') !== -1 || ((axis[0] === 'y' || axis[0] === 'lat') && axis[1] === 'south')) {
|
|
axisOrder += 's';
|
|
} else if (axis[0].indexOf('east') !== -1 || ((axis[0] === 'x' || axis[0] === 'lon') && axis[1] === 'east')) {
|
|
axisOrder += 'e';
|
|
} else if (axis[0].indexOf('west') !== -1 || ((axis[0] === 'x' || axis[0] === 'lon') && axis[1] === 'west')) {
|
|
axisOrder += 'w';
|
|
}
|
|
}
|
|
if (axisOrder.length === 2) {
|
|
axisOrder += 'u';
|
|
}
|
|
if (axisOrder.length === 3) {
|
|
wkt.axis = axisOrder;
|
|
}
|
|
}
|
|
if (wkt.UNIT) {
|
|
wkt.units = wkt.UNIT.name.toLowerCase();
|
|
if (wkt.units === 'metre') {
|
|
wkt.units = 'meter';
|
|
}
|
|
if (wkt.UNIT.convert) {
|
|
if (wkt.type === 'GEOGCS') {
|
|
if (wkt.DATUM && wkt.DATUM.SPHEROID) {
|
|
wkt.to_meter = wkt.UNIT.convert*wkt.DATUM.SPHEROID.a;
|
|
}
|
|
} else {
|
|
wkt.to_meter = wkt.UNIT.convert;
|
|
}
|
|
}
|
|
}
|
|
var geogcs = wkt.GEOGCS;
|
|
if (wkt.type === 'GEOGCS') {
|
|
geogcs = wkt;
|
|
}
|
|
if (geogcs) {
|
|
//if(wkt.GEOGCS.PRIMEM&&wkt.GEOGCS.PRIMEM.convert){
|
|
// wkt.from_greenwich=wkt.GEOGCS.PRIMEM.convert*D2R;
|
|
//}
|
|
if (geogcs.DATUM) {
|
|
wkt.datumCode = geogcs.DATUM.name.toLowerCase();
|
|
} else {
|
|
wkt.datumCode = geogcs.name.toLowerCase();
|
|
}
|
|
if (wkt.datumCode.slice(0, 2) === 'd_') {
|
|
wkt.datumCode = wkt.datumCode.slice(2);
|
|
}
|
|
if (wkt.datumCode === 'new_zealand_geodetic_datum_1949' || wkt.datumCode === 'new_zealand_1949') {
|
|
wkt.datumCode = 'nzgd49';
|
|
}
|
|
if (wkt.datumCode === 'wgs_1984' || wkt.datumCode === 'world_geodetic_system_1984') {
|
|
if (wkt.PROJECTION === 'Mercator_Auxiliary_Sphere') {
|
|
wkt.sphere = true;
|
|
}
|
|
wkt.datumCode = 'wgs84';
|
|
}
|
|
if (wkt.datumCode.slice(-6) === '_ferro') {
|
|
wkt.datumCode = wkt.datumCode.slice(0, - 6);
|
|
}
|
|
if (wkt.datumCode.slice(-8) === '_jakarta') {
|
|
wkt.datumCode = wkt.datumCode.slice(0, - 8);
|
|
}
|
|
if (~wkt.datumCode.indexOf('belge')) {
|
|
wkt.datumCode = 'rnb72';
|
|
}
|
|
if (geogcs.DATUM && geogcs.DATUM.SPHEROID) {
|
|
wkt.ellps = geogcs.DATUM.SPHEROID.name.replace('_19', '').replace(/[Cc]larke\_18/, 'clrk');
|
|
if (wkt.ellps.toLowerCase().slice(0, 13) === 'international') {
|
|
wkt.ellps = 'intl';
|
|
}
|
|
|
|
wkt.a = geogcs.DATUM.SPHEROID.a;
|
|
wkt.rf = parseFloat(geogcs.DATUM.SPHEROID.rf, 10);
|
|
}
|
|
|
|
if (geogcs.DATUM && geogcs.DATUM.TOWGS84) {
|
|
wkt.datum_params = geogcs.DATUM.TOWGS84;
|
|
}
|
|
if (~wkt.datumCode.indexOf('osgb_1936')) {
|
|
wkt.datumCode = 'osgb36';
|
|
}
|
|
if (~wkt.datumCode.indexOf('osni_1952')) {
|
|
wkt.datumCode = 'osni52';
|
|
}
|
|
if (~wkt.datumCode.indexOf('tm65')
|
|
|| ~wkt.datumCode.indexOf('geodetic_datum_of_1965')) {
|
|
wkt.datumCode = 'ire65';
|
|
}
|
|
if (wkt.datumCode === 'ch1903+') {
|
|
wkt.datumCode = 'ch1903';
|
|
}
|
|
if (~wkt.datumCode.indexOf('israel')) {
|
|
wkt.datumCode = 'isr93';
|
|
}
|
|
}
|
|
if (wkt.b && !isFinite(wkt.b)) {
|
|
wkt.b = wkt.a;
|
|
}
|
|
|
|
function toMeter(input) {
|
|
var ratio = wkt.to_meter || 1;
|
|
return input * ratio;
|
|
}
|
|
var renamer = function(a) {
|
|
return rename(wkt, a);
|
|
};
|
|
var list = [
|
|
['standard_parallel_1', 'Standard_Parallel_1'],
|
|
['standard_parallel_1', 'Latitude of 1st standard parallel'],
|
|
['standard_parallel_2', 'Standard_Parallel_2'],
|
|
['standard_parallel_2', 'Latitude of 2nd standard parallel'],
|
|
['false_easting', 'False_Easting'],
|
|
['false_easting', 'False easting'],
|
|
['false-easting', 'Easting at false origin'],
|
|
['false_northing', 'False_Northing'],
|
|
['false_northing', 'False northing'],
|
|
['false_northing', 'Northing at false origin'],
|
|
['central_meridian', 'Central_Meridian'],
|
|
['central_meridian', 'Longitude of natural origin'],
|
|
['central_meridian', 'Longitude of false origin'],
|
|
['latitude_of_origin', 'Latitude_Of_Origin'],
|
|
['latitude_of_origin', 'Central_Parallel'],
|
|
['latitude_of_origin', 'Latitude of natural origin'],
|
|
['latitude_of_origin', 'Latitude of false origin'],
|
|
['scale_factor', 'Scale_Factor'],
|
|
['k0', 'scale_factor'],
|
|
['latitude_of_center', 'Latitude_Of_Center'],
|
|
['latitude_of_center', 'Latitude_of_center'],
|
|
['lat0', 'latitude_of_center', d2r],
|
|
['longitude_of_center', 'Longitude_Of_Center'],
|
|
['longitude_of_center', 'Longitude_of_center'],
|
|
['longc', 'longitude_of_center', d2r],
|
|
['x0', 'false_easting', toMeter],
|
|
['y0', 'false_northing', toMeter],
|
|
['long0', 'central_meridian', d2r],
|
|
['lat0', 'latitude_of_origin', d2r],
|
|
['lat0', 'standard_parallel_1', d2r],
|
|
['lat1', 'standard_parallel_1', d2r],
|
|
['lat2', 'standard_parallel_2', d2r],
|
|
['azimuth', 'Azimuth'],
|
|
['alpha', 'azimuth', d2r],
|
|
['srsCode', 'name']
|
|
];
|
|
list.forEach(renamer);
|
|
if (!wkt.long0 && wkt.longc && (wkt.projName === 'Albers_Conic_Equal_Area' || wkt.projName === 'Lambert_Azimuthal_Equal_Area')) {
|
|
wkt.long0 = wkt.longc;
|
|
}
|
|
if (!wkt.lat_ts && wkt.lat1 && (wkt.projName === 'Stereographic_South_Pole' || wkt.projName === 'Polar Stereographic (variant B)')) {
|
|
wkt.lat0 = d2r(wkt.lat1 > 0 ? 90 : -90);
|
|
wkt.lat_ts = wkt.lat1;
|
|
} else if (!wkt.lat_ts && wkt.lat0 && wkt.projName === 'Polar_Stereographic') {
|
|
wkt.lat_ts = wkt.lat0;
|
|
wkt.lat0 = d2r(wkt.lat0 > 0 ? 90 : -90);
|
|
}
|
|
}
|
|
var index = function(wkt) {
|
|
var lisp = parseString(wkt);
|
|
var type = lisp.shift();
|
|
var name = lisp.shift();
|
|
lisp.unshift(['name', name]);
|
|
lisp.unshift(['type', type]);
|
|
var obj = {};
|
|
sExpr(lisp, obj);
|
|
cleanWKT(obj);
|
|
return obj;
|
|
};
|
|
|
|
module.exports = index;
|