Skip to content

Fix various bugs with raster data and tiling - #215

Draft
pyrollo wants to merge 3 commits into
mainfrom
pyr/fix-raster
Draft

pyrollo wants to merge 3 commits into
mainfrom
pyr/fix-raster

Conversation

@pyrollo

@pyrollo pyrollo commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Issue

Closes #191
Closes #210

Changes

Three different ones:

  • Fix a probable future bug due to FloatArrayGeographicDataMatrix2d.getFloat returning unwanted value if x out of bounds;
  • Adapt WMS resolution to real needs instead of always asking one pixel per map unit;
  • Start fixing tiled rendering of GeoTiff data (main part of this PR).

Wrong cellsize / offset computation

We were doing something wrong with GeoTiff (and maybe with other raster data). Here is a try to explain what was wrong and what to do.

Raster data is made of sample, placed on a regular grid in a given CRS.
To deal with this data, we need to know:

  • Its CRS
  • The coordinates of one of its point in that CRS. We use point (0,0) and call these offset.
  • The spacing of the samples on the grid in the two dimensions, we call that cellsize;
raster-data

Raster samples are represented by blue dots. Purple dot is (0,0) and arrows shows the spacing of the raster grid.

Now, we want to use some of these samples to draw some voxels. Voxels and samples may not be on the same grid (depending on the CRS, the grids may even look distorted from each other). Lets place voxel grid on raster samples grid:

raster1

Voxel grid is represented with grey squares. We want to draw the voxel tile represented by the red square.

First, we have to select raster sample. That's another issue. Let's say we are able to select the correct samples:

raster2

Selected samples are in purple (we need samples around voxels so we can interpolate). The samples envelope is draw as purple square (here we consider only samples, some implementation consider a square around sample but that is also another issue).

Here what we did wrong when calculating offset/cellsize before this PR:

raster3

Offset and cellsize where calculated according to voxel envelope (red box) but it should have been calculated from raster samples envelope. This lead distort sample positions.

In most cases that was almost invisible. But with large cells and tiling, we could see a gap in resulting values between tiles (as samples are not distorted the same way by adjacent tiles).

Raster sample selection issue

Selecting samples we need may be a issue.

For GeoTiff, we rely on Geotools GridCoverage2D to compute the set of samples needed. This causes an issue as samples are not considered as dot but as squares (or cells). Sample is at cell center (this could be different in some geotiff files, to be dug).

If selection contains only intersecting cells, we will have something like that:

envelope

We need data for voxels in the red box.

Selected samples will be the purple ones because their cell intersect with our red box.

The voxel marked with the red dot is missing some sample to be able to interpolate a value. Missing samples are drawn in light blue. They are missing because the edge of the red box is closer to the sample on the left. On top of the red box, we have all samples we need because the top of the red box is closer to outer samples.

Red box is drawn as if we were considering voxels as cells but actually, we consider them as dots. So the actual box is half a voxel smaller in every direction.

This issue is not really solved in that PR. We simply grow the sample envelope by two in all direction so we are sure to have enough samples.

Dot-Samples or Square-samples issues

TODO

TODOs

  • Complete PR text
  • Test how GeoTIFF data behaves on image borders
  • Recheck WMS raster data management to see if it is correct according to the new process

Self-checks

  • The code has unit tests associated
  • The code has Javadoc Comments associated
  • Complex / Unexpected code is explained / justified with a small comment
  • Relevant documentation inside the /docs folder has been updated
  • All examples in examples/ work the same (or have been adapted if subject to changes in this PR)
  • Git history is clean (each commit accomplish a single task and describe it accordingly)
  • The texts have been proofread (documentation, error messages, logs, comments...)

@github-actions

Copy link
Copy Markdown

[Maven Build Status]

📑 Commit: 00b96e95b7ed61e026dbdfc93e43ee819d7cb42b
⌚️ Date: 2026-10-12T17:11:32 (CEST)
🛠️ Status: ✅ Success

📦 Download artifact: voxatile.jar

@indyteo indyteo added type: bug Something isn't working size: small Relatively small change type: technical Refactoring or technical change not affecting the result directly labels Oct 13, 2026
Comment on lines +72 to +88
// Compute in grid envelope (pixel envelope)
GridEnvelope2D gridEnvelope;
try {
gridEnvelope = grid.getGridGeometry().worldToGrid(envelope);
} catch (FactoryException | TransformException | org.geotools.api.referencing.operation.TransformException e) {
} catch (InvalidGridGeometryException | org.geotools.api.referencing.operation.TransformException e) {
throw new GenerationFailedException(e);
}

// This will ensure we have enough pixels for interpolation
// - First, we need to be sure we always have four samples around each voxel center.
// I.E: we need to be sure we always include outer samples
// gridEnvelope only includes a sample if original envelope overlaps its pixel.
// this means outer sample is only included if envelope limit is closer to them than to inner sample.
// - Then, we have to fix envelope computation which excludes last line/column.
// We could do that more accurately. Growing by 2 pixel in every direction will work but may include one or two useless pixel columns or lines.
// TODO: grown envelope may overflow out of raster data. Is this correct?
gridEnvelope.grow(2, 2);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you looked at worldToGrid method? Inside, you can see it is computed using the lower and upper bounds. Maybe we should replace its call with a function of our own, with lower-1 and upper+1 bounds? Or maybe not 1, but something depending on voxel size?

It would probably be the cleanest option to avoid the grow

@pyrollo pyrollo Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw there may be a field or something in GeoTIFF format that should tell where is the anchor and worldToGrid does not take it in account anyway..
Having our own function is probably the cleanest solution but... does it worth it? I'm wondering. Grow will not increase amount of fetched data much. Maybe we could do that now and improve that with margin management development.

@pyrollo pyrollo Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course I still have to check the overflow issue. If it works as is, that would also mean that we don't need to intersect envelopes above (would be strange, but why not).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intersection was needed to avoid out of bounds exception iirc, so I guess the grow will need to be checked

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: small Relatively small change type: bug Something isn't working type: technical Refactoring or technical change not affecting the result directly

Projects

None yet

2 participants