covutils
Version:
Utilities for creating, transforming, and handling Coverage Data objects.
449 lines (393 loc) • 15.6 kB
JavaScript
'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);
});
}