UNPKG

covutils

Version:

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

449 lines (393 loc) 15.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"); } }; }(); exports.normalizeIndexSubsetConstraints = normalizeIndexSubsetConstraints; exports.subsetDomainByIndex = subsetDomainByIndex; exports.subsetCoverageByIndex = subsetCoverageByIndex; exports.subsetCoverageByValue = subsetCoverageByValue; var _referencing = require('./referencing.js'); var _array = require('./array.js'); 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); } } /** * After normalization, all constraints are start,stop,step objects. * It holds that stop > start, step > 0, start >= 0, stop >= 1. * For each axis, a constraint exists. */ function normalizeIndexSubsetConstraints(domain, constraints) { // check and normalize constraints to simplify code var normalizedConstraints = {}; for (var axisName in constraints) { if (!domain.axes.has(axisName)) { // TODO clarify cov behaviour in the JS API spec continue; } if (constraints[axisName] === undefined || constraints[axisName] === null) { continue; } if (typeof constraints[axisName] === 'number') { var constraint = constraints[axisName]; normalizedConstraints[axisName] = { start: constraint, stop: constraint + 1 }; } else { normalizedConstraints[axisName] = constraints[axisName]; } var _normalizedConstraint = normalizedConstraints[axisName]; var _normalizedConstraint2 = _normalizedConstraint.start; var start = _normalizedConstraint2 === undefined ? 0 : _normalizedConstraint2; var _normalizedConstraint3 = _normalizedConstraint.stop; var stop = _normalizedConstraint3 === undefined ? domain.axes.get(axisName).values.length : _normalizedConstraint3; var _normalizedConstraint4 = _normalizedConstraint.step; var step = _normalizedConstraint4 === undefined ? 1 : _normalizedConstraint4; if (step <= 0) { throw new Error('Invalid constraint for ' + axisName + ': step=' + step + ' must be > 0'); } if (start >= stop || start < 0) { throw new Error('Invalid constraint for ' + axisName + ': stop=' + stop + ' must be > start=' + start + ' and both >= 0'); } normalizedConstraints[axisName] = { start: start, stop: stop, step: step }; } var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = domain.axes.keys()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _axisName = _step.value; if (!(_axisName in normalizedConstraints)) { var len = domain.axes.get(_axisName).values.length; normalizedConstraints[_axisName] = { start: 0, stop: len, step: 1 }; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return normalizedConstraints; } function subsetDomainByIndex(domain, constraints) { constraints = normalizeIndexSubsetConstraints(domain, constraints); // subset the axis arrays of the domain (immediately + cached) var newdomain = { type: _constants.DOMAIN, // TODO remove profiles in favour of domainType at some point // TODO are the profiles still valid? profiles: domain.profiles, domainType: domain.domainType, axes: new Map(domain.axes), referencing: domain.referencing }; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { var _loop = function _loop() { var axisName = _step2.value; var axis = domain.axes.get(axisName); var coords = axis.values; var bounds = axis.bounds; var constraint = constraints[axisName]; var newcoords = void 0; var newbounds = void 0; var start = constraint.start; var stop = constraint.stop; var step = constraint.step; if (start === 0 && stop === coords.length && step === 1) { newcoords = coords; newbounds = bounds; } else if (step === 1) { // TypedArray has subarray which creates a view, while Array has slice which makes a copy if (coords.subarray) { newcoords = coords.subarray(start, stop); } else { newcoords = coords.slice(start, stop); } if (bounds) { newbounds = { get: function get(i) { return bounds.get(start + i); } }; } } else { var q = Math.trunc((stop - start) / step); var r = (stop - start) % step; var len = q + r; newcoords = new coords.constructor(len); // array or typed array for (var i = start, j = 0; i < stop; i += step, j++) { newcoords[j] = coords[i]; } if (bounds) { newbounds = { get: function get(i) { return bounds.get(start + i * step); } }; } } var newaxis = { dataType: axis.dataType, components: axis.components, values: newcoords, bounds: newbounds }; newdomain.axes.set(axisName, newaxis); }; for (var _iterator2 = Object.keys(constraints)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { _loop(); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return newdomain; } /** * Generic subsetByIndex function that can be used when building new Coverage objects. * * @example * var cov = { * type: 'Coverage', * ... * subsetByIndex: constraints => subsetCoverageByIndex(cov, constraints) * } */ function subsetCoverageByIndex(cov, constraints) { return cov.loadDomain().then(function (domain) { constraints = normalizeIndexSubsetConstraints(domain, constraints); var newdomain = subsetDomainByIndex(domain, constraints); // subset ranges (on request) var rangeWrapper = function rangeWrapper(range) { var newrange = { dataType: range.dataType, get: function get(obj) { // translate subsetted to original indices var newobj = {}; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = Object.keys(obj)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var axisName = _step3.value; var _constraints$axisName = constraints[axisName]; var _start = _constraints$axisName.start; var _step4 = _constraints$axisName.step; newobj[axisName] = _start + obj[axisName] * _step4; } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } return range.get(newobj); } }; newrange.shape = new Map(); var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = domain.axes.keys()[Symbol.iterator](), _step5; !(_iteratorNormalCompletion4 = (_step5 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var axisName = _step5.value; var size = newdomain.axes.get(axisName).values.length; newrange.shape.set(axisName, size); } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } return newrange; }; var loadRange = function loadRange(key) { return cov.loadRange(key).then(rangeWrapper); }; var loadRanges = function loadRanges(keys) { return cov.loadRanges(keys).then(function (ranges) { return new Map([].concat(_toConsumableArray(ranges)).map(function (_ref) { var _ref2 = _slicedToArray(_ref, 2); var key = _ref2[0]; var range = _ref2[1]; return [key, rangeWrapper(range)]; })); }); }; // assemble everything to a new coverage var newcov = { type: _constants.COVERAGE, // TODO are the profiles still valid? domainProfiles: cov.domainProfiles, domainType: cov.domainType, parameters: cov.parameters, loadDomain: function loadDomain() { return Promise.resolve(newdomain); }, loadRange: loadRange, loadRanges: loadRanges }; newcov.subsetByIndex = subsetCoverageByIndex.bind(null, newcov); newcov.subsetByValue = subsetCoverageByValue.bind(null, newcov); return newcov; }); } /** * Generic subsetByValue function that can be used when building new Coverage objects. * Requires cov.subsetByIndex function. * * @example * var cov = { * type: 'Coverage', * ... * subsetByValue: constraints => subsetCoverageByValue(cov, constraints) * } */ function subsetCoverageByValue(cov, constraints) { return cov.loadDomain().then(function (domain) { // calculate indices and use subsetByIndex var indexConstraints = {}; var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = Object.keys(constraints)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion5 = (_step6 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var axisName = _step6.value; var spec = constraints[axisName]; if (spec === undefined || spec === null || !domain.axes.has(axisName)) { continue; } var axis = domain.axes.get(axisName); var vals = axis.values; // special-case handling var isISODate = (0, _referencing.isISODateAxis)(domain, axisName); var isLongitude = (0, _referencing.isLongitudeAxis)(domain, axisName); // wrap input longitudes into longitude range of domain axis var lonWrapper = isLongitude ? (0, _referencing.getLongitudeWrapper)(domain, axisName) : undefined; if (typeof spec === 'number' || typeof spec === 'string' || spec instanceof Date) { var match = spec; if (isISODate) { // convert times to numbers before searching match = (0, _referencing.asTime)(match); vals = vals.map(function (v) { return new Date(v).getTime(); }); } else if (isLongitude) { match = lonWrapper(match); } var i = void 0; // older browsers don't have TypedArray.prototype.indexOf if (vals.indexOf) { i = vals.indexOf(match); } else { i = Array.prototype.indexOf.call(vals, match); } if (i === -1) { throw new Error('Domain value not found: ' + spec); } indexConstraints[axisName] = i; } else if ('target' in spec) { // find index of value closest to target var target = spec.target; if (isISODate) { // convert times to numbers before searching target = (0, _referencing.asTime)(target); vals = vals.map(function (v) { return new Date(v).getTime(); }); } else if (isLongitude) { target = lonWrapper(target); } else if (typeof vals[0] !== 'number' || typeof target !== 'number') { throw new Error('Invalid axis or constraint value type'); } var _i = (0, _array.indexOfNearest)(vals, target); indexConstraints[axisName] = _i; } else if ('start' in spec && 'stop' in spec) { // TODO what about bounds? var _start2 = spec.start; var _stop = spec.stop; if (isISODate) { var _ref3 = [(0, _referencing.asTime)(_start2), (0, _referencing.asTime)(_stop)]; // convert times to numbers before searching _start2 = _ref3[0]; _stop = _ref3[1]; vals = vals.map(function (v) { return new Date(v).getTime(); }); } else if (isLongitude) { var _ref4 = [lonWrapper(_start2), lonWrapper(_stop)]; _start2 = _ref4[0]; _stop = _ref4[1]; } else if (typeof vals[0] !== 'number' || typeof _start2 !== 'number') { throw new Error('Invalid axis or constraint value type'); } var _indicesOfNearest = (0, _array.indicesOfNearest)(vals, _start2); var _indicesOfNearest2 = _slicedToArray(_indicesOfNearest, 2); var lo1 = _indicesOfNearest2[0]; var hi1 = _indicesOfNearest2[1]; var _indicesOfNearest3 = (0, _array.indicesOfNearest)(vals, _stop); var _indicesOfNearest4 = _slicedToArray(_indicesOfNearest3, 2); var lo2 = _indicesOfNearest4[0]; var hi2 = _indicesOfNearest4[1]; // cov is a bit arbitrary and may include one or two indices too much // (but since we don't handle bounds it doesn't matter that much) var imin = Math.min(lo1, hi1, lo2, hi2); var imax = Math.max(lo1, hi1, lo2, hi2) + 1; // subsetByIndex is exclusive indexConstraints[axisName] = { start: imin, stop: imax }; } else { throw new Error('Invalid subset constraints'); } } } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } return cov.subsetByIndex(indexConstraints); }); }