Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

oshawa-connection
Copy link
Contributor

@oshawa-connection oshawa-connection commented May 14, 2025

As discussed in #495 I have created a first draft to see if the approach is ok.
Users have to do something like this in their code. Not particularly friendly, but it does match roughly how users have to load NTV2 grids from an arraybuffer.

proj4.defs("EPSG:27700_tif","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +nadgrids=britishtif +units=m +no_defs +type=crs");

async function loadGeotiff() {
    const response = await fetch('uk_os_OSTN15_NTv2_OSGBtoETRS.tif');
    const arrayBuffer = await response.arrayBuffer();
    const tiff = await fromArrayBuffer(arrayBuffer);
    const image = await tiff.getImage();
    const extent = image.getBoundingBox(); // comes out as: [-9, 48.99166666666667, 2.0166666666666657, 61]
    const rasters = await image.readRasters();
    
    const fakeExtent = [-9,49, 2, 61]; // ATTENTION! Need this part to match what comes from equivalent ntv2 file.
    const result = proj4.nadtifgrid('britishtif', rasters, {imageWidth:image.getWidth(), imageHeight:image.getHeight() extent: fakeExtent });
    
    console.log(proj4('EPSG:4326', 'EPSG:27700_tif', wgs84Coords)); // matches
}

Secondly, I am a little bit stuck because of a small (but significant) difference between the british .gsb and .tif calculated extents - I'm not sure how to handle this. When proj4js reads from the british NTV2 file (OSTN15_NTv2_OSGBtoETRS.gsb), it determines the extent to be [-9,49, 2, 61]; whereas geotiff.js returns [-9, 48.99166666666667, 2.0166666666666657, 61] from uk_os_OSTN15_NTv2_OSGBtoETRS.tif which results in differences in the calculated projected coordinate.

In QGIS, there is no difference between these two files, so I'm not sure what the cause of this difference is....

@oshawa-connection
Copy link
Contributor Author

oshawa-connection commented May 14, 2025

Ignore the second point, I figured it out...
The user code now looks like:

proj4.defs("EPSG:27700_tif","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +nadgrids=britishtif +units=m +no_defs +type=crs");

async function loadGeotiff() {
    const response = await fetch('uk_os_OSTN15_NTv2_OSGBtoETRS.tif');
    const arrayBuffer = await response.arrayBuffer();
    const tiff = await fromArrayBuffer(arrayBuffer);
    const image = await tiff.getImage();
    const extent = image.getBoundingBox(); // comes out as: [-9, 48.99166666666667, 2.0166666666666657, 61]
    const rasters = await image.readRasters();
    
     proj4.nadtifgrid('britishtif', rasters, { lim:[image.getWidth(), image.getHeight()], ll: extent, del:[image.fileDirectory.ModelPixelScale[0], image.fileDirectory.ModelPixelScale[1]] });
    
    console.log(proj4('EPSG:4326', 'EPSG:27700_tif', wgs84Coords)); // matches
}

The function call needs a bit of work to make it a bit more user friendly, but what do you think of the approach in general?

@ahocevar
Copy link
Member

Thanks for putting this together, @oshawa-connection. The general approach looks good!

Like you suggest, the API could be simplified - since the configuration object contains geotiff.js specific data anyway, you could pass the geotiff object directly. Instead of introducing a new proj4.nadtifgrid function, the existing proj4.nadgrid could be used and detect a TIFF if the provided data is not an instance of ArrayBuffer.

proj4.defs("EPSG:27700_tif","+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +nadgrids=britishtif +units=m +no_defs +type=crs");

async function loadGeotiff() {
    const response = await fetch('uk_os_OSTN15_NTv2_OSGBtoETRS.tif');
    const arrayBuffer = await response.arrayBuffer();
    const tiff = await fromArrayBuffer(arrayBuffer);
    
    await proj4.nadgrid('britishtif', tiff).ready;
    
    console.log(proj4('EPSG:4326', 'EPSG:27700_tif', wgs84Coords)); // matches
}

@oshawa-connection
Copy link
Contributor Author

oshawa-connection commented May 17, 2025

Thanks for the feedback, that's implemented so hopefully a bit easier on users now :)

  • I found a good example of a GeoTIFF with subgrids, those are handled.
  • It looks like proj structures these so that the higher resolution subgrids are last, with low res subgrids first. I reverse this order so that we preferentially select applicable high res subgrids.
  • I tested these changes against a handful of different .tif files + coord systems, but in my automated tests I only used the Quebec one (which has subgrids) as I don't want to add too many big files to the project. If you want more tests I can potentially clip the tiffs to make them smaller.
  • Sadly, I had to add a dev dependency on geotiff.js library to let me run tests

@oshawa-connection oshawa-connection marked this pull request as ready for review May 17, 2025 14:15
Copy link
Member

@ahocevar ahocevar left a comment

Choose a reason for hiding this comment

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

Thanks @oshawa-connection, this is a great addition!

@ahocevar ahocevar merged commit 766679b into proj4js:master May 17, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants