如何使用Rasterio读取S3中带.aux.xml元数据的栅格文件?
问题
AWS S3存储桶中有一对关联文件:
s3://my_s3_bucket/myraster.tif s3://my_s3_bucket/myraster.tif.aux.xml
使用Rasterio直接从S3加载栅格的代码如下:
fn = 's3://my_s3_bucket/myraster.tif' with rasterio.Env(session, **rio_gdal_options): with rasterio.open(fn) as src: src_nodata = src.nodata scales = src.scales offsets = src.offsets bands = src.tags()['bands']
本地执行时,Rasterio能自动识别并加载.aux.xml元数据文件,正常获取波段标签、缩放比例与偏移量;但在S3环境下,栅格能成功打开,却无法自动加载关联的.aux.xml,导致相关元数据无法读取。由于元数据过大无法嵌入TIF文件,该.aux.xml是Rasterio(底层基于GDAL)生成栅格时自动创建的,请问如何在S3环境下实现自动加载,或有什么替代方案?
解决方案
方法1:开启GDAL目录扫描功能
GDAL处理S3这类远程文件系统时,默认不会主动扫描目录下的辅助文件。可以通过设置GDAL_DISABLE_READDIR_ON_OPEN参数为FALSE,强制GDAL在打开主文件时检查同目录下的辅助文件。修改代码如下:
rio_gdal_options = { 'GDAL_DISABLE_READDIR_ON_OPEN': 'FALSE', # 保留原有其他GDAL参数 } fn = 's3://my_s3_bucket/myraster.tif' with rasterio.Env(session, **rio_gdal_options): with rasterio.open(fn) as src: src_nodata = src.nodata scales = src.scales offsets = src.offsets bands = src.tags()['bands']
注意:该操作会触发S3目录的文件列表读取,若目录内文件数量较多,会增加请求开销和延迟。
方法2:手动指定辅助文件路径
若不想开启目录扫描,可直接将.aux.xml路径告知GDAL,有两种实现方式:
fn = 's3://my_s3_bucket/myraster.tif' aux_fn = 's3://my_s3_bucket/myraster.tif.aux.xml' # 方式1:通过Env环境变量指定 with rasterio.Env(session, GDAL_AUX_FILES=aux_fn, **rio_gdal_options): with rasterio.open(fn) as src: src_nodata = src.nodata scales = src.scales offsets = src.offsets bands = src.tags()['bands'] # 方式2:通过open方法的options参数指定 with rasterio.Env(session, **rio_gdal_options): with rasterio.open(fn, options={'AUX_FILES': aux_fn}) as src: src_nodata = src.nodata scales = src.scales offsets = src.offsets bands = src.tags()['bands']
这种方式无需扫描整个目录,性能更优,适合文件较多的S3目录场景。
方法3:手动解析并注入元数据
如果以上方法都不适用,可单独读取.aux.xml内容,解析后手动将元数据注入到Rasterio数据源中:
import xml.etree.ElementTree as ET import boto3 # 从S3读取aux.xml内容 s3 = boto3.client('s3') bucket = 'my_s3_bucket' key = 'myraster.tif.aux.xml' response = s3.get_object(Bucket=bucket, Key=key) aux_content = response['Body'].read().decode('utf-8') # 解析XML提取元数据 root = ET.fromstring(aux_content) scales = [] offsets = [] band_tags = {} for band_node in root.findall('.//PAMRasterBand'): band_idx = int(band_node.get('band')) scales.append(float(band_node.find('Scale').text)) offsets.append(float(band_node.find('Offset').text)) # 提取波段标签(根据实际XML结构调整) band_tags[band_idx] = band_node.find('Description').text # 打开栅格后手动注入元数据 fn = 's3://my_s3_bucket/myraster.tif' with rasterio.Env(session, **rio_gdal_options): with rasterio.open(fn) as src: src_nodata = src.nodata # 手动赋值解析后的元数据 src.scales = scales src.offsets = offsets # 注入波段标签 for idx, tag in band_tags.items(): src.set_band_tag(idx, 'Description', tag) bands = src.tags()['bands']
该方式灵活性最高,但需要自行适配XML结构和元数据映射逻辑,适合复杂元数据场景。
内容的提问来源于stack exchange,提问作者Kristof
相关产品推荐
相关产品推荐

