Skip to content

Commit f641b90

Browse files
authored
add dem_error.py --dem-err-file and save_qgis.py --zero-first option (#1007)
* dem_error: add `--dem-error-file` option to specify a customized file name for the estimated DEM error. * save_qgis: add `--zf / --zero-first` option, same as `tsview.py`, to set displacement at 1st acquisition to zero.
1 parent df96e0b commit f641b90

File tree

5 files changed

+65
-47
lines changed

5 files changed

+65
-47
lines changed

docs/QGIS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Displacement time-series can be exported as QGIS PS Time Series Viewer plugin co
33
1. Download and install [QGIS](https://qgis.org/en/site/) if you have not done so.
44
2. Download [ps-time-series](https://plugins.qgis.org/plugins/pstimeseries/) plugin as a *.zip file; and install it to QGIS through "Plugins -> Manage and install Plugins -> install from ZIP".
55
3. On the left side of QGIS window, browse to the *.shp file generated by `save_qgis.py` and add to the layer.
6-
4. Open "View -> Layer Styling", then "Fill color -> Edit" and add the command below to color code the map based on velocity value.
6+
4. Open "View -> Panels -> Layer Styling", then on the right choose "Fill color -> Edit" and add the command below to color code the map based on velocity value.
77

88
```
99
ramp_color('RdBu', scale_linear(VEL, -20, 20, 0, 1))
@@ -13,7 +13,7 @@ ramp_color('RdBu', scale_linear(VEL, -20, 20, 0, 1))
1313
<img width="1000" src="https://yunjunzhang.files.wordpress.com/2019/11/ps-time-series-viewer-1.png">
1414
</p>
1515

16-
5. Select the [PS Time Series Viewer logo](https://gitlab.com/faunalia/ps-speed/blob/master/icons/logo.png) to activate the tool, and click/play on the map to display the time-series!
16+
5. Click the [PS Time Series Viewer logo](https://gitlab.com/faunalia/ps-speed/blob/master/icons/logo.png) to activate the tool, and click/play on the map to display the time-series!
1717

1818
<p align="left">
1919
<img width="800" src="https://yunjunzhang.files.wordpress.com/2019/11/ps-time-series-viewer-2.png">

src/mintpy/cli/dem_error.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,16 @@ def create_parser(subparsers=None):
4141
parser = arg_utils.create_argument_parser(
4242
name, synopsis=synopsis, description=synopsis, epilog=epilog, subparsers=subparsers)
4343

44-
parser.add_argument('timeseries_file',
45-
help='Timeseries file to be corrrected')
44+
parser.add_argument('ts_file', help='Time-series HDF5 file to be corrrected.')
4645
parser.add_argument('-g', '--geometry', dest='geom_file',
4746
help='geometry file including datasets:\n'+
4847
'incidence angle\n'+
4948
'slant range distance\n' +
5049
'and/or 3D perpendicular baseline')
51-
parser.add_argument('-o', '--outfile',
52-
help='Output file name for corrected time-series')
50+
parser.add_argument('-o', '--outfile', dest='ts_cor_file',
51+
help='Output file name for corrected time-series (default: add suffix of "_demErr")')
52+
parser.add_argument('--dem-err-file','--dem-error-file', dest='dem_err_file', default='demErr.h5',
53+
help='Output file name for the estimated DEM error (default: %(default)s).')
5354

5455
defo_model = parser.add_argument_group('temporal deformation model')
5556
defo_model.add_argument('-t', '--template', dest='template_file',
@@ -69,7 +70,7 @@ def create_parser(subparsers=None):
6970
help='Use phase velocity instead of phase for inversion constrain.')
7071
parser.add_argument('--update', dest='update_mode', action='store_true',
7172
help='Enable update mode, and skip inversion if:\n'+
72-
'1) output timeseries file already exists, readable '+
73+
'1) output time-series file already exists, readable '+
7374
'and newer than input interferograms file\n' +
7475
'2) all configuration parameters are the same.')
7576
# computing
@@ -106,10 +107,18 @@ def cmd_line_parse(iargs=None):
106107
if inps.polyOrder < 1:
107108
raise argparse.ArgumentTypeError("Minimum polynomial order is 1")
108109

109-
# default: --output
110-
if not inps.outfile:
111-
fbase = os.path.splitext(inps.timeseries_file)[0]
112-
inps.outfile = f'{fbase}_demErr.h5'
110+
# check: --output / --dem-error-file option (must be HDF5 file)
111+
for fname in [inps.ts_cor_file, inps.dem_err_file]:
112+
if fname:
113+
fext = os.path.splitext(fname)[1]
114+
if fext not in ['.h5','.he5']:
115+
msg = f'--output / --dem-err-file option ({fext}) supports HDF5 file only!'
116+
raise ValueError(msg)
117+
118+
# default: --output option
119+
if not inps.ts_cor_file:
120+
fbase = os.path.splitext(inps.ts_file)[0]
121+
inps.ts_cor_file = f'{fbase}_demErr.h5'
113122

114123
return inps
115124

src/mintpy/cli/save_qgis.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ def create_parser(subparsers=None):
3737
parser.add_argument('-B', '--geo-bbox', dest='geo_bbox', type=float, nargs=4, default=None,
3838
metavar=('S','N','W','E'), help='bounding box in lat lon: South North West East')
3939

40+
# other options
41+
parser.add_argument('--zf', '--zero-first', dest='zero_first', action='store_true',
42+
help='Set displacement at the first acquisition to zero.')
43+
4044
return parser
4145

4246

src/mintpy/dem_error.py

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,28 +35,28 @@ def run_or_skip(inps):
3535
flag = 'skip'
3636

3737
# check output file
38-
if not os.path.isfile(inps.outfile):
38+
if not os.path.isfile(inps.ts_cor_file):
3939
flag = 'run'
40-
print(f'1) output file {inps.outfile} NOT found.')
40+
print(f'1) output file {inps.ts_cor_file} NOT found.')
4141
else:
4242
# check if time-series file is partly written using file size
4343
# since time-series file is not compressed
44-
with h5py.File(inps.outfile, 'r') as f:
44+
with h5py.File(inps.ts_cor_file, 'r') as f:
4545
fsize_ref = f['timeseries'].size * 4
46-
fsize = os.path.getsize(inps.outfile)
46+
fsize = os.path.getsize(inps.ts_cor_file)
4747
if fsize <= fsize_ref:
4848
flag = 'run'
49-
print(f'1) output file {inps.outfile} is NOT fully written.')
49+
print(f'1) output file {inps.ts_cor_file} is NOT fully written.')
5050

5151
else:
52-
print(f'1) output file {inps.outfile} already exists.')
52+
print(f'1) output file {inps.ts_cor_file} already exists.')
5353

5454
# check modification time
55-
infiles = [inps.timeseries_file]
55+
infiles = [inps.ts_file]
5656
if inps.geom_file:
5757
infiles.append(inps.geom_file)
5858
ti = max(os.path.getmtime(i) for i in infiles)
59-
to = os.path.getmtime(inps.outfile)
59+
to = os.path.getmtime(inps.ts_cor_file)
6060
if ti > to:
6161
flag = 'run'
6262
print(f'2) output file is NOT newer than input file: {infiles}.')
@@ -65,9 +65,9 @@ def run_or_skip(inps):
6565

6666
# check configuration
6767
if flag == 'skip':
68-
date_list_all = timeseries(inps.timeseries_file).get_date_list()
68+
date_list_all = timeseries(inps.ts_file).get_date_list()
6969
inps.excludeDate = read_exclude_date(inps.excludeDate, date_list_all, print_msg=False)[1]
70-
meta = readfile.read_attribute(inps.outfile)
70+
meta = readfile.read_attribute(inps.ts_cor_file)
7171
if any(str(vars(inps)[key]) != meta.get(key_prefix+key, 'None') for key in config_keys):
7272
flag = 'run'
7373
print(f'3) NOT all key configuration parameters are the same:{config_keys}')
@@ -161,7 +161,7 @@ def get_design_matrix4defo(inps):
161161
model['periodic'] = inps.periodic
162162

163163
# prepare SAR info
164-
ts_obj = timeseries(inps.timeseries_file)
164+
ts_obj = timeseries(inps.ts_file)
165165
date_list = ts_obj.get_date_list()
166166
seconds = ts_obj.get_metadata().get('CENTER_LINE_UTC', 0)
167167

@@ -439,7 +439,7 @@ def correct_dem_error(inps):
439439
## 1. input info
440440

441441
# 1.1 read date info
442-
ts_obj = timeseries(inps.timeseries_file)
442+
ts_obj = timeseries(inps.ts_file)
443443
ts_obj.open()
444444
num_date = ts_obj.numDate
445445
length, width = ts_obj.length, ts_obj.width
@@ -465,20 +465,18 @@ def correct_dem_error(inps):
465465
meta[key_prefix+key] = str(vars(inps)[key])
466466

467467
# 2.2 instantiate est. DEM error
468-
dem_err_file = 'demErr.h5'
469468
meta['FILE_TYPE'] = 'dem'
470469
meta['UNIT'] = 'm'
471470
ds_name_dict = {'dem' : [np.float32, (length, width), None]}
472-
writefile.layout_hdf5(dem_err_file, ds_name_dict, metadata=meta)
471+
writefile.layout_hdf5(inps.dem_err_file, ds_name_dict, metadata=meta)
473472

474473
# 2.3 instantiate corrected time-series
475-
ts_cor_file = inps.outfile
476474
meta['FILE_TYPE'] = 'timeseries'
477-
writefile.layout_hdf5(ts_cor_file, metadata=meta, ref_file=inps.timeseries_file)
475+
writefile.layout_hdf5(inps.ts_cor_file, metadata=meta, ref_file=inps.ts_file)
478476

479477
# 2.4 instantiate residual phase time-series
480-
ts_res_file = os.path.join(os.path.dirname(inps.outfile), 'timeseriesResidual.h5')
481-
writefile.layout_hdf5(ts_res_file, metadata=meta, ref_file=inps.timeseries_file)
478+
ts_res_file = os.path.join(os.path.dirname(inps.ts_cor_file), 'timeseriesResidual.h5')
479+
writefile.layout_hdf5(ts_res_file, metadata=meta, ref_file=inps.ts_file)
482480

483481

484482
## 3. run the estimation and write to disk
@@ -503,7 +501,7 @@ def correct_dem_error(inps):
503501
# 3.2 prepare the input arguments for *_patch()
504502
data_kwargs = {
505503
'G_defo' : G_defo,
506-
'ts_file' : inps.timeseries_file,
504+
'ts_file' : inps.ts_file,
507505
'geom_file' : inps.geom_file,
508506
'date_flag' : date_flag,
509507
'phase_velocity' : inps.phaseVelocity,
@@ -558,15 +556,15 @@ def correct_dem_error(inps):
558556
# DEM error - 2D
559557
block = [box[1], box[3], box[0], box[2]]
560558
writefile.write_hdf5_block(
561-
dem_err_file,
559+
inps.dem_err_file,
562560
data=delta_z,
563561
datasetName='dem',
564562
block=block)
565563

566564
# corrected time-series - 3D
567565
block = [0, num_date, box[1], box[3], box[0], box[2]]
568566
writefile.write_hdf5_block(
569-
ts_cor_file,
567+
inps.ts_cor_file,
570568
data=ts_cor,
571569
datasetName='timeseries',
572570
block=block)
@@ -586,4 +584,4 @@ def correct_dem_error(inps):
586584
m, s = divmod(time.time()-start_time, 60)
587585
print(f'time used: {m:02.0f} mins {s:02.1f} secs.')
588586

589-
return dem_err_file, ts_cor_file, ts_res_file
587+
return inps.dem_err_file, inps.ts_cor_file, ts_res_file

src/mintpy/save_qgis.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,21 @@ def gather_files(ts_file, geom_file):
4949

5050
#Can also add DEM Error here: corrected_DEM = DEM + DEM_error
5151
ts_dir = os.path.dirname(ts_file)
52-
fDict = { 'TimeSeries' : ts_file,
53-
'Velocity' : os.path.join(ts_dir, vel_file),
54-
'Coherence' : os.path.join(ts_dir, coh_file),
55-
'Mask' : os.path.join(ts_dir, msk_file),
56-
'Geometry' : geom_file,
57-
}
52+
fDict = {
53+
'TimeSeries' : ts_file,
54+
'Velocity' : os.path.join(ts_dir, vel_file),
55+
'Coherence' : os.path.join(ts_dir, coh_file),
56+
'Mask' : os.path.join(ts_dir, msk_file),
57+
'Geometry' : geom_file,
58+
}
5859

5960
#Check if the files exists.
6061
for fname in fDict.values():
6162
if not os.path.isfile(fname):
6263
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), fname)
6364

6465
for key, value in fDict.items():
65-
print(f'{key:<12}: {value}')
66+
print(f'{key:<10}: {value}')
6667
return fDict
6768

6869

@@ -86,13 +87,14 @@ def read_bounding_box(pix_box, geo_box, geom_file):
8687
return pix_box
8788

8889

89-
def write_shape_file(fDict, shp_file, box=None):
90+
def write_shape_file(fDict, shp_file, box=None, zero_first=False):
9091
'''Write time-series data to a shape file
9192
92-
Parameters: fDict - dict, with value for path of data files
93-
shp_file - str, output filename
94-
box - tuple of 4 int, in (x0, y0, x1, y1)
95-
Returns: shp_file - str, output filename
93+
Parameters: fDict - dict, with value for path of data files
94+
shp_file - str, output filename
95+
box - tuple of 4 int, in (x0, y0, x1, y1)
96+
zero_first - bool, set displacement at 1st acquisition to zero
97+
Returns: shp_file - str, output filename
9698
'''
9799

98100
shpDriver = ogr.GetDriverByName("ESRI Shapefile")
@@ -161,6 +163,8 @@ def write_shape_file(fDict, shp_file, box=None):
161163
mask = readfile.read(fDict['Mask'], box=box)[0]
162164
nValid = np.sum(mask != 0)
163165
print('number of points with time-series:', nValid)
166+
if zero_first:
167+
print('set displacement at the first acquisition to zero.')
164168

165169
lats, lons = ut.get_lat_lon(ts_obj.metadata, geom_file=fDict['Geometry'], box=box)
166170

@@ -183,6 +187,9 @@ def write_shape_file(fDict, shp_file, box=None):
183187

184188
# read data for the line
185189
ts = tsid['timeseries'][:, line, box[0]:box[2]].astype(np.float64)
190+
if zero_first:
191+
ts -= np.tile(ts[0, :], (ts.shape[0], 1))
192+
186193
coh = cohid['temporalCoherence'][line, box[0]:box[2]].astype(np.float64)
187194
vel = velid['velocity'][line, box[0]:box[2]].astype(np.float64)
188195
vel_std = velid['velocityStd'][line, box[0]:box[2]].astype(np.float64)
@@ -214,7 +221,7 @@ def write_shape_file(fDict, shp_file, box=None):
214221

215222
# update counter / progress bar
216223
counter += 1
217-
prog_bar.update(counter, every=100, suffix=f'{counter}/{nValid}')
224+
prog_bar.update(counter, every=100, suffix=f'line {counter}/{nValid}')
218225
prog_bar.close()
219226

220227
print(f'finished writing to file: {shp_file}')
@@ -235,6 +242,6 @@ def save_qgis(inps):
235242
fDict = gather_files(inps.ts_file, inps.geom_file)
236243

237244
# Write shape file
238-
write_shape_file(fDict, inps.shp_file, box=box)
245+
write_shape_file(fDict, inps.shp_file, box=box, zero_first=inps.zero_first)
239246

240247
return

0 commit comments

Comments
 (0)