Skip to content
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

Support for 3D transformations and PROJ pipelines #2433

Open
scottyhq opened this issue Apr 8, 2022 · 18 comments
Open

Support for 3D transformations and PROJ pipelines #2433

scottyhq opened this issue Apr 8, 2022 · 18 comments

Comments

@scottyhq
Copy link

scottyhq commented Apr 8, 2022

Expected behavior and actual behavior.

Currently it seems rasterio.warp.reproject does not perform 3D reprojections, for example converting elevation rasters from geoid to ellipsoid heights (CompoundCRS -> CompoundCRS).

Below is an example for a relatively straightforward transform (projinfo -s EPSG:9518 -t EPSG:7661 -o PROJ --hide-ballpark --spatial-test intersects). Looking at DEBUG logs rasterio does invoke PROJ which retrieves the correct vertical shift grid, but the grid is not applied to raster values as expected.

The PROJ CRS conversion guide shows how tricky these pipelines can get. But often for modern datasets such as Copernicus DEM, it's just a matter of applying a vertical shift grid.

Previous discussion with @snowman2 (corteva/rioxarray#494) suggests this could be fixed by #1990?

Steps to reproduce the problem.

import rasterio #1.2.10, gdal=3.4.1, proj=8.2.1
from rasterio.warp import calculate_default_transform, reproject, Resampling
import numpy as np
import logging
import os

# Necessary to expose PROJ-level logs
os.environ["PROJ_DEBUG"] = "2"

logging.basicConfig(level=logging.DEBUG,
                    handlers=[logging.StreamHandler(),
                              logging.FileHandler('rasterio.log')], 
                    )


url = 'https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif'

with rasterio.Env(GDAL_DISABLE_READDIR_ON_OPEN='EMPTY_DIR',
                   AWS_NO_SIGN_REQUEST='YES',
                   CPL_DEBUG=True,
                   ):
    

    with rasterio.open(url) as src:        
        src_data = src.read()
        print(src_data.mean()) 
        
        # original metadata unaware of vertical reference
        src_crs3D = rasterio.crs.CRS.from_epsg('9518')
        dst_crs = rasterio.crs.CRS.from_epsg('7661')    
        transform, width, height = calculate_default_transform(src_crs3D, 
                                                               dst_crs, 
                                                               src.width, 
                                                               src.height, 
                                                               *src.bounds)
        kwargs = src.meta.copy()
        kwargs.update({
            'crs': dst_crs,
            'transform': transform,
            'width': width,
            'height': height
        })
        
        with rasterio.open('test7661.tif', 'w', **kwargs) as dst:
            dst_data = np.zeros((1,width,height))
            reproject(source=src_data,
                      destination=dst_data,
                      src_transform=src.transform,
                      src_crs=src_crs3D,
                      dst_transform=transform,
                      dst_crs=dst_crs,
                      resampling=Resampling.nearest
                     )
            
            # Expect dst_data (ellipsoid) mean to be -15 meters
            print(dst_data.mean())
            dst.write(dst_data)
            
            np.testing.assert_equal(src_data, dst_data)
            print('Warning: array values unchanged')

DEBUG log

Click to show log
DEBUG:rasterio.env:Entering env context: <rasterio.env.Env object at 0x7f3d331d4af0>
DEBUG:rasterio.env:Starting outermost env
DEBUG:rasterio.env:No GDAL environment exists
DEBUG:rasterio.env:New GDAL environment <rasterio._env.GDALEnv object at 0x7f3d331c2a90> created
DEBUG:rasterio._env:GDAL_DATA found in environment.
DEBUG:rasterio._env:PROJ_LIB found in environment.
DEBUG:rasterio._env:Started GDALEnv: self=<rasterio._env.GDALEnv object at 0x7f3d331c2a90>.
DEBUG:rasterio.env:Entered env context: <rasterio.env.Env object at 0x7f3d331d4af0>
DEBUG:rasterio.env:Got a copy of environment <rasterio._env.GDALEnv object at 0x7f3d331c2a90> options
DEBUG:rasterio.env:Entering env context: <rasterio.env.Env object at 0x7f3ce2b77640>
DEBUG:rasterio.env:Got a copy of environment <rasterio._env.GDALEnv object at 0x7f3d331c2a90> options
DEBUG:rasterio.env:Entered env context: <rasterio.env.Env object at 0x7f3ce2b77640>
DEBUG:rasterio._base:Sharing flag: 0
DEBUG:rasterio._env:CPLE_None in HTTP: libcurl/7.82.0 OpenSSL/1.1.1l zlib/1.2.11 libssh2/1.10.0 nghttp2/1.47.0
DEBUG:rasterio._env:CPLE_None in VSICURL: GetFileSize(https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif)=40712353  response_code=200
DEBUG:rasterio._env:CPLE_None in VSICURL: Downloading 0-16383 (https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif)...
DEBUG:rasterio._env:CPLE_None in VSICURL: Got response_code=206
DEBUG:rasterio._env:CPLE_None in GDAL: GDALOpen(/vsicurl/https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif, this=0x55c5df989de0) succeeds as GTiff.
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(proj.db): call fopen(/srv/conda/envs/notebook/share/proj/proj.db) - succeeded
DEBUG:rasterio._base:Nodata success: 0, Nodata value: -10000000000.000000
DEBUG:rasterio._base:Dataset <open DatasetReader name='https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif' mode='r'> is started.
DEBUG:rasterio.env:Exiting env context: <rasterio.env.Env object at 0x7f3ce2b77640>
DEBUG:rasterio.env:Cleared existing <rasterio._env.GDALEnv object at 0x7f3d331c2a90> options
DEBUG:rasterio._env:Stopped GDALEnv <rasterio._env.GDALEnv object at 0x7f3d331c2a90>.
DEBUG:rasterio.env:No GDAL environment exists
DEBUG:rasterio.env:New GDAL environment <rasterio._env.GDALEnv object at 0x7f3d331c2a90> created
DEBUG:rasterio._env:GDAL_DATA found in environment.
DEBUG:rasterio._env:PROJ_LIB found in environment.
DEBUG:rasterio._env:Started GDALEnv: self=<rasterio._env.GDALEnv object at 0x7f3d331c2a90>.
DEBUG:rasterio.env:Exited env context: <rasterio.env.Env object at 0x7f3ce2b77640>
DEBUG:rasterio._io:Output nodata value read from file: None
DEBUG:rasterio._io:Output nodata values: [None]
DEBUG:rasterio._env:CPLE_None in GTiff: ScanDirectories()
DEBUG:rasterio._env:CPLE_None in GTiff: Opened 1800x1800 overview.
DEBUG:rasterio._env:CPLE_None in GTiff: Opened 900x900 overview.
DEBUG:rasterio._env:CPLE_None in GTiff: Opened 450x450 overview.
DEBUG:rasterio._io:all_valid: True
DEBUG:rasterio._io:mask_flags: ([<MaskFlags.all_valid: 1>],)
DEBUG:rasterio._io:Jump straight to _read()
DEBUG:rasterio._io:Window: None
DEBUG:rasterio._io:IO window xoff=0.0 yoff=0.0 width=3600.0 height=3600.0
DEBUG:rasterio._env:CPLE_None in VSICURL: Downloading 10633216-19234815 (https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif)...
DEBUG:rasterio._env:CPLE_None in VSICURL: Got response_code=206
DEBUG:rasterio._env:CPLE_None in GDAL: GDAL_CACHEMAX = 785 MB
DEBUG:rasterio._env:CPLE_None in VSICURL: Downloading 19234816-27836415 (https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif)...
DEBUG:rasterio._env:CPLE_None in VSICURL: Got response_code=206
DEBUG:rasterio._env:CPLE_None in VSICURL: Downloading 27836416-36438015 (https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif)...
DEBUG:rasterio._env:CPLE_None in VSICURL: Got response_code=206
DEBUG:rasterio._env:CPLE_None in VSICURL: Downloading 36438016-40712352 (https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif)...
DEBUG:rasterio._env:CPLE_None in VSICURL: Got response_code=206
DEBUG:rasterio._env:CPLE_None in VRT: No valid sources found for band in VRT file 
DEBUG:rasterio._env:CPLE_None in GDAL: GDALOpen(<VRTDataset rasterYSize="3600" rasterXSize="3600"><VRTRasterBand /><SRS>COMPD_CS["WGS 84 + EGM2008 height",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]],VERT_CS["EGM2008 height",VERT_DATUM["EGM2008 geoid",2005,AUTHORITY["EPSG","1027"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Gravity-related height",UP],AUTHORITY["EPSG","3855"]],AUTHORITY["EPSG","9518"]]</SRS><GeoTransform>-109.00013888888888,0.0002777777777777778,0.0,40.00013888888889,0.0,-0.0002777777777777778</GeoTransform></VRTDataset>, this=0x55c5dfbb9330) succeeds as VRT.
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(proj.ini): call fopen(/srv/conda/envs/notebook/share/proj/proj.ini) - succeeded
DEBUG:rasterio._env:CPLE_None in GDAL: Computing area of interest: -109, 39.0001, -108, 40.0001
DEBUG:rasterio._env:CPLE_None in OGRCT: Wrap source at -108.5.
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(us_nga_egm08_25.tif): call fopen(/srv/conda/envs/notebook/share/proj/us_nga_egm08_25.tif) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(egm08_25.gtx): call fopen(/srv/conda/envs/notebook/share/proj/egm08_25.gtx) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: Using https://cdn.proj.org/us_nga_egm08_25.tif
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(us_nga_egm08_25.tif): call fopen(/srv/conda/envs/notebook/share/proj/us_nga_egm08_25.tif) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(egm08_25.gtx): call fopen(/srv/conda/envs/notebook/share/proj/egm08_25.gtx) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: Using https://cdn.proj.org/us_nga_egm08_25.tif
DEBUG:rasterio._warp:Created transformer and warp output.
DEBUG:rasterio._env:CPLE_None in GDAL: GDALClose(<VRTDataset rasterYSize="3600" rasterXSize="3600"><VRTRasterBand /><SRS>COMPD_CS["WGS 84 + EGM2008 height",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]],VERT_CS["EGM2008 height",VERT_DATUM["EGM2008 geoid",2005,AUTHORITY["EPSG","1027"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Gravity-related height",UP],AUTHORITY["EPSG","3855"]],AUTHORITY["EPSG","9518"]]</SRS><GeoTransform>-109.00013888888888,0.0002777777777777778,0.0,40.00013888888889,0.0,-0.0002777777777777778</GeoTransform></VRTDataset>, this=0x55c5dfbb9330)
DEBUG:rasterio.env:Got a copy of environment <rasterio._env.GDALEnv object at 0x7f3d331c2a90> options
DEBUG:rasterio.env:Entering env context: <rasterio.env.Env object at 0x7f3ce2b776d0>
DEBUG:rasterio.env:Got a copy of environment <rasterio._env.GDALEnv object at 0x7f3d331c2a90> options
DEBUG:rasterio.env:Entered env context: <rasterio.env.Env object at 0x7f3ce2b776d0>
DEBUG:rasterio._io:Path: UnparsedPath(path='test7661.tif'), mode: w, driver: GTiff
DEBUG:rasterio._env:CPLE_None in GDAL: GDALOpen(test7661.tif, this=0x55c5e270c0b0) succeeds as GTiff.
DEBUG:rasterio._env:CPLE_None in GDAL: GDALClose(test7661.tif, this=0x55c5e270c0b0)
DEBUG:rasterio._env:CPLE_None in GDAL: GDALOpen(test7661.tif, this=0x55c5e270c0b0) succeeds as GTiff.
DEBUG:rasterio._env:CPLE_None in GDAL: GDALDefaultOverviews::OverviewScan()
DEBUG:rasterio._env:CPLE_None in GDAL: GDALClose(test7661.tif, this=0x55c5e270c0b0)
DEBUG:rasterio._env:CPLE_None in GDAL: GDALDriver::Create(GTiff,test7661.tif,3600,3600,1,Float32,(nil))
DEBUG:rasterio._base:Nodata success: 0, Nodata value: -10000000000.000000
DEBUG:rasterio.env:Exiting env context: <rasterio.env.Env object at 0x7f3ce2b776d0>
DEBUG:rasterio.env:Cleared existing <rasterio._env.GDALEnv object at 0x7f3d331c2a90> options
DEBUG:rasterio._env:Stopped GDALEnv <rasterio._env.GDALEnv object at 0x7f3d331c2a90>.
DEBUG:rasterio.env:No GDAL environment exists
DEBUG:rasterio.env:New GDAL environment <rasterio._env.GDALEnv object at 0x7f3d331c2a90> created
DEBUG:rasterio._env:GDAL_DATA found in environment.
DEBUG:rasterio._env:PROJ_LIB found in environment.
DEBUG:rasterio._env:Started GDALEnv: self=<rasterio._env.GDALEnv object at 0x7f3d331c2a90>.
DEBUG:rasterio.env:Exited env context: <rasterio.env.Env object at 0x7f3ce2b776d0>
DEBUG:rasterio._env:CPLE_None in GDAL: GDALDriver::Create(MEM,7225a08d-dbee-4187-8a56-a2b4d08fbc51,3600,3600,1,Float32,(nil))
DEBUG:rasterio._io:Set CRS on temp dataset: b'COMPD_CS["WGS 84 + EGM2008 height",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]],VERT_CS["EGM2008 height",VERT_DATUM["EGM2008 geoid",2005,AUTHORITY["EPSG","1027"]],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Gravity-related height",UP],AUTHORITY["EPSG","3855"]],AUTHORITY["EPSG","9518"]]'
DEBUG:rasterio._env:CPLE_None in GDAL: GDALDriver::Create(MEM,b559f253-e65c-4b36-b841-db65bf606076,3600,3600,1,Float64,(nil))
DEBUG:rasterio._io:Set CRS on temp dataset: b'GEOGCRS["WGS 84 (G1150)",DYNAMIC[FRAMEEPOCH[2001]],DATUM["World Geodetic System 1984 (G1150)",ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,3],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],AXIS["ellipsoidal height (h)",up,ORDER[3],LENGTHUNIT["metre",1]],USAGE[SCOPE["Geodesy. Navigation and positioning using GPS satellite system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",7661]]'
DEBUG:rasterio._warp:Created temp destination dataset.
DEBUG:rasterio._env:CPLE_None in GDAL: Computing area of interest: -109, 39.0001, -108, 40.0001
DEBUG:rasterio._warp:Created approximate transformer
DEBUG:rasterio._warp:Created transformer and options.
DEBUG:rasterio._warp:Setting NUM_THREADS option: 1
DEBUG:rasterio._warp:Configured to warp src band 1 to destination band 1
DEBUG:rasterio._warp:Set transformer options
DEBUG:rasterio._env:CPLE_None in PROJ: Using coordinate operation WGS 84 to EGM2008 height (1)
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(us_nga_egm08_25.tif): call fopen(/srv/conda/envs/notebook/share/proj/us_nga_egm08_25.tif) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(egm08_25.gtx): call fopen(/srv/conda/envs/notebook/share/proj/egm08_25.gtx) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: Using https://cdn.proj.org/us_nga_egm08_25.tif
DEBUG:rasterio._env:CPLE_None in PROJ: Using coordinate operation WGS 84 to EGM2008 height (1)
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(us_nga_egm08_25.tif): call fopen(/srv/conda/envs/notebook/share/proj/us_nga_egm08_25.tif) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(egm08_25.gtx): call fopen(/srv/conda/envs/notebook/share/proj/egm08_25.gtx) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: Using https://cdn.proj.org/us_nga_egm08_25.tif
DEBUG:rasterio._warp:Chunk and warp window: 0, 0, 3600, 3600.
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(us_nga_egm08_25.tif): call fopen(/srv/conda/envs/notebook/share/proj/us_nga_egm08_25.tif) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: pj_open_lib(egm08_25.gtx): call fopen(/srv/conda/envs/notebook/share/proj/egm08_25.gtx) - failed
DEBUG:rasterio._env:CPLE_None in PROJ: Using https://cdn.proj.org/us_nga_egm08_25.tif
DEBUG:rasterio._env:CPLE_None in GDAL: GDALWarpKernel()::GWKRealCase() Src=0,0,1800x1800 Dst=0,0,1800x1800
DEBUG:rasterio._env:CPLE_None in GDAL: GDALWarpKernel()::GWKRealCase() Src=1800,0,1800x1800 Dst=1800,0,1800x1800
DEBUG:rasterio._env:CPLE_None in GDAL: GDALWarpKernel()::GWKRealCase() Src=0,1800,1800x1800 Dst=0,1800,1800x1800
DEBUG:rasterio._env:CPLE_None in GDAL: GDALWarpKernel()::GWKRealCase() Src=1800,1800,1800x1800 Dst=1800,1800,1800x1800
DEBUG:rasterio._env:CPLE_None in GDAL: GDALClose(Temporary destination dataset for _reproject(), this=0x55c5dee96a90)
DEBUG:rasterio._env:CPLE_None in GDAL: GDALClose(7225a08d-dbee-4187-8a56-a2b4d08fbc51, this=0x55c5dfc595a0)
DEBUG:rasterio._env:CPLE_None in GDAL: GDALClose(test7661.tif, this=0x55c5e270c0b0)
DEBUG:rasterio._env:CPLE_None in GDAL: GDALClose(/vsicurl/https://copernicus-dem-30m.s3.eu-central-1.amazonaws.com/Copernicus_DSM_COG_10_N39_00_W109_00_DEM/Copernicus_DSM_COG_10_N39_00_W109_00_DEM.tif, this=0x55c5df989de0)
DEBUG:rasterio.env:Exiting env context: <rasterio.env.Env object at 0x7f3d331d4af0>
DEBUG:rasterio.env:Cleared existing <rasterio._env.GDALEnv object at 0x7f3d331c2a90> options
DEBUG:rasterio._env:Stopped GDALEnv <rasterio._env.GDALEnv object at 0x7f3d331c2a90>.
DEBUG:rasterio.env:Exiting outermost env
DEBUG:rasterio.env:Exited env context: <rasterio.env.Env object at 0x7f3d331d4af0>

Operating system

Ubuntu 18.04.6 LTS

Rasterio version and provenance

rasterio 1.2.10 (conda-forge). also tested via pypi install

@snowman2
Copy link
Member

snowman2 commented Apr 8, 2022

One thing @scottyhq mentioned in the previous discussion is that gdalwarp transformed the elevation values:

gdalwarp -s_srs EPSG:4326+3855 -t_srs EPSG:7661 $INPUT $OUTPUT

@sgillies
Copy link
Member

sgillies commented Apr 8, 2022

Yeah, we really need to prioritize #1990. It's on my mind for 1.3 and I may even get to do something about it next week.

@sgillies
Copy link
Member

sgillies commented Apr 13, 2022

I think this is a duplicate of #2354 after all. Or not 😄

@sgillies sgillies reopened this Apr 13, 2022
@sgillies
Copy link
Member

@scottyhq if I read https://github.com/OSGeo/gdal/blob/master/apps/gdalwarp_lib.cpp#L613-L614 correctly, vertical grid shift with GDAL 3.4 might be "just" this: reproject(..., apply_vertical_shift=True). The unnamed keyword arguments of reproject get turned into GDAL transformer and warper options. Can you give it a try?

My work on #1990 is blocked by missing features of the GDALWarp API, so I'm looking at other ways around the problem.

@scottyhq
Copy link
Author

Thanks for looking into this @sgillies! I wonder if that could be set as a default somehow to mirror gdalwarp behavior? "Starting with GDAL 2.2, if the SRS has an explicit vertical datum that points to a PROJ.4 geoidgrids, and the input dataset is a single band dataset, a vertical correction will be applied to the values of the dataset."

I can confirm apply_vertical_shift=True does apply a shift (DEBUG:rasterio._warp:Set _reproject Transformer option b'APPLY_VERTICAL_SHIFT'=b'TRUE')

But... The result is not the same result as gdalwarp (which seems to more accurately reflect the shift grid values from https://cdn.proj.org/us_nga_egm08_25.tif). I think this is best illustrated with a figure:

Screen Shot 2022-04-14 at 11 15 38 AM

@sgillies
Copy link
Member

@scottyhq are you sure you're using the same versions of GDAL, PROJ, its grids, on each side of the comparison? It's easy to get into a place where we have software and data discrepancies.

@scottyhq
Copy link
Author

the same versions of GDAL, PROJ, its grids, on each side of the comparison? It's easy to get into a place where we have software and data discrepancies.

As far as I can tell, yes. I saw #2349, and wasn't quite sure how to verify command line gdal and dependencies match the rasterio wheels, so I'm going off https://github.com/rasterio/rasterio-wheels/blob/master/env_vars.sh:

rasterio=1.3a3
gdal=3.4.1
proj=8.2.1
* latest grid from https://cdn.proj.org/us_nga_egm08_25.tif

I've gone ahead and documented things in more detail here using https://github.com/scottyhq/riodatum. Since this is all public data you can run the exact same environment and generate the above figure via Binder.

One thing that jumps out at me is what looks like a vertical artifact at -108.5 longitude, and I notice the rasterio log outputs the following: DEBUG:rasterio._env:CPLE_None in OGRCT: Wrap source at -108.5. I'm still a bit unclear on where the code for applying these grids lives (OSGeo/gdal#5414 (comment)).

@scottyhq
Copy link
Author

scottyhq commented May 5, 2022

One thing that jumps out at me is what looks like a vertical artifact at -108.5 longitude

After a bit more exploring, this artifact appears to be coming from the warp_mem_limit setting. With defaults we get chunks dividing this image into 4 chunks (GDAL: GDALWarpKernel()::GWKRealCase() Src=0,1800,1800x1800 Dst=0,1800,1800x1800). Just to illustrate if I set warp_mem_limit=10 we get closer to matching GDAL b/c the sampling appears to match at the edges of these warp chunk boundaries. So I'm guessing there is some issue with how samples are passed to the grid sampling CPP code in GDAL?

Screen Shot 2022-05-05 at 12 03 46 PM

@snowman2
Copy link
Member

snowman2 commented May 5, 2022

wasn't quite sure how to verify command line gdal and dependencies match the rasterio wheels

With rasterio==1.3a4:

rio --show-versions or python -c "import rasterio; rasterio.show_versions()"

@scottyhq
Copy link
Author

scottyhq commented May 5, 2022

I observe the same behavior documented above with rasterio=1.3a4, but it seems that brings GDAL3.4.2 and PROJ8.2.1 whereas the GDAL3.4.2 docker containers or GDAL from conda-forge bring PROJ 9.0.0.

I also tried passing the pipeline directly to reproject in 1.3a4, but the result is the same as documented above:

    proj_pipeline="proj=pipeline step proj=axisswap order=2,1 step proj=unitconvert xy_in=deg xy_out=rad step proj=vgridshift grids=us_nga_egm08_25.tif multiplier=1 step proj=unitconvert xy_in=rad xy_out=deg step proj=axisswap order=2,1"
    with rasterio.open(url) as src:                
        # original metadata unaware of vertical reference
        src_crs3D = rasterio.crs.CRS.from_epsg('9518')
        dst_crs = rasterio.crs.CRS.from_epsg('7661')    
        transform, width, height = calculate_default_transform(src_crs3D, 
                                                               dst_crs, 
                                                               src.width, 
                                                               src.height, 
                                                               *src.bounds,
                                                               coordinate_operation=proj_pipeline)
        kwargs = src.meta.copy()
        kwargs.update({
            'crs': dst_crs,
            'transform': transform,
            'width': width,
            'height': height
        })
        
        with rasterio.open('epsg7661_rio13.tif', 'w', **kwargs) as dst:
            reproject(
                      rasterio.band(src, src.indexes),
                      rasterio.band(dst, dst.indexes),
                      resampling=Resampling.nearest,
                      apply_vertical_shift=True,
                      coordinate_operation=proj_pipeline,
                      warp_mem_limit=10,
                     )

@snowman2
Copy link
Member

snowman2 commented May 5, 2022

I observe the same behavior documented above with rasterio=1.3a4, but it seems that brings GDAL3.4.2 and PROJ8.2.1 whereas the GDAL3.4.2 docker containers or GDAL from conda-forge bring PROJ 9.0.0.

Need to install with --no-binary rasterio if you want to install rasterio into the Conda environment or the Docker container and have the same version of GDAL.

@scottyhq
Copy link
Author

Need to install with --no-binary rasterio if you want to install rasterio into the Conda environment or the Docker container and have the same version of GDAL.

@snowman2 - I'm fairly convinced there is something with warp_mem_limit at the root of this issue rather than versions being matched (#2433 (comment)).

Nevertheless, I tried the following with rasterio 1.3b1 (perhaps this deserves a separate issue or discussion thread):

docker run --rm -it osgeo/gdal:ubuntu-small-3.5.0  bash
apt install pip
pip install rasterio==1.3b1 --no-binary rasterio
python
import rasterio
rasterio.show_versions()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.8/dist-packages/rasterio/__init__.py", line 13, in <module>
    from rasterio.crs import CRS
  File "rasterio/crs.pyx", line 1, in init rasterio.crs
  File "rasterio/_base.pyx", line 31, in init rasterio._base
  File "/usr/local/lib/python3.8/dist-packages/rasterio/transform.py", line 14, in <module>
    from rasterio.env import env_ctx_if_needed
  File "/usr/local/lib/python3.8/dist-packages/rasterio/env.py", line 16, in <module>
    from rasterio._env import (
  File "rasterio/_env.pyx", line 1, in init rasterio._env
ImportError: /usr/local/lib/python3.8/dist-packages/rasterio/_filepath.cpython-38-x86_64-linux-gnu.so: undefined symbol: VSIAllocFilesystemPluginCallbacksStruct

@snowman2
Copy link
Member

I tried the following with rasterio 1.3b1 (perhaps this deserves a separate issue or discussion thread):

docker run --rm -it osgeo/gdal:ubuntu-small-3.5.0  bash
apt install pip
pip install rasterio==1.3b1 --no-binary rasterio
python
import rasterio
rasterio.show_versions()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.8/dist-packages/rasterio/__init__.py", line 13, in <module>
    from rasterio.crs import CRS
  File "rasterio/crs.pyx", line 1, in init rasterio.crs
  File "rasterio/_base.pyx", line 31, in init rasterio._base
  File "/usr/local/lib/python3.8/dist-packages/rasterio/transform.py", line 14, in <module>
    from rasterio.env import env_ctx_if_needed
  File "/usr/local/lib/python3.8/dist-packages/rasterio/env.py", line 16, in <module>
    from rasterio._env import (
  File "rasterio/_env.pyx", line 1, in init rasterio._env
ImportError: /usr/local/lib/python3.8/dist-packages/rasterio/_filepath.cpython-38-x86_64-linux-gnu.so: undefined symbol: VSIAllocFilesystemPluginCallbacksStruct

I am seeing similar, slightly different, errors with a GDAL 3.5 test here: https://github.com/rasterio/rasterio/runs/6538351664

Do you see a similar issue with GDAL 3.4?

@scottyhq
Copy link
Author

scottyhq commented Aug 2, 2022

@sgillies @snowman2 I reran this analysis with the latest matched GDAL and rasterio and am pretty confident it is a bug. Interestingly, I'm observing that reducing warp_mem_limit eventually converges to the same result as GDAL. Here is another reproducible example using SRTM elevations https://gist.github.com/fd70d40e494db5c54a796555f6e46e38. The gist generates this plot, which shows what I am observing:

Screen Shot 2022-08-02 at 11 58 56 AM

So maybe breaking the image up by warp_mem_limit affects the vertical shift grid sampling? I've had a look at the logs but can't quite figure out how the code paths differ between what GDAL is doing vs rasterio...

@snowman2
Copy link
Member

snowman2 commented Aug 9, 2022

I wonder if there is any connection with this fix #2512?

@snowman2
Copy link
Member

Wonder if this is related? conda-forge/rasterio-feedstock#239

@rhugonnet
Copy link

This thread reminds me of our discussion with @snowman2 here: pyproj4/pyproj#761.
I ended up using that to implement a vertical reference transformation in xDEM: https://github.com/GlacioHack/xdem/blob/main/xdem/dem.py#L167 (need to write more tests in the CI)

Is it really useful to have this implemented directly in rasterio in addition to pyproj? I'm not sure I grasp the difference and what more it would bring.

(btw, hi @scottyhq! I just started at UW)

@scottyhq
Copy link
Author

@snowman @sgillies apologies for letting this issue drift a bit with intermediate updates, but as documented above we've seen that PROJ pipelines are supported in rasterio>=1.3, and rasterio does make use shift grids as long as the apply_vertical_shift=True is passed to reproject()

However, for outputs requiring a vertical shift grid rasterio is giving incorrect outputs apparently due to different sampling of the shift grid compared to gdalwarp. I keep seeing this for all recent versions of rasterio no matter how it's installed. I can open a separate issue for this specific bug if that would help organize things. The discrepancy is best illustrated using a coarse grid (like EGM96 for SRTM data below):

Screenshot 2023-03-15 at 8 58 17 AM

code for above figure: https://gist.github.com/scottyhq/af8ee3c440cbe0a3a5ffaa538643b104

It's hard to debug this further because I'm not sure how inspect intermediate data structures or follow the difference in code paths between GDAL and rasterio. It was suggested earlier that switching _reproject to GDALWarp internally might fix this and the previous blocker seems fixed on the GDAL side to continue pursuing that (#2437 (comment))? I'd be happy to test if possible.

The only differences standing out to me in logs at this point are:

  1. Rasterio creating an intermediate VRT (DEBUG:CPLE_None in GDAL: GDALOpen(<VRTDataset>...)
  2. Rasterio using GDALWarpKernel()::GWKRealCase() versus GDAL's GDALWarpKernel()::GWKNearestFloat()

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

No branches or pull requests

4 participants