covutils
Version:
Utilities for creating, transforming, and handling Coverage Data objects.
397 lines (346 loc) • 13.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"); } }; }();
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;
}