Files

276 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#! /usr/bin/env python
#################################################################
### This program is part of PyINT v2.1 ###
### Copy Right (c): 2017-2019, Yunmeng Cao ###
### Author: Yunmeng Cao ###
### Modified: 2026, Z. Zou - HyP3 UTM GeoTIFF output ###
### Contact : ymcmrs@gmail.com ###
#################################################################
import numpy as np
import os
import sys
import argparse
import time
from datetime import datetime
from pyint import _utils as ut
INTRODUCTION = '''
-------------------------------------------------------------------
将 geocode_gamma.py 已地理编码的 GAMMA 二进制产品转换为
ASF HyP3 兼容的 UTM GeoTIFF 格式, 用于 MintPy 时间序列分析。
本脚本 **不做任何地理编码或计算**, 仅执行:
1. data2geotiff : GAMMA 二进制 (EQA) → GeoTIFF (EQA)
2. gdalwarp : GeoTIFF (EQA) → GeoTIFF (UTM)
3. rasterio : 生成水体掩膜
所有地理编码产品由 geocode_gamma.py 提供:
amp, corr, dem, unw_phase, wrapped_phase,
los_disp, vert_disp, lv_theta, lv_phi
'''
EXAMPLE = '''
Usage:
hyp3format_gamma.py projectName ifgPair
hyp3format_gamma.py shanghaiT171F128S1A 20241105-20241117
hyp3format_gamma.py shanghaiT171F128S1A 20241105-20241117 --output-dir /path/to/output
-------------------------------------------------------------------
'''
def cmdLineParse():
parser = argparse.ArgumentParser(
description='Convert geocoded GAMMA binaries to HyP3 UTM GeoTIFF.',
formatter_class=argparse.RawTextHelpFormatter,
epilog=INTRODUCTION + '\n' + EXAMPLE)
parser.add_argument('projectName', help='项目名称')
parser.add_argument('ifgPair', help='干涉图对 (如 20241105-20241117)')
parser.add_argument('--output-dir', dest='output_dir', default=None,
help='输出目录. 默认: projectDir/Hyp3Products/')
inps = parser.parse_args()
return inps
def get_utm_epsg(lon, lat):
"""根据中心经纬度自动计算 UTM 投影的 EPSG 代码"""
zone = int((lon + 180) / 6) + 1
return 32600 + zone if lat >= 0 else 32700 + zone
def run_cmd(cmd, desc=''):
"""执行 shell 命令并检查返回值"""
if desc:
print(f" [{desc}] {cmd}")
else:
print(f" {cmd}")
ret = os.system(cmd)
if ret != 0:
print(f" WARNING: 返回非零退出码 {ret}")
return ret
def main(argv):
start_time = time.time()
inps = cmdLineParse()
projectName = inps.projectName
ifgPair = inps.ifgPair
Mdate = ifgPair.split('-')[0]
Sdate = ifgPair.split('-')[1]
# 验证日期并计算时间基线
d1 = datetime.strptime(Mdate, '%Y%m%d')
d2 = datetime.strptime(Sdate, '%Y%m%d')
interval = abs((d2 - d1).days)
# 读取模板参数
scratchDir = os.getenv('SCRATCHDIR')
templateDir = os.getenv('TEMPLATEDIR')
templateFile = templateDir + '/' + projectName + '.template'
templateDict = ut.update_template(templateFile)
rlks = templateDict['range_looks']
azlks = templateDict['azimuth_looks']
masterDate = templateDict['masterDate']
# 项目目录
projectDir = scratchDir + '/' + projectName
ifgDir = projectDir + '/ifgrams'
demDir = projectDir + '/DEM'
workDir = ifgDir + '/' + ifgPair
if not os.path.isdir(workDir):
print(f"ERROR: 干涉图目录不存在: {workDir}")
sys.exit(1)
# 输出目录
outputDir = inps.output_dir or (projectDir + '/Hyp3Products')
productDir = os.path.join(outputDir, ifgPair)
os.makedirs(productDir, exist_ok=True)
# ============================================================
# DEM 参数 (直接引用, 不复制不删除)
# ============================================================
DEMpar = demDir + '/' + masterDate + '_' + rlks + 'rlks.utm.dem.par'
nWidthDEM = ut.read_gamma_par(DEMpar, 'read', 'width')
nLineDEM = ut.read_gamma_par(DEMpar, 'read', 'nlines')
# 自动检测 UTM 投影
corner_lat = float(ut.read_gamma_par(DEMpar, 'read', 'corner_lat').split()[0])
corner_lon = float(ut.read_gamma_par(DEMpar, 'read', 'corner_lon').split()[0])
post_lat = float(ut.read_gamma_par(DEMpar, 'read', 'post_lat').split()[0])
post_lon = float(ut.read_gamma_par(DEMpar, 'read', 'post_lon').split()[0])
center_lat = corner_lat + float(nLineDEM) / 2 * post_lat
center_lon = corner_lon + float(nWidthDEM) / 2 * post_lon
utm_epsg = get_utm_epsg(center_lon, center_lat)
# HyP3 文件名前缀 (含 YYYYMMDDTHHMMSS, 兼容 MintPy 日期解析)
hyp3_prefix = f"{projectName}_{Mdate}T000000_{Sdate}T000000"
print("\n" + "=" * 70)
print(f"HyP3 格式转换: {projectName} / {ifgPair}")
print(f"时间基线: {interval} 天, DEM: {nWidthDEM}×{nLineDEM}")
print(f"中心: {center_lat:.4f}°N, {center_lon:.4f}°E → EPSG:{utm_epsg}")
print(f"输出: {productDir}")
print("=" * 70)
# ============================================================
# 已地理编码 (EQA) GAMMA 二进制文件 (由 geocode_gamma.py 生成)
# ============================================================
geo_unw = workDir + '/geo_' + ifgPair + '_' + rlks + 'rlks.diff_filt.unw'
geo_wrapped_pha = workDir + '/geo_' + ifgPair + '_' + rlks + 'rlks.diff_filt.pha'
geo_los_disp = workDir + '/geo_' + ifgPair + '_' + rlks + 'rlks.los_disp'
geo_vert_disp = workDir + '/geo_' + ifgPair + '_' + rlks + 'rlks.vert_disp'
geo_amp = workDir + '/geo_' + masterDate + '_' + rlks + 'rlks.amp'
geo_hgt = workDir + '/geo_' + masterDate + '_' + rlks + 'rlks.hgt'
lv_theta = workDir + '/lv_theta'
lv_phi = workDir + '/lv_phi'
# 相干性可能用 Pair 或 masterDate 命名
geo_cor = workDir + '/geo_' + ifgPair + '_' + rlks + 'rlks.diff_filt.cor'
if not os.path.exists(geo_cor):
geo_cor = workDir + '/geo_' + masterDate + '_' + rlks + 'rlks.diff_filt.cor'
# ============================================================
# data2geotiff (EQA binary → EQA GeoTIFF) → gdalwarp (→ UTM)
# ============================================================
print("\n--- GAMMA 二进制 → EQA GeoTIFF → UTM GeoTIFF ---")
product_map = [
('amp', geo_amp, 'bilinear'),
('corr', geo_cor, 'bilinear'),
('dem', geo_hgt, 'bilinear'),
('unw_phase', geo_unw, 'bilinear'),
('wrapped_phase', geo_wrapped_pha, 'bilinear'),
('los_disp', geo_los_disp, 'bilinear'),
('vert_disp', geo_vert_disp, 'bilinear'),
('lv_theta', lv_theta, 'bilinear'),
('lv_phi', lv_phi, 'bilinear'),
]
generated = {}
for name, geo_bin, interp in product_map:
utm_tif = os.path.join(productDir, f'{hyp3_prefix}_{name}.tif')
# 跳过已存在且非空的输出
if os.path.exists(utm_tif) and os.path.getsize(utm_tif) > 0:
print(f" {name}: 已存在,跳过")
generated[name] = utm_tif
continue
if not os.path.exists(geo_bin):
print(f" {name}: 源文件不存在 ({os.path.basename(geo_bin)}),跳过")
continue
eqa_tif = workDir + '/' + name + '_hyp3_eqa.tif'
# GAMMA 二进制 → EQA GeoTIFF
run_cmd(f'data2geotiff {DEMpar} {geo_bin} 2 {eqa_tif}', f'{name} → EQA')
if not os.path.exists(eqa_tif):
print(f" WARNING: {name} EQA GeoTIFF 生成失败")
continue
# EQA → UTM (gdalwarp)
run_cmd(f'gdalwarp -t_srs EPSG:{utm_epsg} -r {interp} '
f'-co COMPRESS=LZW -overwrite {eqa_tif} {utm_tif}',
f'{name} → UTM')
# 清理 EQA 中间文件
if os.path.exists(eqa_tif):
os.remove(eqa_tif)
if os.path.exists(utm_tif) and os.path.getsize(utm_tif) > 0:
generated[name] = utm_tif
# ============================================================
# 生成水体掩膜 (相干性阈值)
# ============================================================
print("\n--- 水体掩膜 ---")
water_mask_tif = os.path.join(productDir, f'{hyp3_prefix}_water_mask.tif')
corr_tif = generated.get('corr')
if corr_tif and os.path.exists(corr_tif) and not os.path.exists(water_mask_tif):
try:
import rasterio
with rasterio.open(corr_tif) as src:
corr_data = src.read(1)
mask = np.ones_like(corr_data, dtype=np.uint8)
mask[corr_data < 0.05] = 0
mask[np.isnan(corr_data)] = 0
profile = src.profile.copy()
profile.update(dtype=rasterio.uint8, count=1, compress='lzw', nodata=None)
with rasterio.open(water_mask_tif, 'w', **profile) as dst:
dst.write(mask, 1)
generated['water_mask'] = water_mask_tif
print(f" 水体掩膜已生成")
except Exception as e:
print(f" WARNING: 水体掩膜生成失败: {e}")
elif os.path.exists(water_mask_tif):
generated['water_mask'] = water_mask_tif
print(f" 水体掩膜已存在,跳过")
# ============================================================
# 生成入射角图 (inc_map / inc_map_ell, 复制自 lv_theta)
# ============================================================
print("\n--- 入射角图 ---")
inc_map_tif = os.path.join(productDir, f'{hyp3_prefix}_inc_map.tif')
inc_map_ell_tif = os.path.join(productDir, f'{hyp3_prefix}_inc_map_ell.tif')
lv_theta_tif = generated.get('lv_theta')
if lv_theta_tif and os.path.exists(lv_theta_tif):
if not os.path.exists(inc_map_tif):
os.system(f'cp {lv_theta_tif} {inc_map_tif}')
generated['inc_map'] = inc_map_tif
print(f" inc_map 已生成 (= lv_theta)")
if not os.path.exists(inc_map_ell_tif):
os.system(f'cp {lv_theta_tif} {inc_map_ell_tif}')
generated['inc_map_ell'] = inc_map_ell_tif
print(f" inc_map_ell 已生成 (近似 lv_theta)")
# ============================================================
# 输出摘要
# ============================================================
print("\n" + "=" * 70)
print(f"HyP3 格式转换完成: {ifgPair}")
print(f"共 {len(generated)} 个产品:")
for name, path in sorted(generated.items()):
if os.path.exists(path):
size_mb = os.path.getsize(path) / 1024 / 1024
print(f" {name:20s} ({size_mb:.1f} MB)")
print("=" * 70)
ut.print_process_time(start_time, time.time())
sys.exit(0)
if __name__ == '__main__':
main(sys.argv[:])