openlayers icon indicating copy to clipboard operation
openlayers copied to clipboard

sourcesFromTileGrid should translate from view to source projection

Open jcphill opened this issue 4 months ago • 9 comments

Following the example at https://openlayers.org/en/latest/examples/cog-pyramid.html I have a tile grid in EPSG:4269 that I would like to display in EPSG:3857 but the function returned by sourcesFromTileGrid() is passed an extent and resolution in the view projection EPSG:3857 and does not do any translation to EPSG:4269 before building the list of sources to return. At zoom 0 the function returns the coarsest tiles, which are displayed correctly, but zoomed in the function returns an empty list.

The TileGrid should have a projection associated with it, or sourcesFromTileGrid() should assume that the projection of the TileGrid matches that of the sources returned by the source factory function. The function returned by sourcesFromTileGrid() should somehow determine the view projection and do something reasonable to select sources from the grid. Because a single GeoTIFF source reads projected internal tiles correctly I assume that similar logic is already implemented there.

jcphill avatar Jul 17 '25 20:07 jcphill

I am not sure I understand your concern correctly, @jcphill. Is your goal to create a sources grid with sources that are not in view projection? The sourcesFromTileGrid() function was not designed for that. And to be honest, I cannot imagine that what you have in mind would work well, even if it was possible with sourcesFromTileGrid(). The whole idea behind that function is to access available sources for a rectangular grid of constant resolutions, depending on location and resolutions. When reprojected, chances are low that the resulting grid would be rectangular and with the same resolutions throughout its availability extent.

ahocevar avatar Jul 19 '25 18:07 ahocevar

Yes, I want to be able to use any TileGrid with any view projection the same way that I can use any GeoTIFF source with any view projection and the geotiff library correctly retrieves the relevant internal tiles because openlayers requests them in the source projection rather than the view projection. It is surprising that an extent in the view projection is being passed to a source without transformation to the source projection or including information about the view projection.

jcphill avatar Jul 19 '25 19:07 jcphill

If you have an idea how this could be fixed, I'd be interested to see the proposed fixed alongside a real-life example that makes use of the fix. Do you think you'd be able to create a pull request, @jcphill ?

ahocevar avatar Jul 20 '25 18:07 ahocevar

I can try to put something together but I would need guidance. Would it make sense to create a new TileGridSource class?

jcphill avatar Jul 21 '25 12:07 jcphill

@jcphill I don't think a new class would bring any relief here. I also don't see how what you're envisioning would work, so I'm afraid I'm not able to provide any guidance.

ahocevar avatar Jul 21 '25 12:07 ahocevar

Following https://openlayers.org/en/latest/examples/geotiff-reprojection.html, which has

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/WebGLTile.js';
import GeoTIFF from 'ol/source/GeoTIFF.js';

const source = new GeoTIFF({
  sources: [
    {
      url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/36/Q/WD/2020/7/S2A_36QWD_20200701_0_L2A/TCI.tif',
    },
  ],
});

const map = new Map({
  target: 'map',
  layers: [new TileLayer({source})],
  view: new View({
    center: [0, 0],
    zoom: 12,
  }),
});

I am proposing syntax similar to the following:

const tileGrid = new TileGrid({
  extent: [-180, -90, 180, 90],
  resolutions: [0.703125, 0.3515625, 0.17578125, 8.7890625e-2, 4.39453125e-2],
  tileSizes: [
    [512, 256],
    [1024, 512],
    [2048, 1024],
    [4096, 2048],
    [4096, 4096],
  ],
});

const source = new TileGridSource({
    grid: tileGrid,
    sources: ([z, x, y]) =>
      new GeoTIFF({
        sources: [
          {
            url: `https://s2downloads.eox.at/demo/EOxCloudless/2019/rgb/${z}/${y}/${x}.tif`,
          },
        ],
      },
    projection: 'EPSG:4326',
});

const map = new Map({
  target: 'map',
  layers: [new TileLayer({source})],
  view: new View({
    center: [0, 0],
    zoom: 12,
  }),
});

jcphill avatar Jul 21 '25 19:07 jcphill

Yes, I figured you'd imagine something like this. Unfortunately implementing this is not as simple as giving the sourcesFromTileGrid() function an additional projection argument, or creating a TileGridSource with a projection property. Why? Because reprojecting a set of tiles is much more involved than just transforming the tile corner coordinates. See the src/ol/reproj/Tile.js class to get an impression of that.

And once you've put all the effort into implementing that, it might well be that there's not much practical use for it, because meta-gridded sets of GeoTIFFs that support the resolution ranges needed for reprojecting to arbitrary view projections won't be widely available .

So I think the first thing to check here would be to see if there is a real-life example of a meta-gridded set of GeoTIFFs and a global (?) view projection that play well together.

ahocevar avatar Jul 21 '25 20:07 ahocevar

Thanks for those pointers. It looks like DataTileSource does accept a projection option that is processed by src/ol/reproj/DataTile.js and based on https://openlayers.org/en/latest/examples/numpytile.html it shouldn't be too hard to write a loader function that returns all of the data from a 256x256 GeoTIFF file as an Array.

Am I missing something?

jcphill avatar Jul 21 '25 22:07 jcphill

Correct. It is easy to write custom loaders for ol/source/DataTile. So if your goal is to decide in the loader which GeoTIFF to use and load data for the given tile extent, then you could make use of that to achieve what you want.

ahocevar avatar Jul 22 '25 05:07 ahocevar