前言
在地理信息系统(GIS)和遥感领域中,数据处理是一个核心环节,其中数据的分割、裁剪和拼接是常见的需求。这些操作对于分析局部地区的变化、合成大范围图像或是处理大型数据集时尤为重要。Python作为一种强大的编程语言,结合GDAL库,可以高效地实现这些数据处理任务。本文将详细介绍如何使用Python和GDAL库来进行数据的分割、裁剪与拼接。
分割
这段代码定义了一个名为 split_tiff 的函数,旨在将一个大的 TIFF 文件分割成多个较小的 TIFF 文件块,每个块的大小由参数 n 决定。这里使用了 GDAL 库来处理地理空间数据。
def split_tiff(input_tiff, output_folder, n):
# 打开大的TIFF文件
dataset = gdal.Open(input_tiff)
if dataset is None:
print("无法打开TIFF文件")
return
# 获取TIFF文件的基本信息
width = dataset.RasterXSize
height = dataset.RasterYSize
band_count = dataset.RasterCount
geotransform = dataset.GetGeoTransform()
projection = dataset.GetProjection()
# 计算每个小块的大小
block_width = width // n
block_height = height // n
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 分割TIFF文件为n×n个小块
for i in range(n):
for j in range(n):
# 计算当前小块的范围
x_offset = j * block_width
y_offset = i * block_height # 翻转i以从左下角开始
# 创建小块的文件名
output_filename = os.path.join(output_folder, f"block_{i}_{j}.tif")
print(output_filename)
# 写入小块数据到文件
driver = gdal.GetDriverByName("GTiff")
output_dataset = driver.Create(output_filename, block_width, block_height, band_count, gdal.GDT_Float32)
# 设置小块的地理转换和投影信息
output_dataset.SetGeoTransform((geotransform[0] + x_offset * geotransform[1], geotransform[1], 0,
geotransform[3] + y_offset * geotransform[5], 0, geotransform[5]))
output_dataset.SetProjection(projection)
# 写入小块的数据
for band in range(band_count):
band_data = dataset.GetRasterBand(band + 1).ReadAsArray(x_offset, y_offset, block_width, block_height)
output_dataset.GetRasterBand(band + 1).WriteArray(band_data)
# 关闭输出文件
output_dataset = None
# 关闭输入文件
dataset = None
函数解释
- 打开 TIFF 文件:使用 gdal.Open(input_tiff) 打开输入的 TIFF 文件,并检查是否成功打开。
- 获取文件信息:获取 TIFF 文件的宽度、高度、波段数、地理变换和投影信息。
- 计算小块大小:将大文件的宽度和高度分别除以 n,得到每个小块的宽度和高度。
- 创建输出文件夹:确保输出文件夹存在,如果不存在则创建。
- 分割文件:通过两层循环遍历每个小块的位置,计算每个小块的偏移量,并创建对应的输出文件名。
- 创建输出文件:为每个小块创建一个新的 TIFF 文件,并设置其地理变换和投影信息。
- 写入数据:从原 TIFF 文件中读取每个波段的数据块,并写入到对应的输出文件中。
- 关闭文件:循环结束后,关闭所有打开的文件。
合并
这段代码定义了一个 merge_tiff_blocks 函数,用于将分割成多个块的 TIFF 文件重新拼接成一个完整的 TIFF 文件。
def merge_tiff_blocks(block_folder, output_path, n):
"""
将分割后的多个 TIFF 文件重新拼接成一个单独的 TIFF 文件。
:param block_folder: 存储分割后的 TIFF 文件的文件夹路径。
:param output_path: 输出的单个 TIFF 文件路径。
:param n: 分块的行数或列数。
"""
try:
# 获取第一个分块 TIFF 文件的基本信息
first_block_path = os.path.join(block_folder, f"block_0_0.tif")
first_block_ds = gdal.Open(first_block_path)
output_width = first_block_ds.RasterXSize * n
output_height = first_block_ds.RasterYSize * n
output_projection = first_block_ds.GetProjection()
output_geotransform = first_block_ds.GetGeoTransform()
band_count = first_block_ds.RasterCount
# 创建输出的数据集
output_driver = gdal.GetDriverByName("GTiff")
output_dataset = output_driver.Create(output_path, output_width, output_height, band_count, gdal.GDT_Float32)
output_dataset.SetProjection(output_projection)
output_dataset.SetGeoTransform(output_geotransform)
# 将分块 TIFF 文件写入到输出数据集中
for i in range(n):
for j in range(n):
block_path = os.path.join(block_folder, f"block_{i}_{j}.tif") # 从左上角开始拼接
block_ds = gdal.Open(block_path)
block_data = block_ds.ReadAsArray()
output_dataset.GetRasterBand(1).WriteArray(block_data, j * first_block_ds.RasterXSize,
i * first_block_ds.RasterYSize)
block_ds = None
# 关闭输出数据集和第一个分块 TIFF 文件
output_dataset = None
first_block_ds = None
print(f"成功拼接并保存为 {output_path}")
except Exception as e:
print(f"拼接 TIFF 文件时出现异常:{e}")
裁剪
这段代码定义了一个名为 cutShp 的函数,用于根据给定的 Shapefile (SHP) 文件裁剪一个 GeoTIFF (TIF) 图像。
def cutShp(input_path, shp_path, out_path):
"""
对单个tif图像进行Shp裁剪
:param input_path:文件输入地址
:param shp_path:shp图层输入地址
:param out_path:文件输出地址
:return:
"""
# 打开栅格数据集
input_raster = gdal.Open(input_path)
# 获取输入栅格数据的地理信息
x_origin, x_pixel_size, _, y_origin, _, y_pixel_size = input_raster.GetGeoTransform()
# 打开矢量多边形数据集(研究区域)
clip_polygon_path = shp_path
clip_polygon_ds = ogr.Open(clip_polygon_path)
clip_polygon_layer = clip_polygon_ds.GetLayer()
# 获取裁剪多边形的边界框
clip_extent = clip_polygon_layer.GetExtent()
a = abs(input_raster.GetGeoTransform()[5])
b = input_raster.GetGeoTransform()[1]
xmin, xmax, ymin, ymax = clip_extent
# 获取裁剪多边形的几何
# 创建输出栅格数据集
driver = gdal.GetDriverByName("GTiff")
output_raster = driver.Create(out_path, int((xmax - xmin) / b) + 2,
int((ymax - ymin) / a) + 2, input_raster.RasterCount, gdal.GDT_Float32)
output_raster.SetProjection(input_raster.GetProjection())
output_raster.SetGeoTransform(
(xmin, x_pixel_size, 0, ymax, 0, y_pixel_size))
logging.info("开始裁剪")
# 执行裁剪操作
gdal.Warp(output_raster, input_raster, cutlineDSName=clip_polygon_path, cropToCutline=True)
logging.info("裁剪完成")
# 关闭数据集
input_raster = None
output_raster = None
clip_polygon_ds = None
return out_path
函数流程
- 打开栅格数据集:使用 GDAL 库打开输入的 TIFF 图像文件,并存储在 input_raster 变量中。
- 获取栅格数据的地理信息:通过 input_raster.GetGeoTransform() 方法获取栅格数据的地理变换信息,包括左上角的 x 坐标 (x_origin)、x 方向的像素大小 (x_pixel_size)、y 方向的像素大小 (y_pixel_size) 以及左上角的 y 坐标 (y_origin,但这里只获取了部分参数,因为 GetGeoTransform() 返回的元组包含六个元素)。
- 打开矢量多边形数据集:使用 OGR 库打开提供的 Shapefile,并获取其图层。这个图层包含了用于裁剪的多边形。
- 获取裁剪多边形的边界框:通过 clip_polygon_layer.GetExtent() 获取裁剪多边形的边界框,即其在地理空间中的最小 x 坐标、最大 x 坐标、最小 y 坐标和最大 y 坐标。
- 计算输出栅格的大小:基于裁剪多边形的边界框和输入栅格的像素大小,计算输出栅格的大小(即宽度和高度)。这里使用 int((xmax - xmin) / b) + 2 和 int((ymax - ymin) / a) + 2 来计算宽度和高度,并加上 2 以确保裁剪区域完全包含在内(虽然这种做法在大多数情况下可能不是必需的,因为 GDAL 的 Warp 函数会处理边界情况)。
- 创建输出栅格数据集:使用 GDAL 的 GTiff 驱动程序创建一个新的 TIFF 文件,并设置其投影、地理变换和波段数等信息。
- 执行裁剪操作:使用 GDAL 的 Warp 函数执行裁剪操作。cutlineDSName 参数指定了裁剪多边形的数据集,cropToCutline 设置为 True 以确保裁剪结果严格符合裁剪多边形的边界。
- 关闭数据集:裁剪完成后,关闭输入栅格、输出栅格和裁剪多边形数据集,以释放资源。
- 返回输出路径:虽然函数在内部已经完成了裁剪操作,但它仍然返回了输出路径,这在某些情况下可能是有用的,尽管这个返回值在函数外部可能不是必需的。
补充 :分割时添加缓冲区
def split_tiff(input_tiff, output_folder, n, buffer_size):
# 打开大的TIFF文件
dataset = gdal.Open(input_tiff)
if dataset is None:
print("无法打开TIFF文件")
return
# 获取TIFF文件的基本信息
width = dataset.RasterXSize
height = dataset.RasterYSize
band_count = dataset.RasterCount
geotransform = dataset.GetGeoTransform()
projection = dataset.GetProjection()
# 计算每个小块的大小
block_width = width // n
block_height = height // n
os.makedirs(output_folder, exist_ok=True)
# 分割TIFF文件为n×n个小块
for i in range(n):
for j in range(n):
# 计算当前小块的范围,考虑缓冲区
x_offset = j * block_width
y_offset = i * block_height
x_start = max(x_offset - buffer_size, 0)
y_start = max(y_offset - buffer_size, 0)
x_end = min(x_offset + block_width + buffer_size, width)
y_end = min(y_offset + block_height + buffer_size, height)
actual_block_width = x_end - x_start
actual_block_height = y_end - y_start
# 创建小块的文件名
output_filename = os.path.join(output_folder, f"block_{i}_{j}.tif")
print(output_filename)
# 写入小块数据到文件
driver = gdal.GetDriverByName("GTiff")
output_dataset = driver.Create(output_filename, actual_block_width, actual_block_height, band_count,
gdal.GDT_Float32)
# 设置小块的地理转换和投影信息
output_dataset.SetGeoTransform((geotransform[0] + x_start * geotransform[1], geotransform[1], 0,
geotransform[3] + y_start * geotransform[5], 0, geotransform[5]))
output_dataset.SetProjection(projection)
# 写入小块的数据
for band in range(band_count):
band_data = dataset.GetRasterBand(band + 1).ReadAsArray(x_start, y_start, actual_block_width,
actual_block_height)
output_dataset.GetRasterBand(band + 1).WriteArray(band_data)
# 关闭输出文件
output_dataset = None
# 关闭输入文件
dataset = None
# 示例用法
input_tiff = r"F:\20240522-ResultMerge\ResultMerge.tiff"
output_folder = r"data1/高程1"
n = 10 # 分割为4×4个小块
buffer_size = 10 # 缓冲区大小为10像素
split_tiff(input_tiff, output_folder, n, buffer_size)
结论
通过使用Python和GDAL/rasterio库,我们可以高效地处理栅格数据,包括分割、裁剪和拼接等操作。这些操作对于GIS和遥感分析至关重要,能够帮助我们更好地理解和利用地理空间数据。希望本文的示例代码能为你的数据处理工作提供一些帮助。