New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle rotated, skewed or flipped GeoTIFF tile grids #15402
base: main
Are you sure you want to change the base?
Conversation
📦 Preview the website for this branch here: https://deploy-preview-15402--ol-site.netlify.app/. |
fba39e7
to
fa3f0ac
Compare
Ready for review as I do not have any files small enough to use for a rotated or skewed rendering test, although the test file from #15428 would be suitable. The Projection setMatrix method should work equally well for a rotated ImageStatic, etc. but I have not documented that as part of the API. |
📦 Preview the website for this branch here: https://deploy-preview-15402--ol-site.netlify.app/. |
e8c58cd
to
9013e51
Compare
2488417
to
5b56e2a
Compare
@mike-000 Thanks for this! I'm happy to provide more test files, though the smallest I have is ~80MB. |
@loganwilliams There is a large selection of open data available via https://radiantearth.github.io/stac-browser/#/external/s3.us-west-2.amazonaws.com/umbra-open-data-catalog/stac/catalog.json typically around that size suitable for use in examples but too big to use as local test data. I have now found a reduced version of the file used in the example at https://github.com/GeoTIFF/test-data which is under 4MB so might be suitable for another rendering test. |
7030605
to
7cc890b
Compare
This is really great, thanks for your work on it @mike-000. Hope it gets merged soon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approach developed in this pull request is good. My suggestions are only about where parts of the implementation belong to, so more users can benefit from it.
src/ol/source/GeoTIFF.js
Outdated
/** | ||
* Replacement for GeoTIFFImage.getResolution() to handle ModelTransformation. | ||
* @param {GeoTIFFImage} image The image. | ||
* @param {GeoTIFFImage} [referenceImage] The reference image. | ||
* @return {Array<number>} The map x and y units per pixel. | ||
*/ | ||
function getResolution(image, referenceImage) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it make more sense to contribute this to geotiff.js?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, if geotiffjs/geotiff.js#413 and geotiffjs/geotiff.js#414 are accepted and included in a release then these copies could be replaced by the real thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both are now merged and released.
src/ol/proj.js
Outdated
if (sourceMatrix || destinationMatrix) { | ||
const transform = transformFunc; | ||
sourceMatrix = invertMatrix((sourceMatrix || createMatrix()).slice()); | ||
destinationMatrix = destinationMatrix || createMatrix(); | ||
const coordTransform = function (coordinate) { | ||
return applyMatrix( | ||
destinationMatrix, | ||
transform(applyMatrix(sourceMatrix, coordinate.slice())), | ||
); | ||
}; | ||
transformFunc = createTransformFromCoordinateTransform(coordTransform); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't bake matrix transforms into projections. It does not feel right. Instead, a matrix transform should be defined with custom transform functions using addCoordinateTransforms()
, like e.g. shown in https://openlayers.org/en/latest/examples/wms-custom-proj.html.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with that approach is transforms would need to be maintained between every matrix variation and every projection added (past and future) via proj4. Given that this is a relatively unusual situation (although it could also be applied to KML ground overlays) I think setting an additional property on the projection and calculating the transforms dynamically as required would be easier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you create a transform function, you can mix in the matrix transform from a separate function or variable, so you can still handle matrix variations outside the once created transform function. Basically the same thing you're currently doing in src/ol/proj.js.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The existing transform functions are indexed by projection codes - the matrixes are assigned to projection objects with those codes. Doing it this way avoids adding complexity to the indexing of the primary transforms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you point me to the code section where that happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new code calls projection.setMatrix(matrix);
or this.projection.setMatrix(matrix);
when determining the projection https://github.com/mike-000/openlayers/blob/7cc890b34747feb99173b974d2e6e143c88e0504/src/ol/source/GeoTIFF.js#L297
and if a matrix is detected when an override projection has been specified
https://github.com/mike-000/openlayers/blob/7cc890b34747feb99173b974d2e6e143c88e0504/src/ol/source/GeoTIFF.js#615
It is handled in existing reprojection code via getTransform
and transform
calls which need no changes as they are passed the projection objects (not codes) and therefore pass them on unchanged to getTransformFromProjections
.
7af1d67
to
e42655e
Compare
e42655e
to
2d6aec0
Compare
710c042
to
ab128a0
Compare
Handle all transforms including flipped Also set matrix when projection is specified in options Avoid unnecessary nodata
eb6ce2c
to
8c33271
Compare
@ahocevar This is ready for further review. Workarounds have been removed in favour of the latest geotiff.js methods. The handling of matrixes in projections now uses entire transform functions (in series if necessary) instead of very inefficiently extracting a single coordinate transform from a projection transform function to use in a new transform function. This has made the rendering noticeably faster. In many cases (as in the two examples and current tests) the matrix transform is independent of projection transforms and could potentially be cached and/or used directly, e.g.
although I suspect there would be little further performance gain from this. Matrix and projection transforms used in series will be needed for generic viewers such as attempted in https://radiantearth.github.io/stac-browser/#/external/s3.us-west-2.amazonaws.com/umbra-open-data-catalog/stac/2023/2023-02/2023-02-12/6d584f33-0489-47dd-9412-14d2c83532fc/6d584f33-0489-47dd-9412-14d2c83532fc.json where the rotated UTM is further transformed to 3857 as in https://jsfiddle.net/w4qxs50k/ - a test is still needed for that (potentially using an ImageStatic as in #7255 (comment)). |
}, | ||
); | ||
} | ||
return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A single transform could be used directly here, but any performance gain from the extra code would be minimal
if ((!sourceTransform || !destinationTransform) && !transformFunc) {
return sourceTransform || destinationTransform;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your continued effort here, @mike-000. The one thing that still does not look right is that you're using Projection
as container for the transform matrix, and the projection transform function to apply a transform matrix. Like I said already, that does not feel like the right place.
Tthinking about it more, my previous suggestion to use transform functions directly does not feel right.
The GeoTIFF
source looks like the right place to maintain a mapping image -> matrix, and to make use of it in an overridden getTile()
method.
Separating matrix and projection transforms has the potential for double reprojection. I would regard the image as being in a rotated version of a standard projection - in the example case a UTM projection - defined by a standard projection code and a rotation matrix, so to render it in for example EPSG:3857 only one reprojection should be needed (no reprojection of reproj tiles) the transform function being a combination of the UTM to 3857 transform and the matrix transform which can be easily done by Similarly the technique could be used for KML ground overlays, by regarding them as being in rotated/scaled version of EPSG:4326, they could be easily rendered in any view projection which has a transform to EPSG:4326. But I do think setting a matrix on a projection object should not be exposed in the API, it should be done only inside the GeoTIFF source code (and potentially inside a ground overlay parser) to avoid any future breaking changes. |
I agree that a single transform of raster data is preferable. But still, this can be done in |
A solution which is not specific to DataTile would be preferable, so it could be easily reused elsewhere, e.g. as part of a fix for #4949/#7255 where rotation transforms can be combined projection transforms without the need to maintain large numbers of individual transforms for multiple projection code/rotation permutations. Also reprojection inevitably reduces the output quality, sometimes as in #15579 that can be unacceptable, so it would nice to allow the view to be in the tiles native projection and reproject any less important background or vector data. Currently |
Fixes #15294
Fixes #15370
Fixes #15428
Uses rotated/skewed/flipped variants of projections to handle GeoTIFF tile grids with rotation, skew or flip specified by ModelTransformation.
See https://deploy-preview-15402--ol-site.netlify.app/en/latest/examples/cog-modeltransformation.html
Uses a local alternative to geotiffjs/geotiff.js#403 which could be removed if the replacement geotiffjs/geotiff.js#413 (with corrected Y axis scale) is merged. The code could be further simplified if geotiffjs/geotiff.js#414 is merged.