GreenTransPowerCalculate/deeplabv3sdRenewable/tools/山东省地貌识别tools/潜力评估阶段/合并矢量转栅格但保存分类(省级区域).py

190 lines
8.5 KiB
Python
Raw Normal View History

2025-04-27 09:58:17 +08:00
import geopandas as gpd
import pandas as pd
import rasterio
from rasterio.features import rasterize
import numpy as np
from shapely.geometry import mapping, box
import os
from tqdm import tqdm # 导入 tqdm 用于进度条
def merge_vectors_seven_categories(file_paths, output_vector_path):
"""
合并7个地貌类型矢量图层并保存为GeoPackage格式去掉cropland
参数:
file_paths: 包含7个矢量文件路径的列表 (按顺序: forest, water, shrub, wetland, jianzhu, grass, bareland)
output_vector_path: 输出合并后的矢量文件路径使用.gpkg格式
"""
try:
# 检查输入文件数量是否为7
if len(file_paths) != 7:
raise ValueError("请提供正好7个矢量文件路径")
for path in file_paths:
if not os.path.exists(path):
raise FileNotFoundError(f"文件不存在: {path}")
# 定义7个类别去掉cropland
categories = ['forest', 'water', 'shrub', 'wetland', 'jianzhu', 'grass', 'bareland']
category_values = {cat: i + 1 for i, cat in enumerate(categories)} # forest=1, water=2, ..., bareland=7
gdfs = []
for i, (path, category) in enumerate(zip(file_paths, categories), 1):
print(f"正在读取第{i}个矢量文件: {path}")
gdf = gpd.read_file(path, engine="pyogrio")
gdf['category'] = category
gdf['value'] = category_values[category]
if 'Shape_Area' in gdf.columns:
gdf['Shape_Area'] = gdf['Shape_Area'].clip(upper=1e9)
gdfs.append(gdf)
# 统一坐标系
base_crs = gdfs[0].crs
for i, gdf in enumerate(gdfs[1:], 2):
if gdf.crs != base_crs:
print(f"{i}个图层坐标系不同,正在转换为第一个图层的坐标系...")
gdfs[i - 1] = gdf.to_crs(base_crs)
print("正在合并图层...")
merged_gdf = gpd.GeoDataFrame(pd.concat(gdfs, ignore_index=True))
merged_gdf = merged_gdf.set_geometry('geometry')
if not output_vector_path.endswith('.gpkg'):
output_vector_path = output_vector_path.replace('.shp', '.gpkg')
print("输出格式已更改为GeoPackage以支持大文件")
print(f"正在保存合并矢量结果到: {output_vector_path}")
merged_gdf.to_file(output_vector_path, driver='GPKG', engine="pyogrio")
print("矢量合并完成!")
print(f"合并矢量保存为: {output_vector_path}")
return output_vector_path
except Exception as e:
print(f"发生错误: {str(e)}")
return None
def rasterize_vector_by_blocks(input_vector_path, output_raster_path, raster_resolution=30, block_size=5000):
"""
将合并后的矢量文件分块转为栅格显示7个类别保留0值作为背景添加进度条不清理数据
参数:
input_vector_path: 输入的合并矢量文件路径.gpkg格式
output_raster_path: 输出栅格文件路径
raster_resolution: 输出栅格的分辨率默认30米
block_size: 每个分块的宽度和高度像素单位默认5000
"""
try:
# 读取合并后的矢量文件
print(f"正在读取合并矢量文件: {input_vector_path}")
merged_gdf = gpd.read_file(input_vector_path, engine="pyogrio")
# 检查 value 字段是否有效
if 'value' not in merged_gdf.columns:
raise ValueError("矢量文件中缺少 'value' 字段,请确保文件包含正确的分类值")
if not merged_gdf['value'].between(1, 7).all():
print("警告: 'value' 字段包含无效值(应在 1-7 之间),可能导致数据丢失")
merged_gdf = merged_gdf[merged_gdf['value'].between(1, 7)]
# 按 value 字段排序,优先级高的类别(例如 value 较大)后处理,避免被覆盖
print("正在按 'value' 字段排序以确保优先级...")
merged_gdf = merged_gdf.sort_values(by='value', ascending=True)
# 计算总范围和栅格尺寸
bounds = merged_gdf.total_bounds # [minx, miny, maxx, maxy]
total_width = int((bounds[2] - bounds[0]) / raster_resolution)
total_height = int((bounds[3] - bounds[1]) / raster_resolution)
if total_width <= 0 or total_height <= 0:
raise ValueError(f"栅格尺寸无效: 宽度={total_width}, 高度={total_height}")
# 创建栅格变换
transform = rasterio.transform.from_bounds(bounds[0], bounds[1], bounds[2], bounds[3], total_width,
total_height)
# 计算总分块数以设置进度条
total_blocks = ((total_height + block_size - 1) // block_size) * ((total_width + block_size - 1) // block_size)
print(f"总分块数: {total_blocks}")
# 创建并写入栅格文件,逐块写入以减少内存占用
with rasterio.open(
output_raster_path,
'w',
driver='GTiff',
height=total_height,
width=total_width,
count=1,
dtype=rasterio.uint8,
crs=merged_gdf.crs,
transform=transform,
nodata=0
) as dst:
# 分块处理并添加进度条
with tqdm(total=total_blocks, desc="栅格化进度") as pbar:
for y in range(0, total_height, block_size):
for x in range(0, total_width, block_size):
block_height = min(block_size, total_height - y)
block_width = min(block_size, total_width - x)
# 计算当前分块的地理范围
block_minx = bounds[0] + x * raster_resolution
block_maxy = bounds[3] - y * raster_resolution
block_maxx = block_minx + block_width * raster_resolution
block_miny = block_maxy - block_height * raster_resolution
# 创建分块边界框
block_bounds = box(block_minx, block_miny, block_maxx, block_maxy)
block_gdf = merged_gdf[merged_gdf.geometry.intersects(block_bounds)].copy()
if block_gdf.empty:
pbar.update(1)
continue
# 分块栅格化
block_transform = rasterio.transform.from_bounds(
block_minx, block_miny, block_maxx, block_maxy, block_width, block_height
)
shapes = ((mapping(geom), value) for geom, value in zip(block_gdf.geometry, block_gdf['value']))
block_raster = rasterize(
shapes=shapes,
out_shape=(block_height, block_width),
transform=block_transform,
fill=0,
dtype=rasterio.uint8,
all_touched=True # 确保所有触及的像素都被计入,避免小对象丢失
)
# 直接写入当前分块到文件中
dst.write(block_raster, 1, window=((y, y + block_height), (x, x + block_width)))
print(f"已完成分块栅格化: x={x}, y={y}, 宽度={block_width}, 高度={block_height}")
pbar.update(1)
print("栅格化完成!")
print(f"栅格保存为: {output_raster_path}")
print(f"栅格中类别值: 0=背景, 1=forest, 2=water, 3=shrub, 4=wetland, 5=jianzhu, 6=grass, 7=bareland")
except Exception as e:
print(f"发生错误: {str(e)}")
# 使用示例
if __name__ == "__main__":
input_files = [
r"sddimaoshp/forest/sdforest.shp",
r"sddimaoshp/water/sdwater.shp",
r"sddimaoshp/shrub/sdshrub.shp",
r"sddimaoshp/wetland/sdwetland.shp",
r"sddimaoshp/jianzhu/jianzhu2.shp",
r"sddimaoshp/grass/sdgrass.shp",
r"sddimaoshp/bareland/sdbareland.shp"
]
output_vector_file = r"sddimaoshp/hebingtif/shandongdimiao_30m.gpkg"
output_raster_file = r"sddimaoshp/hebingtif/sddimao_30m.tif"
# Step 1: 合并矢量
vector_path = merge_vectors_seven_categories(input_files, output_vector_file)
# Step 2: 如果合并成功,进行分块栅格化
if vector_path:
rasterize_vector_by_blocks(vector_path, output_raster_file, raster_resolution=30, block_size=5000)