UNPKG

covutils

Version:

Utilities for creating, transforming, and handling Coverage Data objects.

397 lines (346 loc) 13.6 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _LongitudeAxisIndex; exports.getHorizontalCRSReferenceObject = getHorizontalCRSReferenceObject; exports.isEllipsoidalCRS = isEllipsoidalCRS; exports.getProjection = getProjection; exports.reproject = reproject; exports.getLongitudeWrapper = getLongitudeWrapper; exports.isLongitudeAxis = isLongitudeAxis; exports.isISODateAxis = isISODateAxis; exports.asTime = asTime; var _constants = require('./constants.js'); function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 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 OPENGIS_CRS_PREFIX = 'http://www.opengis.net/def/crs/'; /** 3D WGS84 in lat-lon-height order */ var EPSG4979 = OPENGIS_CRS_PREFIX + 'EPSG/0/4979'; /** 2D WGS84 in lat-lon order */ var EPSG4326 = OPENGIS_CRS_PREFIX + 'EPSG/0/4326'; /** 2D WGS84 in lon-lat order */ var CRS84 = OPENGIS_CRS_PREFIX + 'OGC/1.3/CRS84'; /** CRSs in which position is specified by geodetic latitude and longitude */ var EllipsoidalCRSs = [EPSG4979, EPSG4326, CRS84]; /** Position of longitude axis */ var LongitudeAxisIndex = (_LongitudeAxisIndex = {}, _defineProperty(_LongitudeAxisIndex, EPSG4979, 1), _defineProperty(_LongitudeAxisIndex, EPSG4326, 1), _defineProperty(_LongitudeAxisIndex, CRS84, 0), _LongitudeAxisIndex); /** * Return the reference system connection object for the given domain component, * or undefined if none exists. */ function getReferenceObject(domain, component) { var ref = domain.referencing.find(function (ref) { return ref.components.indexOf(component) !== -1; }); return ref; } /** * Return the reference system connection object of the horizontal CRS of the domain, * or ``undefined`` if none found. * A horizontal CRS is either geodetic (typically ellipsoidal, meaning lat/lon) * or projected and has exactly two axes. */ function getHorizontalCRSReferenceObject(domain) { var isHorizontal = function isHorizontal(ref) { return ['GeodeticCRS', 'ProjectedCRS'].indexOf(ref.system.type) !== -1 && ref.components.length === 2; }; var ref = domain.referencing.find(isHorizontal); return ref; } /** * Return whether the reference system is a CRS in which * horizontal position is specified by geodetic latitude and longitude. */ function isEllipsoidalCRS(rs) { // TODO should support unknown CRSs with embedded axis information // this also covers the case when there is no ID property return EllipsoidalCRSs.indexOf(rs.id) !== -1; } /** * Return a projection object based on the CRS found in the coverage domain. * If no CRS is found or it is unsupported, then ``undefined`` is returned. * * A projection converts between geodetic lat/lon and projected x/y values. * * For lat/lon CRSs the projection is defined such that an input lat/lon * position gets projected/wrapped to the longitude range used in the domain, for example * [0,360]. The purpose of this is to make intercomparison between different coverages easier. * * The following limitations currently apply: * - only ellipsoidal CRSs are supported (lat/lon) * - only primitive axes and Tuple/Polygon composite axes are supported * * @param {Domain} domain A coverage domain object. * @return {IProjection} A stripped-down leaflet IProjection object. */ function getProjection(domain) { var ref = domain.referencing.find(function (ref) { return isEllipsoidalCRS(ref.system); }); if (!ref) { // either no CRS found or not ellipsoidal return; } var lonIdx = LongitudeAxisIndex[ref.system.id]; if (lonIdx > 1) { // this should never happen as longitude is always the first or second axis throw new Error(); } var lonComponent = ref.components[lonIdx]; // we find the min and max longitude occuring in the domain by inspecting the axis values // Note: this is inefficient for big composite axes. // In that case, something like a domain extent might help which has the min/max values for each component. // TODO handle bounds var lonMin = void 0, lonMax = void 0; if (domain.axes.has(lonComponent)) { // longitude is a grid axis var lonAxisName = lonComponent; var lonAxisVals = domain.axes.get(lonAxisName).values; lonMin = lonAxisVals[0]; lonMax = lonAxisVals[lonAxisVals.length - 1]; if (lonMin > lonMax) { var _ref = [lonMax, lonMin]; lonMin = _ref[0]; lonMax = _ref[1]; } } else { // TODO there should be no dependency to CovJSON // longitude is not a primitive grid axis but a component of a composite axis // find the composite axis containing the longitude component var axes = [].concat(_toConsumableArray(domain.axes.values())); var axis = axes.find(function (axis) { return axis.components.indexOf(lonComponent) !== -1; }); var lonCompIdx = axis.components.indexOf(lonComponent); // scan the composite axis for min/max longitude values lonMin = Infinity; lonMax = -Infinity; if (axis.dataType === _constants.COVJSON_DATATYPE_TUPLE) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = axis.values[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var tuple = _step.value; var lon = tuple[lonCompIdx]; lonMin = Math.min(lon, lonMin); lonMax = Math.max(lon, lonMax); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } else if (axis.dataType === _constants.COVJSON_DATATYPE_POLYGON) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = axis.values[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var poly = _step2.value; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = poly[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var ring = _step3.value; var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = ring[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var point = _step4.value; var _lon = point[lonCompIdx]; lonMin = Math.min(_lon, lonMin); lonMax = Math.max(_lon, lonMax); } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } } else { throw new Error('Unsupported data type: ' + axis.dataType); } } var lonMid = (lonMax + lonMin) / 2; var lonMinExtended = lonMid - 180; var lonMaxExtended = lonMid + 180; return { project: function project(_ref2) { var lon = _ref2.lon; var lat = _ref2.lat; var lonProjected = void 0; if (lonMinExtended <= lon && lon <= lonMaxExtended) { // use unchanged to avoid introducing rounding errors lonProjected = lon; } else { lonProjected = ((lon - lonMinExtended) % 360 + 360) % 360 + lonMinExtended; } var _ref3 = lonIdx === 0 ? [lonProjected, lat] : [lat, lonProjected]; var _ref4 = _slicedToArray(_ref3, 2); var x = _ref4[0]; var y = _ref4[1]; return { x: x, y: y }; }, unproject: function unproject(_ref5) { var x = _ref5.x; var y = _ref5.y; var _ref6 = lonIdx === 0 ? [x, y] : [y, x]; var _ref7 = _slicedToArray(_ref6, 2); var lon = _ref7[0]; var lat = _ref7[1]; return { lon: lon, lat: lat }; } }; } /** * Reprojects coordinates from one projection to another. */ function reproject(pos, fromProjection, toProjection) { return toProjection.project(fromProjection.unproject(pos)); } /** * Returns a function which converts an arbitrary longitude to the * longitude extent used in the coverage domain. * This only supports primitive axes since this is what subsetByValue supports. * The longitude extent is extended to 360 degrees if the actual extent is smaller. * The extension is done equally on both sides of the extent. * * For example, the domain may have longitudes within [0,360]. * An input longitude of -70 is converted to 290. * All longitudes within [0,360] are returned unchanged. * * If the domain has longitudes within [10,50] then the * extended longitude range is [-150,210] (-+180 from the middle point). * An input longitude of -170 is converted to 190. * All longitudes within [-150,210] are returned unchanged. * * @ignore */ function getLongitudeWrapper(domain, axisName) { // TODO deprecate this in favour of getProjection, check leaflet-coverage // for primitive axes, the axis identifier = component identifier if (!isLongitudeAxis(domain, axisName)) { throw new Error('\'' + axisName + '\' is not a longitude axis'); } var vals = domain.axes.get(axisName).values; var lon_min = vals[0]; var lon_max = vals[vals.length - 1]; if (lon_min > lon_max) { var _ref8 = [lon_max, lon_min]; lon_min = _ref8[0]; lon_max = _ref8[1]; } var x_mid = (lon_max + lon_min) / 2; var x_min = x_mid - 180; var x_max = x_mid + 180; return function (lon) { if (x_min <= lon && lon <= x_max) { // directly return to avoid introducing rounding errors return lon; } else { return ((lon - x_min) % 360 + 360) % 360 + x_min; } }; } /** * Return whether the given domain axis represents longitudes. * * @ignore */ function isLongitudeAxis(domain, axisName) { var ref = getReferenceObject(domain, axisName); if (!ref) { return false; } var crsId = ref.system.id; // TODO should support unknown CRSs with embedded axis information if (EllipsoidalCRSs.indexOf(crsId) === -1) { // this also covers the case when there is no ID property return false; } var compIdx = ref.components.indexOf(axisName); var isLongitude = LongitudeAxisIndex[crsId] === compIdx; return isLongitude; } /** * Returns true if the given axis has ISO8601 date strings * as axis values. */ function isISODateAxis(domain, axisName) { var val = domain.axes.get(axisName).values[0]; if (typeof val !== 'string') { return false; } return !isNaN(new Date(val).getTime()); } function asTime(inp) { var res = void 0; var err = false; if (typeof inp === 'string') { res = new Date(inp).getTime(); } else if (inp instanceof Date) { res = inp.getTime(); } else { err = true; } if (isNaN(res)) { err = true; } if (err) { throw new Error('Invalid date: ' + inp); } return res; }