Try harder to get the projection from GeoTIFF headers

This commit is contained in:
Tim Schaub
2021-09-06 16:49:58 -06:00
parent e89b6c0a3f
commit be51d0480c
3 changed files with 90 additions and 11 deletions

View File

@@ -8,6 +8,11 @@
* @enum {string} * @enum {string}
*/ */
const Units = { const Units = {
/**
* Radians
* @api
*/
RADIANS: 'radians',
/** /**
* Degrees * Degrees
* @api * @api
@@ -40,6 +45,26 @@ const Units = {
USFEET: 'us-ft', USFEET: 'us-ft',
}; };
/**
* See http://duff.ess.washington.edu/data/raster/drg/docs/geotiff.txt
* @type {Object<number, Units>}
*/
const unitByCode = {
'9001': Units.METERS,
'9002': Units.FEET,
'9003': Units.USFEET,
'9101': Units.RADIANS,
'9102': Units.DEGREES,
};
/**
* @param {number} code Unit code.
* @return {Units} Units.
*/
export function fromCode(code) {
return unitByCode[code];
}
/** /**
* Meters per unit lookup table. * Meters per unit lookup table.
* @const * @const
@@ -48,6 +73,7 @@ const Units = {
*/ */
export const METERS_PER_UNIT = {}; export const METERS_PER_UNIT = {};
// use the radius of the Normal sphere // use the radius of the Normal sphere
METERS_PER_UNIT[Units.RADIANS] = 6370997 / (2 * Math.PI);
METERS_PER_UNIT[Units.DEGREES] = (2 * Math.PI * 6370997) / 360; METERS_PER_UNIT[Units.DEGREES] = (2 * Math.PI * 6370997) / 360;
METERS_PER_UNIT[Units.FEET] = 0.3048; METERS_PER_UNIT[Units.FEET] = 0.3048;
METERS_PER_UNIT[Units.METERS] = 1; METERS_PER_UNIT[Units.METERS] = 1;

View File

@@ -5,10 +5,11 @@ import DataTile from './DataTile.js';
import State from './State.js'; import State from './State.js';
import TileGrid from '../tilegrid/TileGrid.js'; import TileGrid from '../tilegrid/TileGrid.js';
import {Pool, fromUrl as tiffFromUrl, fromUrls as tiffFromUrls} from 'geotiff'; import {Pool, fromUrl as tiffFromUrl, fromUrls as tiffFromUrls} from 'geotiff';
import {Projection, get as getCachedProjection} from '../proj.js';
import {create as createDecoderWorker} from '../worker/geotiff-decoder.js'; import {create as createDecoderWorker} from '../worker/geotiff-decoder.js';
import {getIntersection} from '../extent.js'; import {getIntersection} from '../extent.js';
import {get as getProjection} from '../proj.js';
import {toSize} from '../size.js'; import {toSize} from '../size.js';
import {fromCode as unitsFromCode} from '../proj/Units.js';
/** /**
* @typedef {Object} SourceInfo * @typedef {Object} SourceInfo
@@ -25,10 +26,22 @@ import {toSize} from '../size.js';
* near-infrared band, configure `bands: [4]`. * near-infrared band, configure `bands: [4]`.
*/ */
/**
* @typedef {Object} GeoKeys
* @property {number} GTModelTypeGeoKey Model type.
* @property {number} GTRasterTypeGeoKey Raster type.
* @property {number} GeogAngularUnitsGeoKey Angular units.
* @property {number} GeogInvFlatteningGeoKey Inverse flattening.
* @property {number} GeogSemiMajorAxisGeoKey Semi-major axis.
* @property {number} GeographicTypeGeoKey Geographic coordinate system code.
* @property {number} ProjLinearUnitsGeoKey Projected linear unit code.
* @property {number} ProjectedCSTypeGeoKey Projected coordinate system code.
*/
/** /**
* @typedef {Object} GeoTIFFImage * @typedef {Object} GeoTIFFImage
* @property {Object} fileDirectory The file directory. * @property {Object} fileDirectory The file directory.
* @property {Object} geoKeys The parsed geo-keys. * @property {GeoKeys} geoKeys The parsed geo-keys.
* @property {boolean} littleEndian Uses little endian byte order. * @property {boolean} littleEndian Uses little endian byte order.
* @property {Object} tiles The tile cache. * @property {Object} tiles The tile cache.
* @property {boolean} isTiled The image is tiled. * @property {boolean} isTiled The image is tiled.
@@ -91,6 +104,49 @@ function getResolution(image, referenceImage) {
} }
} }
/**
* @param {GeoTIFFImage} image A GeoTIFF.
* @return {import("../proj/Projection.js").default} The image projection.
*/
function getProjection(image) {
const geoKeys = image.geoKeys;
if (!geoKeys) {
return null;
}
if (geoKeys.ProjectedCSTypeGeoKey) {
const code = 'EPSG:' + geoKeys.ProjectedCSTypeGeoKey;
let projection = getCachedProjection(code);
if (!projection) {
const units = unitsFromCode(geoKeys.ProjLinearUnitsGeoKey);
if (units) {
projection = new Projection({
code: code,
units: units,
});
}
}
return projection;
}
if (geoKeys.GeographicTypeGeoKey) {
const code = 'EPSG:' + geoKeys.GeographicTypeGeoKey;
let projection = getCachedProjection(code);
if (!projection) {
const units = unitsFromCode(geoKeys.GeogAngularUnitsGeoKey);
if (units) {
projection = new Projection({
code: code,
units: units,
});
}
}
return projection;
}
return null;
}
/** /**
* @param {import("geotiff/src/geotiff.js").GeoTIFF|import("geotiff/src/geotiff.js").MultiGeoTIFF} tiff A GeoTIFF. * @param {import("geotiff/src/geotiff.js").GeoTIFF|import("geotiff/src/geotiff.js").MultiGeoTIFF} tiff A GeoTIFF.
* @return {Promise<Array<import("geotiff/src/geotiffimage.js").GeoTIFFImage>>} Resolves to a list of images. * @return {Promise<Array<import("geotiff/src/geotiffimage.js").GeoTIFFImage>>} Resolves to a list of images.
@@ -419,14 +475,10 @@ class GeoTIFFSource extends DataTile {
const firstSource = sources[0]; const firstSource = sources[0];
for (let i = firstSource.length - 1; i >= 0; --i) { for (let i = firstSource.length - 1; i >= 0; --i) {
const image = firstSource[i]; const image = firstSource[i];
if (image.geoKeys) { const projection = getProjection(image);
const code = if (projection) {
image.geoKeys.ProjectedCSTypeGeoKey || this.projection = projection;
image.geoKeys.GeographicTypeGeoKey; break;
if (code) {
this.projection = getProjection('EPSG:' + code);
break;
}
} }
} }
} }

View File

@@ -2,7 +2,7 @@ import GeoTIFFSource from '../../../../../src/ol/source/GeoTIFF.js';
import State from '../../../../../src/ol/source/State.js'; import State from '../../../../../src/ol/source/State.js';
import TileState from '../../../../../src/ol/TileState.js'; import TileState from '../../../../../src/ol/TileState.js';
describe('ol.source.GeoTIFF', function () { describe('ol/source/GeoTIFF', function () {
/** @type {GeoTIFFSource} */ /** @type {GeoTIFFSource} */
let source; let source;
beforeEach(function () { beforeEach(function () {
@@ -30,6 +30,7 @@ describe('ol.source.GeoTIFF', function () {
expect(source.nodataValues_).to.eql([[0]]); expect(source.nodataValues_).to.eql([[0]]);
expect(source.getTileGrid().getResolutions().length).to.be(1); expect(source.getTileGrid().getResolutions().length).to.be(1);
expect(source.projection.getCode()).to.be('EPSG:4326'); expect(source.projection.getCode()).to.be('EPSG:4326');
expect(source.projection.getUnits()).to.be('degrees');
done(); done();
}); });
}); });