Apply current workspace changes
This commit is contained in:
+565
@@ -0,0 +1,565 @@
|
||||
#! /usr/bin/env python
|
||||
#################################################################
|
||||
### 从本地 FABDEM 瓦片库生成 GAMMA 格式 DEM ###
|
||||
### 输入: 经纬度范围 + 本地 FABDEM ZIP 目录 ###
|
||||
### 输出: .dem + .dem.par (与 makedem.py 输出完全一致) ###
|
||||
### Author: Cascade AI + zouyuandong ###
|
||||
### Date : 2026-03-15 ###
|
||||
#################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import math
|
||||
import glob
|
||||
import zipfile
|
||||
import tempfile
|
||||
import shutil
|
||||
import argparse
|
||||
import subprocess
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# ========================= GAMMA 参数文件写入 =========================
|
||||
|
||||
def write_dempar_file(filepath, corner_lon, corner_lat, post_lon, post_lat,
|
||||
width, nlines, data_format='REAL*4'):
|
||||
"""写入 GAMMA DEM 参数文件 (.dem.par),与 makedem.py 格式完全一致"""
|
||||
with open(filepath, 'w') as f:
|
||||
f.write("Gamma DIFF&GEO DEM/MAP parameter file\n")
|
||||
f.write("title:\tIMPORTED DEM FROM FABDEM V1-2\n")
|
||||
f.write("DEM_projection: EQA\n")
|
||||
f.write("data_format: %s\n" % data_format)
|
||||
f.write("DEM_hgt_offset: 0.00000\n")
|
||||
f.write("DEM_scale: 1.00000\n")
|
||||
f.write("width: %s\n" % str(int(width)))
|
||||
f.write("nlines: %s\n" % str(int(nlines)))
|
||||
f.write("corner_lat: %s decimal degrees\n" % str(float(corner_lat)))
|
||||
f.write("corner_lon: %s decimal degrees\n" % str(float(corner_lon)))
|
||||
f.write("post_lat: %s decimal degrees\n" % str(float(post_lat)))
|
||||
f.write("post_lon: %s decimal degrees\n" % str(float(post_lon)))
|
||||
f.write("\n")
|
||||
f.write("ellipsoid_name: WGS 84\n")
|
||||
f.write("ellipsoid_ra: 6378137.000 m\n")
|
||||
f.write("ellipsoid_reciprocal_flattening: 298.2572236\n")
|
||||
f.write("\n")
|
||||
f.write("datum_name: WGS 1984\n")
|
||||
f.write("datum_shift_dx: 0.000 m\n")
|
||||
f.write("datum_shift_dy: 0.000 m\n")
|
||||
f.write("datum_shift_dz: 0.000 m\n")
|
||||
f.write("datum_scale_m: 0.00000e+00\n")
|
||||
f.write("datum_rotation_alpha: 0.00000e+00 arc-sec\n")
|
||||
f.write("datum_rotation_beta: 0.00000e+00 arc-sec\n")
|
||||
f.write("datum_rotation_gamma: 0.00000e+00 arc-sec\n")
|
||||
f.write("datum_country_list Global Definition, WGS84, World\n")
|
||||
f.write("\n")
|
||||
|
||||
|
||||
# ========================= FABDEM 瓦片索引逻辑 =========================
|
||||
|
||||
def parse_fabdem_zip_name(zip_name):
|
||||
"""解析 FABDEM ZIP 文件名,提取覆盖的经纬度范围
|
||||
|
||||
示例: N30E120-N40E130_FABDEM_V1-2.zip → (lat_min=30, lon_min=120, lat_max=40, lon_max=130)
|
||||
S10W080-S00W070_FABDEM_V1-2.zip → (lat_min=-10, lon_min=-80, lat_max=0, lon_max=-70)
|
||||
"""
|
||||
basename = Path(zip_name).stem # N30E120-N40E130_FABDEM_V1-2
|
||||
match = re.match(
|
||||
r'([NS])(\d+)([EW])(\d+)-([NS])(\d+)([EW])(\d+)_FABDEM',
|
||||
basename
|
||||
)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
lat1 = int(match.group(2)) * (1 if match.group(1) == 'N' else -1)
|
||||
lon1 = int(match.group(4)) * (1 if match.group(3) == 'E' else -1)
|
||||
lat2 = int(match.group(6)) * (1 if match.group(5) == 'N' else -1)
|
||||
lon2 = int(match.group(8)) * (1 if match.group(7) == 'E' else -1)
|
||||
|
||||
return {
|
||||
'lat_min': min(lat1, lat2),
|
||||
'lat_max': max(lat1, lat2),
|
||||
'lon_min': min(lon1, lon2),
|
||||
'lon_max': max(lon1, lon2),
|
||||
}
|
||||
|
||||
|
||||
def tile_name_for_coord(lat, lon):
|
||||
"""根据经纬度生成 FABDEM 1°×1° 瓦片文件名
|
||||
|
||||
参数 lat, lon 为瓦片左下角整数坐标
|
||||
示例: (31, 121) → 'N31E121_FABDEM_V1-2.tif'
|
||||
(-1, -70) → 'S01W070_FABDEM_V1-2.tif'
|
||||
"""
|
||||
lat_prefix = 'N' if lat >= 0 else 'S'
|
||||
lon_prefix = 'E' if lon >= 0 else 'W'
|
||||
return f"{lat_prefix}{abs(lat):02d}{lon_prefix}{abs(lon):03d}_FABDEM_V1-2.tif"
|
||||
|
||||
|
||||
def find_needed_tiles(west, south, east, north):
|
||||
"""计算覆盖目标区域所需的全部 1°×1° 瓦片坐标列表
|
||||
|
||||
返回: [(lat, lon), ...] 瓦片左下角坐标
|
||||
"""
|
||||
# 向下取整到最近的整数度
|
||||
lat_start = math.floor(south)
|
||||
lat_end = math.ceil(north) # 不含
|
||||
lon_start = math.floor(west)
|
||||
lon_end = math.ceil(east)
|
||||
|
||||
tiles = []
|
||||
for lat in range(lat_start, lat_end):
|
||||
for lon in range(lon_start, lon_end):
|
||||
tiles.append((lat, lon))
|
||||
return tiles
|
||||
|
||||
|
||||
def find_zip_for_tile(lat, lon, fabdem_dir):
|
||||
"""查找包含指定 1°×1° 瓦片的 ZIP 文件
|
||||
|
||||
FABDEM ZIP 按 10°×10° 分块,文件名编码了覆盖范围
|
||||
"""
|
||||
fabdem_path = Path(fabdem_dir)
|
||||
zip_files = sorted(fabdem_path.glob('*_FABDEM_V1-2.zip'))
|
||||
|
||||
for zf in zip_files:
|
||||
bounds = parse_fabdem_zip_name(zf.name)
|
||||
if bounds is None:
|
||||
continue
|
||||
if (bounds['lat_min'] <= lat < bounds['lat_max'] and
|
||||
bounds['lon_min'] <= lon < bounds['lon_max']):
|
||||
return zf
|
||||
return None
|
||||
|
||||
|
||||
def extract_tiles_from_zips(tile_coords, fabdem_dir, extract_dir):
|
||||
"""从 FABDEM ZIP 文件中提取所需的 1°×1° GeoTIFF 瓦片
|
||||
|
||||
参数:
|
||||
tile_coords: [(lat, lon), ...] 需要提取的瓦片坐标
|
||||
fabdem_dir: FABDEM ZIP 文件所在目录
|
||||
extract_dir: 解压目标目录
|
||||
|
||||
返回:
|
||||
提取成功的 GeoTIFF 文件路径列表
|
||||
"""
|
||||
extract_path = Path(extract_dir)
|
||||
extract_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 按 ZIP 文件分组,避免重复打开同一个 ZIP
|
||||
zip_to_tiles = {}
|
||||
missing_tiles = []
|
||||
|
||||
for lat, lon in tile_coords:
|
||||
zip_file = find_zip_for_tile(lat, lon, fabdem_dir)
|
||||
if zip_file is None:
|
||||
tile_name = tile_name_for_coord(lat, lon)
|
||||
print(f" ⚠ 未找到包含 {tile_name} 的 ZIP 文件 (lat={lat}, lon={lon})")
|
||||
missing_tiles.append((lat, lon))
|
||||
continue
|
||||
|
||||
zip_key = str(zip_file)
|
||||
if zip_key not in zip_to_tiles:
|
||||
zip_to_tiles[zip_key] = []
|
||||
zip_to_tiles[zip_key].append((lat, lon))
|
||||
|
||||
# 逐个 ZIP 文件提取
|
||||
extracted_files = []
|
||||
for zip_path, coords in zip_to_tiles.items():
|
||||
zip_name = Path(zip_path).name
|
||||
print(f" 📦 从 {zip_name} 提取 {len(coords)} 个瓦片...")
|
||||
|
||||
try:
|
||||
with zipfile.ZipFile(zip_path, 'r') as zf:
|
||||
zip_contents = zf.namelist()
|
||||
|
||||
for lat, lon in coords:
|
||||
tile_name = tile_name_for_coord(lat, lon)
|
||||
|
||||
if tile_name in zip_contents:
|
||||
zf.extract(tile_name, extract_dir)
|
||||
out_file = extract_path / tile_name
|
||||
if out_file.exists() and out_file.stat().st_size > 0:
|
||||
extracted_files.append(str(out_file))
|
||||
print(f" ✓ {tile_name}")
|
||||
else:
|
||||
print(f" ✗ {tile_name} 提取后为空")
|
||||
else:
|
||||
# 海洋区域可能没有对应瓦片(正常)
|
||||
print(f" - {tile_name} 不在 ZIP 中(可能是海洋区域)")
|
||||
except Exception as e:
|
||||
print(f" ✗ 打开 {zip_name} 失败: {e}")
|
||||
|
||||
if missing_tiles:
|
||||
print(f"\n ⚠ 共 {len(missing_tiles)} 个瓦片未找到对应的 ZIP 文件")
|
||||
|
||||
return extracted_files
|
||||
|
||||
|
||||
# ========================= 瓦片拼接 + GAMMA 格式转换(一步完成) =========================
|
||||
|
||||
def _parse_gdalinfo(gdalinfo_text):
|
||||
"""从 gdalinfo 输出解析元数据"""
|
||||
width = nlines = None
|
||||
corner_lon = corner_lat = post_lon = post_lat = None
|
||||
for line in gdalinfo_text.splitlines():
|
||||
if 'Size is' in line:
|
||||
parts = line.split('Size is')[1].strip().split(',')
|
||||
width = int(parts[0].strip())
|
||||
nlines = int(parts[1].strip())
|
||||
elif 'Origin =' in line:
|
||||
parts = line.split('(')[1].split(')')[0].split(',')
|
||||
corner_lon = float(parts[0].strip())
|
||||
corner_lat = float(parts[1].strip())
|
||||
elif 'Pixel Size =' in line:
|
||||
parts = line.split('(')[1].split(')')[0].split(',')
|
||||
post_lon = float(parts[0].strip())
|
||||
post_lat = float(parts[1].strip())
|
||||
return width, nlines, corner_lon, corner_lat, post_lon, post_lat
|
||||
|
||||
|
||||
def tiles_to_gamma_dem(tif_files, output_name, west, south, east, north, byteorder='big'):
|
||||
"""从多个 GeoTIFF 瓦片生成 GAMMA 格式 DEM (.dem + .dem.par)
|
||||
|
||||
流程: srtm2dem 逐个转换 → mosaic 合并(全部使用 GAMMA 原生工具)
|
||||
|
||||
参数:
|
||||
tif_files: GeoTIFF 瓦片文件路径列表
|
||||
output_name: 输出文件名前缀(不含扩展名)
|
||||
west, south, east, north: 裁剪范围(度)
|
||||
byteorder: 字节序(未使用,srtm2dem 自动处理大端)
|
||||
返回:
|
||||
(dem_file, dem_par_file) 或 (None, None)
|
||||
"""
|
||||
if not tif_files:
|
||||
print(" ✗ 没有可用的瓦片文件")
|
||||
return None, None
|
||||
|
||||
Path(output_name).parent.mkdir(parents=True, exist_ok=True)
|
||||
dem_file = output_name + '.dem'
|
||||
dem_par_file = output_name + '.dem.par'
|
||||
|
||||
# 临时目录放 /tmp/(本地盘),srtm2dem 单瓦片很快
|
||||
import tempfile as _tf
|
||||
_tmp_dir = _tf.mkdtemp(prefix='gamma_dem_conv_')
|
||||
|
||||
# ---- 步骤1: srtm2dem 逐个将 GeoTIFF 转为 GAMMA 格式 ----
|
||||
n = len(tif_files)
|
||||
print(f" [1/2] srtm2dem 转换 {n} 个瓦片...")
|
||||
gamma_tiles = [] # [(dem_path, dem_par_path), ...]
|
||||
|
||||
for i, tif in enumerate(tif_files):
|
||||
tile_name = Path(tif).stem
|
||||
tile_dem = os.path.join(_tmp_dir, f'{tile_name}.dem')
|
||||
tile_par = os.path.join(_tmp_dir, f'{tile_name}.dem.par')
|
||||
|
||||
# gflg=3: 不做大地水准面校正, NODATA 替换为 0.0
|
||||
cmd = f'srtm2dem {tif} {tile_dem} {tile_par} 3'
|
||||
ret = os.system(cmd + ' > /dev/null 2>&1')
|
||||
|
||||
if ret == 0 and os.path.exists(tile_dem) and os.path.getsize(tile_dem) > 0:
|
||||
gamma_tiles.append((tile_dem, tile_par))
|
||||
print(f" ✓ [{i+1}/{n}] {tile_name}")
|
||||
else:
|
||||
print(f" ✗ [{i+1}/{n}] {tile_name} srtm2dem 失败")
|
||||
|
||||
if not gamma_tiles:
|
||||
print(" ✗ 所有瓦片转换失败")
|
||||
shutil.rmtree(_tmp_dir, ignore_errors=True)
|
||||
return None, None
|
||||
|
||||
print(f" 成功转换 {len(gamma_tiles)}/{n} 个瓦片")
|
||||
|
||||
# ---- 步骤2: mosaic 合并所有 GAMMA DEM 瓦片 ----
|
||||
if len(gamma_tiles) == 1:
|
||||
# 只有一个瓦片,直接复制
|
||||
shutil.copy2(gamma_tiles[0][0], dem_file)
|
||||
shutil.copy2(gamma_tiles[0][1], dem_par_file)
|
||||
print(f" [2/2] 单瓦片,直接输出")
|
||||
else:
|
||||
print(f" [2/2] mosaic 合并 {len(gamma_tiles)} 个 GAMMA DEM...")
|
||||
# 构建 mosaic 命令: mosaic nfiles dem1 par1 dem2 par2 ... dem_out par_out mode format
|
||||
cmd_parts = ['mosaic', str(len(gamma_tiles))]
|
||||
for tile_dem, tile_par in gamma_tiles:
|
||||
cmd_parts.append(tile_dem)
|
||||
cmd_parts.append(tile_par)
|
||||
cmd_parts.extend([dem_file, dem_par_file, '1', '0'])
|
||||
# mode=1: 重叠区取平均, format=0: FLOAT
|
||||
|
||||
cmd_str = ' '.join(cmd_parts)
|
||||
ret = os.system(cmd_str)
|
||||
|
||||
if ret != 0 or not os.path.exists(dem_file) or os.path.getsize(dem_file) == 0:
|
||||
print(f" ✗ mosaic 失败 (exit={ret})")
|
||||
shutil.rmtree(_tmp_dir, ignore_errors=True)
|
||||
return None, None
|
||||
|
||||
# 清理临时目录
|
||||
shutil.rmtree(_tmp_dir, ignore_errors=True)
|
||||
|
||||
size_mb = os.path.getsize(dem_file) / (1024 * 1024)
|
||||
print(f" ✓ {dem_file} ({size_mb:.1f} MB)")
|
||||
print(f" ✓ {dem_par_file}")
|
||||
|
||||
return dem_file, dem_par_file
|
||||
|
||||
|
||||
# ========================= 主流程 =========================
|
||||
|
||||
INTRODUCTION = '''
|
||||
================================================================================
|
||||
make_local_dem.py — 从本地 FABDEM 瓦片库生成 GAMMA 格式 DEM
|
||||
|
||||
功能:
|
||||
1. 根据经纬度范围自动查找所需的 FABDEM 1°×1° 瓦片
|
||||
2. 从 10°×10° ZIP 包中提取瓦片
|
||||
3. 使用 GDAL 拼接并裁剪到目标范围
|
||||
4. 转换为 GAMMA 格式 (.dem + .dem.par)
|
||||
|
||||
输出与 makedem.py 完全一致,可直接用于 generate_rdc_dem.py
|
||||
================================================================================
|
||||
'''
|
||||
|
||||
EXAMPLE = """
|
||||
用法:
|
||||
# 方式1: 指定经纬度范围
|
||||
make_local_dem.py -r 120/123/30/32 -f /mnt/ZYD/全球FABDEM -o output_dem
|
||||
|
||||
# 方式2: 从 SLC 参数文件自动确定范围
|
||||
make_local_dem.py -s master.slc.par -f /mnt/ZYD/全球FABDEM -o output_dem
|
||||
|
||||
# 方式3: 从 PyINT 模板文件读取(集成到工作流)
|
||||
make_local_dem.py --template shanghaiT171F128S1A -f /mnt/ZYD/全球FABDEM
|
||||
"""
|
||||
|
||||
|
||||
def cmdLineParse():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='从本地 FABDEM 瓦片库生成 GAMMA 格式 DEM',
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
epilog=INTRODUCTION + '\n' + EXAMPLE
|
||||
)
|
||||
|
||||
parser.add_argument('-r', dest='region',
|
||||
help='研究区域范围: west/east/south/north (如: 120/123/30/32)')
|
||||
parser.add_argument('-s', dest='slc_par',
|
||||
help='SLC 参数文件路径(自动从中提取研究区域范围)')
|
||||
parser.add_argument('-f', '--fabdem-dir', dest='fabdem_dir', required=True,
|
||||
help='本地 FABDEM ZIP 文件目录 (如: /mnt/ZYD/全球FABDEM)')
|
||||
parser.add_argument('-o', '--output', dest='output_name', default=None,
|
||||
help='输出文件名前缀(不含扩展名)[默认: 工作目录/out]')
|
||||
parser.add_argument('--dir', dest='work_dir', default=None,
|
||||
help='工作目录 [默认: 当前目录]')
|
||||
parser.add_argument('--template', dest='template_name', default=None,
|
||||
help='PyINT 项目名(从模板文件读取 DEM 路径和 SLC 位置)')
|
||||
parser.add_argument('--byteorder', dest='byteorder', choices=['big', 'little'],
|
||||
default='big',
|
||||
help='输出 DEM 字节序 [默认: big (GAMMA 标准)]')
|
||||
parser.add_argument('--margin', dest='margin', type=float, default=1.0,
|
||||
help='在 SLC 覆盖范围外扩展的余量(度)[默认: 1.0]')
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def get_region_from_slc_par(slc_par_file):
|
||||
"""从 GAMMA SLC 参数文件提取研究区域范围"""
|
||||
corners_txt = 'corners_tmp.txt'
|
||||
call_str = f"SLC_corners {slc_par_file} > {corners_txt}"
|
||||
os.system(call_str)
|
||||
|
||||
if not os.path.isfile(corners_txt):
|
||||
print(f"✗ SLC_corners 执行失败")
|
||||
return None
|
||||
|
||||
with open(corners_txt, 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
os.remove(corners_txt)
|
||||
|
||||
# 解析 SLC_corners 输出(第 9-10 行包含 lat/lon 范围)
|
||||
try:
|
||||
lat_line = lines[8]
|
||||
lon_line = lines[9]
|
||||
min_lat = float(lat_line.split(':')[1].split('max.')[0].strip())
|
||||
max_lat = float(lat_line.split(':')[2].strip())
|
||||
min_lon = float(lon_line.split(':')[1].split('max.')[0].strip())
|
||||
max_lon = float(lon_line.split(':')[2].strip())
|
||||
return min_lon, min_lat, max_lon, max_lat
|
||||
except (IndexError, ValueError) as e:
|
||||
print(f"✗ 解析 SLC_corners 输出失败: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_region_from_template(project_name, margin=1.0):
|
||||
"""从 PyINT 模板文件获取区域范围(通过 master SLC 参数文件)"""
|
||||
try:
|
||||
from pyint import _utils as ut
|
||||
except ImportError:
|
||||
print("✗ 无法导入 pyint._utils,请确保 PyINT 在 PYTHONPATH 中")
|
||||
return None, None
|
||||
|
||||
scratchDir = os.getenv('SCRATCHDIR')
|
||||
templateDir = os.getenv('TEMPLATEDIR')
|
||||
if not scratchDir or not templateDir:
|
||||
print("✗ 环境变量 SCRATCHDIR 或 TEMPLATEDIR 未设置")
|
||||
return None, None
|
||||
|
||||
template_file = Path(templateDir) / f"{project_name}.template"
|
||||
if not template_file.exists():
|
||||
print(f"✗ 模板文件不存在: {template_file}")
|
||||
return None, None
|
||||
|
||||
template_dict = ut.update_template(str(template_file))
|
||||
master_date = template_dict['masterDate']
|
||||
slc_dir = Path(scratchDir) / project_name / 'SLC'
|
||||
master_slc_par = slc_dir / master_date / f"{master_date}.slc.par"
|
||||
|
||||
if not master_slc_par.exists():
|
||||
print(f"✗ Master SLC 参数文件不存在: {master_slc_par}")
|
||||
return None, None
|
||||
|
||||
region = get_region_from_slc_par(str(master_slc_par))
|
||||
if region is None:
|
||||
return None, None
|
||||
|
||||
west, south, east, north = region
|
||||
# 外扩 margin 度并取整
|
||||
west = math.floor(west - margin)
|
||||
south = math.floor(south - margin)
|
||||
east = math.ceil(east + margin)
|
||||
north = math.ceil(north + margin)
|
||||
|
||||
# 确定输出路径(使用模板中 DEM 路径或默认路径)
|
||||
dem_dir = os.getenv('DEMDIR', '')
|
||||
if 'DEM' in template_dict and template_dict['DEM'].strip():
|
||||
output_name = template_dict['DEM'].replace('.dem', '')
|
||||
else:
|
||||
dem_out_dir = Path(dem_dir) / project_name
|
||||
dem_out_dir.mkdir(parents=True, exist_ok=True)
|
||||
output_name = str(dem_out_dir / project_name)
|
||||
|
||||
return (west, south, east, north), output_name
|
||||
|
||||
|
||||
def main():
|
||||
args = cmdLineParse()
|
||||
|
||||
# 确定工作目录
|
||||
if args.work_dir:
|
||||
work_dir = Path(args.work_dir)
|
||||
else:
|
||||
work_dir = Path.cwd()
|
||||
work_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 确定 FABDEM 目录
|
||||
fabdem_dir = Path(args.fabdem_dir)
|
||||
if not fabdem_dir.is_dir():
|
||||
print(f"✗ FABDEM 目录不存在: {fabdem_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
zip_count = len(list(fabdem_dir.glob('*_FABDEM_V1-2.zip')))
|
||||
print(f"✓ FABDEM 目录: {fabdem_dir} ({zip_count} 个 ZIP 文件)")
|
||||
|
||||
# 确定研究区域
|
||||
output_name = args.output_name
|
||||
region = None
|
||||
|
||||
if args.template_name:
|
||||
# 从 PyINT 模板获取
|
||||
result = get_region_from_template(args.template_name, args.margin)
|
||||
if result[0] is not None:
|
||||
region = result[0]
|
||||
if output_name is None:
|
||||
output_name = result[1]
|
||||
else:
|
||||
print("✗ 无法从模板文件获取区域范围")
|
||||
sys.exit(1)
|
||||
|
||||
elif args.slc_par:
|
||||
# 从 SLC 参数文件获取
|
||||
slc_region = get_region_from_slc_par(args.slc_par)
|
||||
if slc_region is None:
|
||||
sys.exit(1)
|
||||
west, south, east, north = slc_region
|
||||
west = math.floor(west - args.margin)
|
||||
south = math.floor(south - args.margin)
|
||||
east = math.ceil(east + args.margin)
|
||||
north = math.ceil(north + args.margin)
|
||||
region = (west, south, east, north)
|
||||
|
||||
elif args.region:
|
||||
# 从命令行参数解析
|
||||
parts = args.region.split('/')
|
||||
if len(parts) != 4:
|
||||
print("✗ 区域格式错误,应为: west/east/south/north")
|
||||
sys.exit(1)
|
||||
west, east, south, north = [float(x) for x in parts]
|
||||
region = (west, south, east, north)
|
||||
|
||||
else:
|
||||
print("✗ 必须指定研究区域: -r, -s 或 --template")
|
||||
sys.exit(1)
|
||||
|
||||
if output_name is None:
|
||||
output_name = str(work_dir / 'out')
|
||||
|
||||
# 确保输出目录存在
|
||||
Path(output_name).parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
west, south, east, north = region
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print(f" 从本地 FABDEM 生成 GAMMA DEM")
|
||||
print(f"{'='*70}")
|
||||
print(f" 区域范围: {west}°E ~ {east}°E, {south}°N ~ {north}°N")
|
||||
print(f" FABDEM 目录: {fabdem_dir}")
|
||||
print(f" 输出文件: {output_name}.dem / {output_name}.dem.par")
|
||||
print(f" 字节序: {args.byteorder}")
|
||||
print(f"{'='*70}\n")
|
||||
|
||||
# 1. 计算所需瓦片
|
||||
tiles = find_needed_tiles(west, south, east, north)
|
||||
print(f"[1/4] 需要 {len(tiles)} 个 1°×1° 瓦片\n")
|
||||
|
||||
# 2. 从 ZIP 中提取瓦片
|
||||
print(f"[2/4] 从 FABDEM ZIP 文件中提取瓦片...")
|
||||
temp_dir = tempfile.mkdtemp(prefix='fabdem_tiles_')
|
||||
try:
|
||||
tif_files = extract_tiles_from_zips(tiles, str(fabdem_dir), temp_dir)
|
||||
|
||||
if not tif_files:
|
||||
print("\n✗ 没有提取到任何瓦片文件,请检查 FABDEM 目录和区域范围")
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\n 共提取 {len(tif_files)}/{len(tiles)} 个瓦片\n")
|
||||
|
||||
# 3-4. 一步完成: VRT → ENVI 二进制 → GAMMA .dem
|
||||
print(f"[3/4] 拼接裁剪 + 转换 GAMMA 格式...")
|
||||
dem_file, dem_par_file = tiles_to_gamma_dem(
|
||||
tif_files, output_name, west, south, east, north, args.byteorder
|
||||
)
|
||||
|
||||
if dem_file is None:
|
||||
print("\n✗ DEM 生成失败")
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
sys.exit(1)
|
||||
|
||||
finally:
|
||||
# 清理临时目录
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
# 完成
|
||||
print(f"\n{'='*70}")
|
||||
print(f" ✓ FABDEM → GAMMA DEM 转换完成!")
|
||||
print(f"{'='*70}")
|
||||
print(f" DEM 文件: {dem_file}")
|
||||
print(f" 参数文件: {dem_par_file}")
|
||||
print(f" 字节序: {args.byteorder} endian")
|
||||
print(f" 可直接用于: generate_rdc_dem.py")
|
||||
print(f"{'='*70}\n")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user