uriproj
Version:
Map projection functions from standard coordinate reference system URIs.
195 lines (175 loc) • 7.26 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.get = get;
exports.load = load;
exports.set = set;
require('whatwg-fetch');
var _proj = require('proj4');
var _proj2 = _interopRequireDefault(_proj);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var ROOT_PREFIX = 'http://www.opengis.net/def/crs/';
var OGC_PREFIX = ROOT_PREFIX + 'OGC/';
var EPSG_PREFIX = ROOT_PREFIX + 'EPSG/0/';
/**
* @typedef {Object} Projection
* @property {function(lonlat: Array<number>): Array<number>} forward
* Transforms a geographic WGS84 [longitude, latitude] coordinate to an [x, y] projection coordinate.
* @property {function(xy: Array<number>): Array<number>} inverse
* Transforms an [x, y] projection coordinate to a geographic WGS84 [longitude, latitude] coordinate.
*/
// a cache of URI string -> Projection object mappings
var projCache = {};
// work-arounds for incorrect epsg.io / proj4 behaviour
var needsAxesReordering = _defineProperty({}, EPSG_PREFIX + 4326, true);
// store some built-in projections which are not available on epsg.io
var LONLAT = (0, _proj2.default)('+proj=longlat +datum=WGS84 +no_defs');
set(OGC_PREFIX + '1.3/CRS84', LONLAT);
set(EPSG_PREFIX + 4979, reverseAxes(LONLAT));
/**
* Returns a stored {@link Projection} for a given URI, or {@link undefined} if no {@link Projection} is stored for that URI.
*
* @param {string} crsUri The CRS URI for which to return a {@link Projection}.
* @return {Projection|undefined} A {@link Projection} object, or {@link undefined} if not stored by {@link load} or {@link set}.
*
* @example
* // has to be stored previously via load() or set()
* var proj = uriproj.get('http://www.opengis.net/def/crs/EPSG/0/27700')
* var [longitude, latitude] = [-1.54, 55.5]
* var [easting,northing] = proj.forward([longitude, latitude])
*/
function get(crsUri) {
return projCache[crsUri];
}
/**
* Returns a {@link Promise} that succeeds with an already stored {@link Projection} or, if not stored,
* that remotely loads the {@link Projection} (currently using https://epsg.io), stores it, and then succeeds with it.
*
* @param {string} crsUri The CRS URI for which to return a projection.
* @return {Promise<Projection,Error>} A {@link Promise} object succeeding with a {@link Projection} object,
* and failing with an {@link Error} object in case of network or PROJ.4 parsing problems.
*
* @example <caption>Loading a single projection</caption>
* uriproj.load('http://www.opengis.net/def/crs/EPSG/0/27700').then(proj => {
* var [longitude, latitude] = [-1.54, 55.5]
* var [easting,northing] = proj.forward([longitude, latitude])
* })
*
* @example <caption>Loading multiple projections</caption>
* var uris = [
* 'http://www.opengis.net/def/crs/EPSG/0/27700',
* 'http://www.opengis.net/def/crs/EPSG/0/7376',
* 'http://www.opengis.net/def/crs/EPSG/0/7375']
* Promise.all(uris.map(uriproj.load)).then(projs => {
* // all projections are loaded and stored now
*
* // get the first projection
* var proj1 = projs[0]
* // or:
* var proj1 = uriproj.get(uris[0])
* })
*
*/
function load(crsUri) {
if (crsUri in projCache) {
return Promise.resolve(projCache[crsUri]);
}
var epsg = crsUriToEPSG(crsUri);
var url = 'https://epsg.io/' + epsg + '.proj4';
return fetch(url).then(function (response) {
if (!response.ok) {
throw new Error('HTTP response code: ' + response.status);
}
return response.text();
}).then(function (proj4string) {
return set(crsUri, proj4string, { reverseAxes: crsUri in needsAxesReordering });
});
}
/**
* Stores a given projection for a given URI that can then be accessed via {@link get} and {@link load}.
* If the projection is given as proj4 string and does not require axis reversal, then it is stored
* as a named projection in proj4 itself under the given URI.
*
* @param {string} crsUri The CRS URI for which to store the projection.
* @param {string|Projection} proj A proj4 string or a {@link Projection} object.
* @param {Object} [options] Options object.
* @param {boolean} [options.reverseAxes=false] If proj is a proj4 string, whether to reverse the projection axes.
* @return {Projection} The newly stored projection.
* @throws {Error} If crsUri or proj is missing, or if a PROJ.4 string cannot be parsed by proj4js.
*
* @example <caption>Storing a projection using a PROJ.4 string</caption>
* var uri = 'http://www.opengis.net/def/crs/EPSG/0/27700'
* var proj4 = '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 ' +
* '+ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs'
* uriproj.set(uri, proj4)
*
* @example <caption>Storing a projection using a Projection object</caption>
* var uri = 'http://www.opengis.net/def/crs/EPSG/0/27700'
* var proj = {
* forward: ([lon,lat]) => [..., ...],
* inverse: ([x,y]) => [..., ...]
* }
* uriproj.set(uri, proj)
*/
function set(crsUri, proj) {
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
if (!crsUri || !proj) {
throw new Error('crsUri and proj cannot be empty');
}
var projobj = void 0;
if (typeof proj === 'string') {
projobj = (0, _proj2.default)(proj);
if (!projobj) {
throw new Error('Unsupported proj4 string: ' + proj);
}
_proj2.default.defs(crsUri, proj);
if (options.reverseAxes) {
projobj = reverseAxes(projobj);
}
} else {
projobj = proj;
}
projCache[crsUri] = projobj;
return projobj;
}
/**
* Return the EPSG code of an OGC CRS URI of the form
* http://www.opengis.net/def/crs/EPSG/0/1234 (would return 1234).
*
* @param {string} crsUri The CRS URI for which to return the EPSG code.
* @return {string} The EPSG code.
*/
function crsUriToEPSG(uri) {
var epsg = void 0;
if (uri.indexOf(EPSG_PREFIX) === 0) {
epsg = uri.substr(EPSG_PREFIX.length);
} else {
throw new Error('Unsupported CRS URI: ' + uri);
}
return epsg;
}
/**
* Reverses projection axis order.
*
* For example, a projection with lon, lat axis order is turned into one with lat, lon order.
* This is necessary since geographic projections in proj4 can only be defined with
* lon,lat order, however some CRSs have lat,lon order (like EPSG4326).
* Incorrectly, epsg.io returns a proj4 string (with lon,lat order) even if the CRS
* has lat,lon order. This function manually flips the axis order of a given projection.
* See also `needsAxesReordering` above.
*
* @param {Projection} proj The projection whose axis order to revert.
* @return {Projection} The projection with reversed axis order.
*/
function reverseAxes(proj) {
return {
forward: function forward(pos) {
return proj.forward(pos).reverse();
},
inverse: function inverse(pos) {
return proj.inverse([pos[1], pos[0]]);
}
};
}