UNPKG

uriproj

Version:

Map projection functions from standard coordinate reference system URIs.

195 lines (175 loc) 7.26 kB
'use strict'; 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]]); } }; }