#!/usr/bin/env python3 ############################################################ # Program is part of MintPy # # Copyright (c) 2013, Zhang Yunjun, Heresh Fattahi # # Author: Zhang Yunjun, Heresh Fattahi, 2013 # ############################################################ # Recommend import: # from mintpy import view import os import sys import re import argparse import datetime as dt import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import make_axes_locatable import warnings warnings.filterwarnings("ignore", category=UserWarning, module="matplotlib") import cartopy.crs as ccrs from mintpy.objects import ( giantIfgramStack, geometryDatasetNames, ifgramDatasetNames, ifgramStack, timeseriesKeyNames, timeseries, ) from mintpy.objects.gps import GPS from mintpy.utils import ( arg_group, ptime, readfile, utils as ut, plot as pp, ) from mintpy.multilook import multilook_data from mintpy import subset, version ################################################################################################## EXAMPLE = """example: view.py velocity.h5 view.py velocity.h5 velocity --wrap --wrap-range -2 2 -c cmy --lalo-label view.py velocity.h5 --ref-yx 210 566 #change reference pixel for display view.py velocity.h5 --sub-lat 31.05 31.10 --sub-lon 130.05 130.10 #subset in lalo / yx view.py timeseries.h5 view.py timeseries.h5 -m no #do not use auto mask view.py timeseries.h5 --ref-date 20101120 #change reference date view.py timeseries.h5 --ex drop_date.txt #exclude dates to plot view.py timeseries.h5 '*2017*' '*2018*' #all acquisitions in 2017 and 2018 view.py timeseries.h5 20200616_20200908 #reconstruct interferogram on the fly view.py ifgramStack.h5 coherence view.py ifgramStack.h5 unwrapPhase- #unwrapPhase only in the presence of unwrapPhase_bridging view.py ifgramStack.h5 -n 6 #the 6th slice view.py ifgramStack.h5 20171010_20171115 #all data related with 20171010_20171115 view.py ifgramStack.h5 'coherence*20171010*' #all coherence related with 20171010 view.py ifgramStack.h5 unwrapPhase-20070927_20100217 --zero-mask --wrap #wrapped phase view.py ifgramStack.h5 unwrapPhase-20070927_20100217 --mask ifgramStack.h5 #mask using connected components # GPS (for one subplot in geo-coordinates only) view.py geo_velocity_msk.h5 velocity --show-gps --gps-label #show locations of available GPS view.py geo_velocity_msk.h5 velocity --show-gps --gps-comp enu2los --ref-gps GV01 view.py geo_timeseries_ERA5_ramp_demErr.h5 20180619 --ref-date 20141213 --show-gps --gps-comp enu2los --ref-gps GV01 # Save and Output view.py velocity.h5 --save view.py velocity.h5 --nodisplay view.py geo_velocity.h5 velocity --nowhitespace """ PLOT_TEMPLATE = """Plot Setting: plot.name = 'Yunjun et al., 2016, AGU, Fig 4f' plot.type = LOS_VELOCITY plot.startDate = plot.endDate = plot.displayUnit = cm/yr plot.displayMin = -2 plot.displayMax = 2 plot.colormap = jet plot.subset.lalo = 33.05:33.15, 131.15:131.27 plot.seed.lalo = 33.0651, 131.2076 """ def create_parser(): parser = argparse.ArgumentParser(description='Plot InSAR Product in 2D', formatter_class=argparse.RawTextHelpFormatter, epilog=EXAMPLE) infile = parser.add_argument_group('Input File', 'File/Dataset to display') infile.add_argument('file', type=str, help='file for display') infile.add_argument('dset', type=str, nargs='*', default=[], help='optional - dataset(s) to display (default: %(default)s).') infile.add_argument('-n', '--dset-num', dest='dsetNumList', metavar='NUM', type=int, nargs='*', default=[], help='optional - order number of date/dataset(s) to display (default: %(default)s).') infile.add_argument('--nosearch', dest='search_dset', action='store_false', help='Disable glob search for input dset.') infile.add_argument('--ex', '--exclude', dest='exDsetList', metavar='Dset', nargs='*', default=[], help='dates will not be displayed (default: %(default)s).') parser.add_argument('--show-kept','--show-kept-ifgram', dest='plot_drop_ifgram', action='store_false', help='display kept interferograms only, without dropped interferograms') parser.add_argument('--plot-setting', dest='disp_setting_file', help='Template file with plot setting.\n'+PLOT_TEMPLATE) parser.add_argument('--noverbose', dest='print_msg', action='store_false', help='Disable the verbose message printing (default: %(default)s).') parser.add_argument('--math', dest='math_operation', choices={'square','sqrt','reverse','inverse'}, help='Apply the math operation before displaying [for single subplot ONLY].\n' 'E.g. plot the std. dev. of the variance file.\n' ' square = x^2\n' ' sqrt = x^1/2\n' ' reverse = x * -1\n' ' inverse = 1 / x') parser = arg_group.add_data_disp_argument(parser) parser = arg_group.add_dem_argument(parser) parser = arg_group.add_figure_argument(parser) parser = arg_group.add_gps_argument(parser) parser = arg_group.add_mask_argument(parser) parser = arg_group.add_map_argument(parser) parser = arg_group.add_memory_argument(parser) parser = arg_group.add_point_argument(parser) parser = arg_group.add_reference_argument(parser) parser = arg_group.add_save_argument(parser) parser = arg_group.add_subset_argument(parser) return parser def cmd_line_parse(iargs=None): """Command line parser.""" parser = create_parser() inps = parser.parse_args(args=iargs) # check invalid file inputs for key in ['file','dem_file','mask_file','pts_file']: fname = vars(inps)[key] if fname not in [None, 'no'] and not os.path.isfile(fname): raise FileNotFoundError('input {} file {} NOT exist!'.format(key, fname)) # --exclude if inps.exDsetList: inps.exDsetList = ptime.read_date_list(inps.exDsetList) # If output flie name assigned or figure shown is turned off, turn on the figure save if inps.outfile or not inps.disp_fig: inps.save_fig = True if inps.lalo_step: inps.lalo_label = True if inps.zero_mask: # turn OFF default mask file detection for --zero-mask # extra manual mask file is still supported if not inps.mask_file: inps.mask_file = 'no' if not inps.disp_whitespace: inps.disp_axis = False inps.disp_title = False inps.disp_cbar = False if not inps.disp_axis: inps.disp_tick = False if inps.flip_lr or inps.flip_ud: inps.auto_flip = False # check geo-only options for files in radar-coordinates geo_opt_names = ['--coord', '--show-gps', '--coastline', '--lalo-label', '--lalo-step', '--scalebar'] geo_opt_names = list(set(geo_opt_names) & set(iargs)) if geo_opt_names and 'Y_FIRST' not in readfile.read_attribute(inps.file).keys(): for opt_name in geo_opt_names: print(f'WARNING: {opt_name} is NOT supported for files in radar-coordinate, ignore it and continue.') # verbose print using --noverbose option global vprint vprint = print if inps.print_msg else lambda *args, **kwargs: None # print view.py command line if --noverbose (used in smallbaselineApp.py) if not inps.print_msg: print('view.py', ' '.join(iargs)) if inps.disp_setting_file: inps = update_inps_with_display_setting_file(inps, inps.disp_setting_file) # Backend setting if not inps.disp_fig: plt.switch_backend('Agg') return inps def run_or_skip(inps): vprint('update mode: ON') flag = 'skip' # get existed output file names outfiles = [] for fname in inps.outfile: fnames = [fname, os.path.join(os.path.dirname(fname), 'pic', os.path.basename(fname))] fnames = [i for i in fnames if os.path.isfile(i)] if len(fnames) > 0: outfiles.append(fnames[0]) else: flag = 'run' if flag == 'skip': ti = os.path.getmtime(inps.file) to = min([os.path.getmtime(i) for i in outfiles]) if ti > to: flag = 'run' else: vprint('{} exist and are newer than input file: {} --> skip.'.format(outfiles, inps.file)) return flag ################################################################################################## def update_inps_with_display_setting_file(inps, disp_set_file): """Update inps using values from display setting file""" disp_set_dict = readfile.read_template(disp_set_file) if not inps.disp_unit and 'plot.displayUnit' in disp_set_dict.keys(): inps.disp_unit = disp_set_dict['plot.displayUnit'] if not inps.disp_min and 'plot.displayMin' in disp_set_dict.keys(): inps.disp_min = float(disp_set_dict['plot.displayMin']) if not inps.disp_max and 'plot.displayMax' in disp_set_dict.keys(): inps.disp_max = float(disp_set_dict['plot.displayMax']) if not inps.colormap and 'plot.colormap' in disp_set_dict.keys(): inps.colormap = disp_set_dict['plot.colormap'] if not inps.subset_lat and 'plot.subset.lalo' in disp_set_dict.keys(): inps.subset_lat = [float(n) for n in disp_set_dict['plot.subset.lalo'].replace(',', ' ').split()[0:2]] if not inps.subset_lon and 'plot.subset.lalo' in disp_set_dict.keys(): inps.subset_lon = [float(n) for n in disp_set_dict['plot.subset.lalo'].replace(',', ' ').split()[2:4]] if not inps.ref_lalo and 'plot.seed.lalo' in disp_set_dict.keys(): inps.ref_lalo = [float(n) for n in disp_set_dict['plot.referenceLalo'].replace(',', ' ').split()] return inps def update_inps_with_file_metadata(inps, metadata): # Subset # Convert subset input into bounding box in radar / geo coordinate # geo_box = None if atr is not geocoded. coord = ut.coordinate(metadata) inps.pix_box, inps.geo_box = subset.subset_input_dict2box(vars(inps), metadata) inps.pix_box = coord.check_box_within_data_coverage(inps.pix_box) inps.geo_box = coord.box_pixel2geo(inps.pix_box) # Out message inps.data_box = (0, 0, inps.width, inps.length) vprint('data coverage in y/x: '+str(inps.data_box)) vprint('subset coverage in y/x: '+str(inps.pix_box)) vprint('data coverage in lat/lon: '+str(coord.box_pixel2geo(inps.data_box))) vprint('subset coverage in lat/lon: '+str(inps.geo_box)) vprint('------------------------------------------------------------------------') # DEM contour display if max(inps.pix_box[3] - inps.pix_box[1], inps.pix_box[2] - inps.pix_box[0]) > 2e3: inps.disp_dem_contour = False if inps.dem_file: vprint('area exceed 2000 pixels, turn off default DEM contour display') # Multilook, if too many subplots in one figure for less memory and faster speed if inps.multilook_num > 1: inps.multilook = True # Colormap inps.colormap = pp.auto_colormap_name(metadata, inps.colormap, datasetName=inps.dset[0], print_msg=inps.print_msg) # Reference Point # Convert ref_lalo if existed, to ref_yx, and use ref_yx for the following # ref_yx is referenced to input data coverage, not subseted area for display if inps.ref_lalo: vprint('input reference point in lat/lon: {}'.format(inps.ref_lalo)) if not inps.geo_box and not coord.lookup_file: print('WARNING: --ref-lalo is NOT supported when 1) file is radar-coded AND 2) no lookup table file found') print(' --> ignore the --ref-lalo input and continue.') inps.ref_lalo = [] else: inps.ref_yx = coord.geo2radar(inps.ref_lalo[0], inps.ref_lalo[1]) vprint('input reference point in y /x : {}'.format(inps.ref_yx)) # ref_lalo if inps.ref_yx and inps.geo_box: inps.ref_lalo = [coord.yx2lalo(inps.ref_yx[0], coord_type='y'), coord.yx2lalo(inps.ref_yx[1], coord_type='x')] elif 'REF_LAT' in metadata.keys(): inps.ref_lalo = [float(metadata['REF_LAT']), float(metadata['REF_LON'])] else: inps.ref_lalo = None # Points of interest inps = pp.read_pts2inps(inps, coord) # Unit and Wrap inps.disp_unit, inps.wrap = pp.check_disp_unit_and_wrap(metadata, disp_unit=inps.disp_unit, wrap=inps.wrap, wrap_range=inps.wrap_range, print_msg=inps.print_msg) # Map Projection via cartopy # cartopy requires that: # 1. file is geocoded AND # 2. file coordinates are in the unit of degrees / meters AND # 3. set to display in geo-coordinates # use cartopy (by initiating inps.map_proj_obj) ONLY IF: # 1. show fancy lat/lon label via --lalo-label OR # 2. show coastline via --coastline inps.map_proj_obj = None inps.coord_unit = metadata.get('Y_UNIT', 'degrees').lower() if (inps.geo_box and inps.coord_unit.startswith(('deg', 'meter')) and inps.fig_coord == 'geo' and (inps.lalo_label or inps.coastline)): # get projection name from the data coord unit # https://scitools.org.uk/cartopy/docs/latest/crs/projections.html msg = 'initiate cartopy map projection: ' if inps.coord_unit.startswith('deg'): inps.map_proj_obj = ccrs.PlateCarree() vprint(msg + 'PlateCarree') elif inps.coord_unit.startswith('meter'): if 'UTM_ZONE' in metadata.keys(): utm_zone = metadata['UTM_ZONE'] inps.map_proj_obj = ccrs.UTM(utm_zone) vprint(msg + f'UTM zone {utm_zone}') # check --lalo-label (works for PlateCarree only) if inps.lalo_label: raise ValueError('--lalo-label is NOT supported for projection: UTM') else: print('WARNING: Un-recognized coordinate unit: {}'.format(inps.coord_unit)) print(' Switch to the native Y/X and continue to plot') inps.fig_coord = 'radar' # Min / Max - Display if not inps.vlim: if (any(i in inps.key.lower() for i in ['coherence', '.cor']) or (inps.key == 'ifgramStack' and inps.dset[0].split('-')[0] in ['coherence']) or inps.dset[0] == 'cmask'): inps.vlim = [0.0, 1.0] elif inps.key in ['.int'] or inps.wrap: inps.vlim = inps.wrap_range # Transparency - Alpha if not inps.transparency: # Auto adjust transparency value when showing shaded relief DEM if inps.dem_file and inps.disp_dem_shade: inps.transparency = 0.8 else: inps.transparency = 1.0 # Flip Left-Right / Up-Down if inps.auto_flip: inps.flip_lr, inps.flip_ud = pp.auto_flip_direction(metadata, print_msg=inps.print_msg) # Figure Title if not inps.fig_title: inps.fig_title = pp.auto_figure_title(metadata['FILE_PATH'], datasetNames=inps.dset, inps_dict=vars(inps)) vprint('figure title: '+inps.fig_title) # Figure output file name if not inps.outfile: inps.outfile = ['{}{}'.format(inps.fig_title, inps.fig_ext)] inps = update_figure_setting(inps) return inps ################################################################################################## def update_data_with_plot_inps(data, metadata, inps): # 1. spatial referencing with respect to the seed point if inps.ref_yx: # and inps.ref_yx != [int(metadata['REF_Y']), int(metadata['REF_X'])]: # update ref_y/x to subset try: ref_y = inps.ref_yx[0] - inps.pix_box[1] ref_x = inps.ref_yx[1] - inps.pix_box[0] except: pass # update ref_y/x for multilooking if inps.multilook_num > 1: ref_y = int((ref_y - int(inps.multilook_num / 2)) / inps.multilook_num) ref_x = int((ref_x - int(inps.multilook_num / 2)) / inps.multilook_num) # applying spatial referencing if len(data.shape) == 2: ref_val = data[ref_y, ref_x] if not np.ma.is_masked(ref_val) and not np.isnan(ref_val): data -= ref_val vprint('set reference pixel to: {}'.format(inps.ref_yx)) else: msg = 'WARNING: input reference pixel ({}, {}) has either masked or NaN value!'.format(ref_y, ref_x) msg += ' -> skip re-referencing.' print(msg) inps.ref_yx = None elif len(data.shape) == 3: # read ref_val if 0 <= ref_y < data.shape[-2] and 0 <= ref_x < data.shape[-1]: ref_val = np.squeeze(data[:, ref_y, ref_x]) elif inps.key == 'timeseries': ref_box = [inps.ref_yx[1], inps.ref_yx[0], inps.ref_yx[1] + 1, inps.ref_yx[0] + 1] ref_val = readfile.read(inps.file, datasetName=inps.dset, box=ref_box, print_msg=False)[0] else: raise ValueError('input reference point {} is out of data coverage!'.format(inps.ref_yx)) # apply referencing if not np.ma.is_masked(ref_val) and np.all(~np.isnan(ref_val)): data -= np.tile(ref_val.reshape(-1, 1, 1), (1, data.shape[1], data.shape[2])) vprint('set reference pixel to: {}'.format(inps.ref_yx)) else: msg = 'WARNING: input reference pixel ({}, {}) has either masked or NaN value!'.format(ref_y, ref_x) msg += ' -> skip re-referencing.' print(msg) inps.ref_yx = None else: inps.ref_yx = None # 2. scale data based on the display unit and re-wrap (data, inps.disp_unit, inps.disp_scale, inps.wrap) = pp.scale_data4disp_unit_and_rewrap(data, metadata=metadata, disp_unit=inps.disp_unit, wrap=inps.wrap, wrap_range=inps.wrap_range, print_msg=inps.print_msg) if inps.wrap: inps.vlim = inps.wrap_range # math operation if inps.math_operation: vprint('Apply math operation: {}'.format(inps.math_operation)) if inps.math_operation == 'square': data = np.square(data) elif inps.math_operation == 'sqrt': data = np.sqrt(data) elif inps.math_operation == 'reverse': data *= -1 elif inps.math_operation == 'inverse': data = 1. / data # 4. update display min/max inps.dlim = [np.nanmin(data), np.nanmax(data)] if not inps.vlim: # and data.ndim < 3: inps.cmap_lut, inps.vlim = pp.auto_adjust_colormap_lut_and_disp_limit(data, print_msg=inps.print_msg) vprint('data range: {} {}'.format(inps.dlim, inps.disp_unit)) vprint('display range: {} {}'.format(inps.vlim, inps.disp_unit)) return data, inps ################################################################################################## def plot_slice(ax, data, metadata, inps=None): """Plot one slice of matrix Parameters: ax : matplot.pyplot axes object data : 2D np.array, metadata : dictionary, attributes of data inps : Namespace, optional, input options for display Returns: ax : matplot.pyplot axes object inps : Namespace for input options im : matplotlib.image.AxesImage object cbar : matplotlib.colorbar.Colorbar object Example: import matplotlib.pyplot as plt import mintpy.utils.readfile as readfile import mintpy.view as pv data, atr = readfile.read('velocity.h5') fig = plt.figure() ax = fig.add_axes([0.1,0.1,0.8,0.8]) ax = pv.plot_slice(ax, data, atr)[0] plt.show() """ global vprint vprint = print if inps.print_msg else lambda *args, **kwargs: None #--------------------------- Initial a inps Namespace if no inps input -----------------------# if not inps: inps = cmd_line_parse(['']) inps = update_inps_with_file_metadata(inps, metadata) if isinstance(inps.colormap, str): inps.colormap = pp.ColormapExt(inps.colormap, cmap_lut=inps.cmap_lut, vlist=inps.cmap_vlist).colormap # read DEM if inps.dem_file: dem, dem_metadata, dem_pix_box = pp.read_dem(inps.dem_file, pix_box=inps.pix_box, geo_box=inps.geo_box, print_msg=inps.print_msg) vprint('display data in transparency: '+str(inps.transparency)) #----------------------- Plot in Geo-coordinate --------------------------------------------# num_row, num_col = data.shape if (inps.geo_box and inps.coord_unit.startswith(('deg', 'meter')) and inps.fig_coord == 'geo'): vprint('plot in geo-coordinate') # Draw coastline using cartopy resolution parameters if inps.coastline: vprint('draw coast line with resolution: {}'.format(inps.coastline)) ax.coastlines(resolution=inps.coastline) # Plot DEM if inps.dem_file: vprint('plotting DEM background ...') pp.plot_dem_background(ax=ax, geo_box=inps.geo_box, dem=dem, inps=inps, print_msg=inps.print_msg) # Plot Data coord = ut.coordinate(metadata) vprint('plotting image ...') if inps.disp_gps and inps.gps_component and inps.ref_gps_site: ref_site_lalo = GPS(site=inps.ref_gps_site).get_stat_lat_lon(print_msg=False) y, x = coord.geo2radar(ref_site_lalo[0], ref_site_lalo[1])[0:2] y -= inps.pix_box[1] x -= inps.pix_box[0] data -= data[y, x] vprint(('referencing InSAR data to the pixel nearest to ' 'GPS station: {} at {}').format(inps.ref_gps_site, ref_site_lalo)) # do not show the original InSAR reference point inps.disp_ref_pixel = False extent = (inps.geo_box[0], inps.geo_box[2], inps.geo_box[3], inps.geo_box[1]) # (W, E, S, N) im = ax.imshow(data, cmap=inps.colormap, vmin=inps.vlim[0], vmax=inps.vlim[1], extent=extent, origin='upper', interpolation='nearest', alpha=inps.transparency, animated=inps.animation, zorder=1) # Scale Bar if inps.coord_unit.startswith('deg') and (inps.geo_box[2] - inps.geo_box[0]) > 30: # do not plot scalebar if the longitude span > 30 deg inps.disp_scalebar = False if inps.disp_scalebar: vprint('plot scale bar: {}'.format(inps.scalebar)) pp.draw_scalebar(ax, geo_box=inps.geo_box, unit=inps.coord_unit, loc=inps.scalebar, labelpad=inps.scalebar_pad, font_size=inps.font_size) # Lat Lon labels if inps.lalo_label: pp.draw_lalo_label(inps.geo_box, ax, lalo_step=inps.lalo_step, lalo_loc=inps.lalo_loc, lalo_max_num=inps.lalo_max_num, font_size=inps.font_size, projection=inps.map_proj_obj, print_msg=inps.print_msg) else: ax.tick_params(which='both', direction='in', labelsize=inps.font_size, left=True, right=True, top=True, bottom=True) # Plot Reference Point if inps.disp_ref_pixel and inps.ref_lalo: ax.plot(inps.ref_lalo[1], inps.ref_lalo[0], inps.ref_marker, ms=inps.ref_marker_size) vprint('plot reference point') # Plot points of interest if inps.pts_lalo is not None: ax.plot(inps.pts_lalo[:, 1], inps.pts_lalo[:, 0], inps.pts_marker, ms=inps.pts_marker_size, mec='k', mew=1.) vprint('plot points of interest') # Show UNR GPS stations if inps.disp_gps: SNWE = (inps.geo_box[3], inps.geo_box[1], inps.geo_box[0], inps.geo_box[2]) ax = pp.plot_gps(ax, SNWE, inps, metadata, print_msg=inps.print_msg) # Status bar if inps.dem_file: coord_dem = ut.coordinate(dem_metadata) dem_len, dem_wid = dem.shape def format_coord(x, y): msg = 'E={:.4f}, N={:.4f}'.format(x, y) col = coord.lalo2yx(x, coord_type='lon') - inps.pix_box[0] row = coord.lalo2yx(y, coord_type='lat') - inps.pix_box[1] if 0 <= col < num_col and 0 <= row < num_row: v = data[row, col] if np.isnan(v) or np.ma.is_masked(v): msg += ', v=[]' else: msg += ', v={:.3f}'.format(v) # DEM if inps.dem_file: dem_col = coord_dem.lalo2yx(x, coord_type='lon') - dem_pix_box[0] dem_row = coord_dem.lalo2yx(y, coord_type='lat') - dem_pix_box[1] if 0 <= dem_col < dem_wid and 0 <= dem_row < dem_len: h = dem[dem_row, dem_col] if not np.isnan(h): msg += ', h={:.0f}'.format(h) # x/y msg += ', x={:.0f}, y={:.0f}'.format(col+inps.pix_box[0], row+inps.pix_box[1]) return msg ax.format_coord = format_coord #------------------------ Plot in Y/X-coordinate ------------------------------------------------# else: inps.fig_coord = 'radar' vprint('plotting in Y/X coordinate ...') # Plot DEM if inps.dem_file: vprint('plotting DEM background ...') pp.plot_dem_background(ax=ax, geo_box=None, dem=dem, inps=inps, print_msg=inps.print_msg) # Plot Data vprint('plotting Data ...') extent = (inps.pix_box[0]-0.5, inps.pix_box[2]-0.5, inps.pix_box[3]-0.5, inps.pix_box[1]-0.5) #(left, right, bottom, top) in data coordinates im = ax.imshow(data, cmap=inps.colormap, vmin=inps.vlim[0], vmax=inps.vlim[1], extent=extent, interpolation='nearest', alpha=inps.transparency, zorder=1) ax.tick_params(labelsize=inps.font_size) # Plot Seed Point if inps.disp_ref_pixel: ref_y, ref_x = None, None if inps.ref_yx: ref_y, ref_x = inps.ref_yx[0], inps.ref_yx[1] elif 'REF_Y' in metadata.keys(): ref_y, ref_x = int(metadata['REF_Y']), int(metadata['REF_X']) if ref_y and ref_x: ax.plot(ref_x, ref_y, inps.ref_marker, ms=inps.ref_marker_size) vprint('plot reference point') # Plot points of interest if inps.pts_yx is not None: ax.plot(inps.pts_yx[:, 1], inps.pts_yx[:, 0], inps.pts_marker, ms=inps.ref_marker_size, mec='black', mew=1.) vprint('plot points of interest') # temporary test code temp_test = False if temp_test: # Champlain Towers South AOI pts_yx = np.array([ [929,1456], [933,1457], [933,1436], [930,1431], [929,1456], ]) ax.plot(pts_yx[:, 1], pts_yx[:, 0], '-', ms=inps.ref_marker_size, mec='black', mew=1.) ax.set_xlim(extent[0:2]) ax.set_ylim(extent[2:4]) # Status bar # read lats/lons if exist geom_file = os.path.join(os.path.dirname(metadata['FILE_PATH']), 'inputs/geometryRadar.h5') if os.path.isfile(geom_file): try: lats = readfile.read(geom_file, datasetName='latitude', box=inps.pix_box, print_msg=False)[0] lons = readfile.read(geom_file, datasetName='longitude', box=inps.pix_box, print_msg=False)[0] except: msg = 'WARNING: no latitude / longitude found in file: {}, '.format(os.path.basename(geom_file)) msg += 'skip showing lat/lon in the status bar.' vprint(msg) geom_file = None else: geom_file = None def format_coord(x, y): msg = 'x={:.1f}, y={:.1f}'.format(x, y) col = int(np.rint(x - inps.pix_box[0])) row = int(np.rint(y - inps.pix_box[1])) if 0 <= col < num_col and 0 <= row < num_row: v = data[row, col] if np.isnan(v) or np.ma.is_masked(v): msg += ', v=[]' else: msg += ', v={:.3f}'.format(v) # DEM if inps.dem_file: h = dem[row, col] if not np.isnan(h): msg += ', h={:.0f} m'.format(h) # lat/lon if geom_file: msg += ', lat={:.4f}, lon={:.4f}'.format(lats[row, col], lons[row, col]) return msg ax.format_coord = format_coord #---------------------- Figure Setting ----------------------------------------# # 3.1 Colorbar cbar = None if inps.disp_cbar: divider = make_axes_locatable(ax) cax = divider.append_axes(inps.cbar_loc, inps.cbar_size, pad=inps.cbar_size, axes_class=plt.Axes) inps, cbar = pp.plot_colorbar(inps, im, cax) # 3.2 Title if inps.disp_title: ax.set_title(inps.fig_title, fontsize=inps.font_size, color=inps.font_color) # 3.3 Flip Left-Right / Up-Down if inps.flip_lr: vprint('flip figure left and right') ax.invert_xaxis() if inps.flip_ud: vprint('flip figure up and down') ax.invert_yaxis() # 3.4 Turn off axis if not inps.disp_axis: ax.axis('off') vprint('turn off axis display') # 3.5 Turn off tick label if inps.disp_tick: # manually turn ON to enable tick labels for UTM with cartopy # link: https://github.com/SciTools/cartopy/issues/491 ax.xaxis.set_visible(True) ax.yaxis.set_visible(True) else: # ax.set_xticklabels([]) # ax.set_yticklabels([]) ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) return ax, inps, im, cbar def read_input_file_info(inps): # File Basic Info atr = readfile.read_attribute(inps.file) msg = 'input file is ' if not inps.file.endswith(('.h5', '.he5')): msg += '{} '.format(atr['PROCESSOR']) msg += '{} file: {}'.format(atr['FILE_TYPE'], os.path.abspath(inps.file)) if 'DATA_TYPE' in atr.keys(): msg += ' in {} format'.format(atr['DATA_TYPE']) vprint('run {} in {}'.format(os.path.basename(__file__), version.version_description)) vprint(msg) ## size and name inps.length = int(atr['LENGTH']) inps.width = int(atr['WIDTH']) inps.key = atr['FILE_TYPE'] inps.fileBase = os.path.splitext(os.path.basename(inps.file))[0] inps.fileExt = os.path.splitext(inps.file)[1] vprint('file size in y/x: {}'.format((inps.length, inps.width))) # File dataset List inps.sliceList = readfile.get_slice_list(inps.file, no_complex=True) # Read input list of dataset to display inps, atr = read_dataset_input(inps) return inps, atr def search_dataset_input(allList, inList=[], inNumList=[], search_dset=True): """Get dataset(es) from input dataset / dataset_num""" # make a copy to avoid weird variable behavior inNumList = [x for x in inNumList] # inList --> inNumList --> outNumList --> outList if inList: if isinstance(inList, str): inList = [inList] tempList = [] if search_dset: for ds in inList: # style of regular expression if '*' not in ds: ds = '*{}*'.format(ds) ds = ds.replace('*','.*') # search tempList += [e for e in allList if re.match(ds, e) is not None] else: tempList += [i for i in inList if i in allList] tempList = sorted(list(set(tempList))) inNumList += [allList.index(e) for e in tempList] # inNumList --> outNumList outNumList = sorted(list(set(inNumList))) # outNumList --> outList outList = [allList[i] for i in outNumList] return outList, outNumList def read_dataset_input(inps): """Check input / exclude / reference dataset input with file dataset list""" # read inps.dset + inps.dsetNumList --> inps.dsetNumList if len(inps.dset) > 0 or len(inps.dsetNumList) > 0: # message if len(inps.dset) > 0: vprint('input dataset: "{}"'.format(inps.dset)) # special rule for special file types if inps.key == 'velocity': inps.search_dset = False vprint('turning glob search OFF for {} file'.format(inps.key)) elif inps.key == 'timeseries' and len(inps.dset) == 1 and '_' in inps.dset[0]: date1, date2 = inps.dset[0].split('_') inps.dset = [date2] inps.ref_date = date1 # search inps.dsetNumList = search_dataset_input(inps.sliceList, inps.dset, inps.dsetNumList, inps.search_dset)[1] else: # default dataset to display for certain type of files if inps.key == 'ifgramStack': inps.dset = ['unwrapPhase'] elif inps.key == 'HDFEOS': inps.dset = ['displacement'] elif inps.key == 'giantTimeseries': inps.dset = 'recons' elif inps.key == 'giantIfgramStack': obj = giantIfgramStack(inps.file) obj.open(print_msg=False) inps.dset = [obj.sliceList[0].split('-')[0]] else: inps.dset = inps.sliceList # do not plot 3D-bperp by default if inps.key == 'geometry': inps.dset = [x for x in inps.dset if not x.startswith('bperp')] inps.dsetNumList = search_dataset_input(inps.sliceList, inps.dset, inps.dsetNumList, inps.search_dset)[1] # read inps.exDsetList inps.exDsetList, inps.exDsetNumList = search_dataset_input(inps.sliceList, inps.exDsetList, [], inps.search_dset) # read inps.plot_drop_ifgram drop_num_list = [] atr = readfile.read_attribute(inps.file) if not inps.plot_drop_ifgram: if atr['FILE_TYPE'] == 'ifgramStack': vprint('do not show the dropped interferograms') date12_drop_list = ifgramStack(inps.file).get_drop_date12_list() drop_slice_list = [x for x in inps.sliceList if x.split('-')[1] in date12_drop_list] drop_num_list = [inps.sliceList.index(x) for x in drop_slice_list] else: vprint('--show-kept option does not apply to file type: {}, ignore and continue.'.format(atr['FILE_TYPE'])) inps.plot_drop_ifgram = True # get inps.dset inps.dsetNumList = sorted(list(set(inps.dsetNumList) - set(inps.exDsetNumList) - set(drop_num_list))) inps.dset = [inps.sliceList[i] for i in inps.dsetNumList] inps.dsetNum = len(inps.dset) if inps.ref_date: if inps.key not in timeseriesKeyNames: inps.ref_date = None ref_date = search_dataset_input(inps.sliceList, [inps.ref_date], [], inps.search_dset)[0][0] if not ref_date: print('WARNING: input reference date {} is not included in input file! Ignore it and continue'.format(inps.ref_date)) inps.ref_date = None else: inps.ref_date = ref_date if inps.key in ['ifgramStack']: vprint('num of datasets in file {}: {}'.format(os.path.basename(inps.file), len(inps.sliceList))) vprint('num of datasets to exclude: {}'.format(len(inps.exDsetList))) vprint('num of datasets to display: {}'.format(len(inps.dset))) else: vprint('num of datasets in file {}: {}'.format(os.path.basename(inps.file), len(inps.sliceList))) vprint('datasets to exclude ({}):\n{}'.format(len(inps.exDsetList), inps.exDsetList)) vprint('datasets to display ({}):\n{}'.format(len(inps.dset), inps.dset)) if inps.ref_date and inps.key in timeseriesKeyNames: vprint('input reference date: {}'.format(inps.ref_date)) if inps.dsetNum == 0: msg = 'No input dataset found!' msg += '\navailable datasets:\n{}'.format(inps.sliceList) raise Exception(msg) atr = readfile.read_attribute(inps.file, datasetName=inps.dset[0].split('-')[0]) return inps, atr def update_figure_setting(inps): """Update figure setting based on number of subplots/datasets 1) fig_size and font_size 2) for multi: figure/row/column number 3) for multi: output file name """ length = float(inps.pix_box[3]-inps.pix_box[1]) width = float(inps.pix_box[2]-inps.pix_box[0]) # One Plot if inps.dsetNum == 1: if not inps.font_size: inps.font_size = 16 if not inps.fig_size: # update length/width based on lat/lon if inps.geo_box and inps.fig_coord == 'geo': length = abs(inps.geo_box[3] - inps.geo_box[1]) width = abs(inps.geo_box[2] - inps.geo_box[0]) # auto figure size inps.fig_size = pp.auto_figure_size(ds_shape=(length, width), disp_cbar=inps.disp_cbar, print_msg=inps.print_msg) # Multiple Plots else: if not inps.fig_size: inps.fig_size = pp.default_figsize_multi vprint('figure size : [{:.2f}, {:.2f}]'.format(inps.fig_size[0], inps.fig_size[1])) # Figure number (<= 200 subplots per figure) if not inps.fig_num: inps.fig_num = 1 while inps.dsetNum/float(inps.fig_num) > 160.0: inps.fig_num += 1 # Row/Column number if (inps.fig_row_num == 1 and inps.fig_col_num == 1 and all(i not in inps.argv for i in ['--nrows', '--ncols'])): # calculate row and col number based on input info data_shape = [length*1.1, width] fig_size4plot = [inps.fig_size[0]*0.95, inps.fig_size[1]] (inps.fig_row_num, inps.fig_col_num) = pp.auto_row_col_num(inps.dsetNum, data_shape, fig_size4plot, inps.fig_num) inps.fig_num = np.ceil(float(inps.dsetNum) / float(inps.fig_row_num * inps.fig_col_num)).astype(int) vprint('dataset number: '+str(inps.dsetNum)) vprint('row number: '+str(inps.fig_row_num)) vprint('column number: '+str(inps.fig_col_num)) vprint('figure number: '+str(inps.fig_num)) if not inps.font_size: inps.font_size = 12 if inps.fig_row_num * inps.fig_col_num > 50: inps.font_size = 8 # Output File Name if inps.outfile: inps.outdir = os.path.dirname(inps.outfile[0]) inps.outfile_base, inps.fig_ext = os.path.splitext(os.path.basename(inps.outfile[0])) inps.fig_ext = inps.fig_ext.lower() else: inps.outdir = os.path.dirname(inps.file) inps.outfile_base = os.path.splitext(os.path.basename(inps.file))[0] # suffix if (inps.pix_box[2]-inps.pix_box[0])*(inps.pix_box[3]-inps.pix_box[1]) < width*length: inps.outfile_base += '_sub' if inps.wrap: inps.outfile_base += '_wrap' if (inps.wrap_range[1] - inps.wrap_range[0]) != 2*np.pi: inps.outfile_base += str(inps.wrap_range[1] - inps.wrap_range[0]) if inps.ref_date: inps.outfile_base += '_ref'+inps.ref_date if inps.exDsetList: inps.outfile_base += '_ex' # output file name list if inps.fig_num == 1: inps.outfile = ['{}{}'.format(inps.outfile_base, inps.fig_ext)] else: inps.outfile = ['{}_{}{}'.format(inps.outfile_base, str(j), inps.fig_ext) for j in range(1, inps.fig_num+1)] inps.outfile = [os.path.join(inps.outdir, outfile) for outfile in inps.outfile] return inps def read_data4figure(i_start, i_end, inps, metadata): """Read multiple datasets for one figure into 3D matrix based on i_start/end""" data = np.zeros((i_end - i_start, int((inps.pix_box[3] - inps.pix_box[1]) / inps.multilook_num), int((inps.pix_box[2] - inps.pix_box[0]) / inps.multilook_num), ), dtype=np.float32) # fast reading for single dataset type if (len(inps.dsetFamilyList) == 1 and inps.key in ['timeseries', 'giantTimeseries', 'ifgramStack', 'HDFEOS', 'geometry']): vprint('reading data as a 3D matrix ...') dset_list = [inps.dset[i] for i in range(i_start, i_end)] data[:] = readfile.read(inps.file, datasetName=dset_list, box=inps.pix_box, xstep=inps.multilook_num, ystep=inps.multilook_num, print_msg=inps.print_msg)[0] if inps.key == 'ifgramStack': # reference pixel info in unwrapPhase if inps.dsetFamilyList[0].startswith('unwrapPhase') and inps.file_ref_yx: ref_y, ref_x = inps.file_ref_yx ref_box = (ref_x, ref_y, ref_x+1, ref_y+1) ref_data = readfile.read(inps.file, datasetName=dset_list, box=ref_box, print_msg=False)[0] for i in range(data.shape[0]): mask = data[i, :, :] != 0. data[i, mask] -= ref_data[i] # slow reading with one 2D matrix at a time else: vprint('reading data as a list of 2D matrices ...') prog_bar = ptime.progressBar(maxValue=i_end-i_start, print_msg=inps.print_msg) for i in range(i_start, i_end): d = readfile.read(inps.file, datasetName=inps.dset[i], box=inps.pix_box, xstep=inps.multilook_num, ystep=inps.multilook_num, print_msg=False)[0] data[i - i_start, :, :] = d prog_bar.update(i - i_start + 1, suffix=inps.dset[i].split('/')[-1]) prog_bar.close() # ref_date for timeseries if inps.ref_date: vprint('consider input reference date: '+inps.ref_date) ref_data = readfile.read(inps.file, datasetName=inps.ref_date, box=inps.pix_box, xstep=inps.multilook_num, ystep=inps.multilook_num, print_msg=False)[0] data -= ref_data # check if all subplots share the same data unit, they could have/be: # 1) the same type OR # 2) velocity or timeseries OR # 3) horizontal/vertical output from asc_desc2horz_vert.py # 4) data/model output from load_gbis.py OR # 5) binary files with multiple undefined datasets, as band1, band2, etc. if (len(inps.dsetFamilyList) == 1 or inps.key in ['timeseries', 'inversion'] or all(d in inps.dsetFamilyList for d in ['horizontal', 'vertical']) or inps.dsetFamilyList == ['data','model','residual'] or inps.dsetFamilyList == ['band{}'.format(i+1) for i in range(len(inps.dsetFamilyList))]): same_unit4all_subplots = True else: same_unit4all_subplots = False # adjust data due to spatial referencing and unit related scaling if same_unit4all_subplots: data, inps = update_data_with_plot_inps(data, metadata, inps) else: if any(x in inps.iargs for x in ['-u', '--unit']): print('WARNING: -u/--unit option is disabled for multi-subplots with different units! Ignore it and continue') inps.disp_unit = None # mask if inps.zero_mask: vprint('masking pixels with zero value') data = np.ma.masked_where(data == 0., data) if inps.msk is not None: vprint('masking data') msk = np.tile(inps.msk, (data.shape[0], 1, 1)) data = np.ma.masked_where(msk == 0., data) # update display min/max inps.dlim = [np.nanmin(data), np.nanmax(data)] if (same_unit4all_subplots and all(arg not in inps.argv for arg in ['-v', '--vlim', '--wrap']) and not (inps.dsetFamilyList[0].startswith('unwrap') and not inps.file_ref_yx) and inps.dsetFamilyList[0] not in ['bperp']): inps.cmap_lut, inps.vlim = pp.auto_adjust_colormap_lut_and_disp_limit(data, num_multilook=10, print_msg=False) return data def plot_subplot4figure(i, inps, ax, data, metadata): """Plot one subplot for one 3D array 1) Plot DEM, data and reference pixel 2) axes setting: tick, ticklabel, title, axis etc. """ # Plot DEM if inps.dem_file: pp.plot_dem_background(ax=ax, geo_box=None, dem_shade=inps.dem_shade, dem_contour=inps.dem_contour, dem_contour_seq=inps.dem_contour_seq, inps=inps, print_msg=inps.print_msg) # Plot Data vlim = inps.vlim if vlim is None: vlim = [np.nanmin(data), np.nanmax(data)] extent = (inps.pix_box[0]-0.5, inps.pix_box[2]-0.5, inps.pix_box[3]-0.5, inps.pix_box[1]-0.5) im = ax.imshow(data, cmap=inps.colormap, vmin=vlim[0], vmax=vlim[1], interpolation='nearest', alpha=inps.transparency, extent=extent, zorder=1) # Plot Seed Point if inps.disp_ref_pixel: ref_y, ref_x = None, None if inps.ref_yx: ref_y, ref_x = inps.ref_yx[0], inps.ref_yx[1] elif 'REF_Y' in metadata.keys(): ref_y, ref_x = int(metadata['REF_Y']), int(metadata['REF_X']) if ref_y and ref_x: ax.plot(ref_x, ref_y, inps.ref_marker, ms=inps.ref_marker_size) ax.set_xlim(extent[0:2]) ax.set_ylim(extent[2:4]) # Subplot Setting ## Tick and Label if not inps.disp_tick or inps.fig_row_num * inps.fig_col_num > 10: ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) # status bar def format_coord(x, y): return 'x={:.1f}, y={:.1f}, v ='.format(x, y) ax.format_coord = format_coord # Title if inps.disp_title: # get title subplot_title = None if inps.key in timeseriesKeyNames or inps.dset[0].startswith('bperp'): # support / for py2-mintpy date_str = inps.dset[i].replace('/','-').split('-')[1] try: subplot_title = dt.datetime.strptime(date_str, '%Y%m%d').isoformat()[0:10] except: subplot_title = date_str else: # dset info - name & index title_str = inps.dset[i] title_ind = inps.sliceList.index(title_str) # ignore dataset family info if there is only one type if len(inps.dsetFamilyList) == 1 and '-' in title_str: title_str = title_str.split('-')[1] # for ifgramStack, show index in the date12 list to facilitate the network modfication if inps.atr['FILE_TYPE'] == 'ifgramStack': title_ind = inps.date12List.index(title_str) # title style depending on the number of subplots num_subplot = inps.fig_row_num * inps.fig_col_num if num_subplot <= 6: subplot_title = title_str elif 6 < num_subplot <= 20: subplot_title = '{}\n{}'.format(title_ind, title_str) elif 20 < num_subplot <= 50: subplot_title = title_str.replace('_','\n').replace('-','\n') else: subplot_title = '{}'.format(title_ind) # plot title if subplot_title: if inps.fig_title_in: prop = dict(size=inps.font_size) pp.add_inner_title(ax, subplot_title, loc=1, prop=prop) else: kwarg = dict(fontsize=inps.font_size) # mark dropped interferograms in bold crimson if inps.dset[i] in inps.dropDatasetList: kwarg['color'] = 'crimson' kwarg['fontweight'] = 'bold' else: # special titles for Sentinel-1 data if metadata.get('PLATFORM', None) == 'Sen' and inps.disp_title4sentinel1: # display S1A/B in different colors s1_sensor = metadata['SENTINEL1_SENSOR'].split()[i] if s1_sensor == 'A': kwarg['color'] = pp.mplColors[0] else: kwarg['color'] = pp.mplColors[1] # display IPF in subplot title s1_IPF = metadata['SENTINEL1_IPF'].split()[i] subplot_title += ' : {}'.format(s1_IPF) ax.set_title(subplot_title, **kwarg) # Flip Left-Right / Up-Down if inps.flip_lr: ax.invert_xaxis() if inps.flip_ud: ax.invert_yaxis() # Turn off axis if not inps.disp_axis: ax.axis('off') return im def plot_figure(j, inps, metadata): """Plot one figure with multiple subplots 1) create figure 2) read all data into 3D array 3) loop to plot each subplot using plot_subplot4figure() 4) common colorbar and save """ fig_title = 'Figure {} - {}'.format(str(j), inps.outfile[j-1]) vprint('----------------------------------------') vprint(fig_title) # Open a new figure object fig, axs = plt.subplots(num=j, figsize=inps.fig_size, nrows=inps.fig_row_num, ncols=inps.fig_col_num, sharex=True, sharey=True) fig.canvas.manager.set_window_title(fig_title) axs = axs.flatten() # Read all data for the current figure into 3D np.array i_start = (j - 1) * inps.fig_row_num * inps.fig_col_num i_end = min([inps.dsetNum, i_start + inps.fig_row_num * inps.fig_col_num]) data = read_data4figure(i_start, i_end, inps, metadata) if isinstance(inps.colormap, str): inps.colormap = pp.ColormapExt(inps.colormap, cmap_lut=inps.cmap_lut, vlist=inps.cmap_vlist).colormap # Loop - Subplots vprint('plotting ...') prog_bar = ptime.progressBar(maxValue=i_end-i_start, print_msg=inps.print_msg) for i in range(i_start, i_end): idx = i - i_start im = plot_subplot4figure(i, inps, ax=axs[idx], data=data[idx, :, :], metadata=metadata) # colorbar for each subplot if inps.disp_cbar and not inps.vlim: cbar = fig.colorbar(im, ax=axs[idx], pad=0.03, shrink=0.5, aspect=30, orientation='vertical') # display unit as colorbar label data_unit = readfile.read_attribute(inps.file, datasetName=inps.dset[i]).get('UNIT', None) if data_unit: cbar.set_label(data_unit) prog_bar.update(idx+1, suffix=inps.dset[i].split('/')[-1]) prog_bar.close() del data # delete empty axes for i in range(i_end-i_start, len(axs)): fig.delaxes(axs[i]) # Min and Max for this figure inps.dlim_all = [np.nanmin([inps.dlim_all[0], inps.dlim[0]]), np.nanmax([inps.dlim_all[1], inps.dlim[1]])] vprint('data range: {} {}'.format(inps.dlim, inps.disp_unit)) if inps.vlim: vprint('display range: {} {}'.format(inps.vlim, inps.disp_unit)) # NOTE: For plt.subplots(), fig.tight_layout() should be run # before fig.add_axes(), which is the case of common colorbar # after fig.colorbar() and fig.set_size_inches(), which is the case of individual/multiple colorbars def adjust_subplots_layout(fig, inps): fig.subplots_adjust(left=0.02, right=0.98, bottom=0.02, top=0.98, wspace=0.05, hspace=0.05) if inps.fig_wid_space or inps.fig_hei_space: fig.subplots_adjust(hspace=inps.fig_hei_space, wspace=inps.fig_wid_space) elif inps.fig_tight_layout: fig.tight_layout() return # Colorbar if inps.disp_cbar: if not inps.vlim: vprint('Note: different color scale for EACH subplot!') vprint('Adjust figsize for the colorbar of each subplot.') fig.set_size_inches(inps.fig_size[0] * 1.1, inps.fig_size[1]) adjust_subplots_layout(fig, inps) else: adjust_subplots_layout(fig, inps) cbar_length = 0.4 if inps.fig_size[1] > 8.0: cbar_length /= 2 vprint('show colorbar') fig.subplots_adjust(right=0.93) cax = fig.add_axes([0.94, (1.0-cbar_length)/2, 0.005, cbar_length]) inps, cbar = pp.plot_colorbar(inps, im, cax) else: adjust_subplots_layout(fig, inps) # Save Figure if inps.save_fig: vprint('save figure to {} with dpi={}'.format(os.path.abspath(inps.outfile[j-1]), inps.fig_dpi)) fig.savefig(inps.outfile[j-1], bbox_inches='tight', transparent=True, dpi=inps.fig_dpi) if not inps.disp_fig: fig.clf() return def prepare4multi_subplots(inps, metadata): """Prepare for multiple subplots: 1) check multilook to save memory 2) read existed reference pixel info for unwrapPhase 3) read dropIfgram info 4) read and prepare DEM for background """ inps.dsetFamilyList = sorted(list(set(x.split('-')[0] for x in inps.dset))) inps.dsetFamilyList = sorted(list(set(x.replace('Std','') for x in inps.dsetFamilyList))) if len(inps.dsetFamilyList) == 1 and inps.atr['FILE_TYPE'] == 'ifgramStack': inps.date12List = sorted(list(set(x.split('-')[1] for x in inps.sliceList))) if inps.multilook_num > 1 and inps.print_msg: print('multilook {0} by {0} with nearest interpolation'.format(inps.multilook_num)) elif inps.multilook and inps.multilook_num == 1: ## calculate multilook_num # ONLY IF: # inps.multilook is True (no --nomultilook input) AND # inps.multilook_num ==1 (no --multilook-num input) # inps.multilook is used for this check ONLY inps.multilook_num = pp.auto_multilook_num(inps.pix_box, inps.fig_row_num * inps.fig_col_num, max_memory=inps.maxMemory, print_msg=inps.print_msg) # multilook mask if inps.msk is not None and inps.multilook_num > 1: inps.msk = multilook_data(inps.msk, inps.multilook_num, inps.multilook_num, method='nearest') # Reference pixel for timeseries and ifgramStack inps.file_ref_yx = None if inps.key in ['ifgramStack'] and 'REF_Y' in metadata.keys(): ref_y, ref_x = int(metadata['REF_Y']), int(metadata['REF_X']) length, width = int(metadata['LENGTH']), int(metadata['WIDTH']) if 0 <= ref_y < length and 0 <= ref_x < width: inps.file_ref_yx = [ref_y, ref_x] vprint('consider reference pixel in y/x: {}'.format(inps.file_ref_yx)) if inps.dsetNum > 10: inps.ref_marker_size /= 10. elif inps.dsetNum > 100: inps.ref_marker_size /= 20. # Check dropped interferograms inps.dropDatasetList = [] if inps.key == 'ifgramStack' and inps.disp_title: obj = ifgramStack(inps.file) obj.open(print_msg=False) dropDate12List = obj.get_drop_date12_list() for i in inps.dsetFamilyList: inps.dropDatasetList += ['{}-{}'.format(i, j) for j in dropDate12List] vprint("mark interferograms with 'dropIfgram=False' in red colored title") # Read DEM if inps.dem_file: dem_metadata = readfile.read_attribute(inps.dem_file) if all(dem_metadata[i] == metadata[i] for i in ['LENGTH', 'WIDTH']): vprint('reading DEM: {} ... '.format(os.path.basename(inps.dem_file))) dem = readfile.read(inps.dem_file, datasetName='height', box=inps.pix_box, xstep=inps.multilook_num, ystep=inps.multilook_num, print_msg=False)[0] (inps.dem_shade, inps.dem_contour, inps.dem_contour_seq) = pp.prepare_dem_background(dem=dem, inps=inps, print_msg=inps.print_msg) else: inps.dem_file = None inps.transparency = 1.0 msg = 'WARNING: DEM file has a different size from the data file. ' msg += 'This feature is only supported for single subplot, and not for multi-subplots.' msg += '\n --> Ignore it and continue.' print(msg) return inps ################################################################################################## def prep_slice(cmd, auto_fig=False): """Prepare data from command line as input, for easy call plot_slice() externally Parameters: cmd - string, command to be run in terminal Returns: data - 2D np.ndarray, data to be plotted atr - dict, metadata inps - namespace, input argument for plot setup Example: subplot_kw = dict(projection=ccrs.PlateCarree()) fig, ax = plt.subplots(figsize=[4, 3], subplot_kw=subplot_kw) W, N, E, S = (-91.670, -0.255, -91.370, -0.515) # geo_box cmd = 'view.py geo_velocity.h5 velocity --mask geo_maskTempCoh.h5 --dem srtm1.dem --dem-nocontour ' cmd += f'--sub-lon {W} {E} --sub-lat {S} {N} -c jet -v -3 10 ' cmd += '--cbar-loc bottom --cbar-nbins 3 --cbar-ext both --cbar-size 5% ' cmd += '--lalo-step 0.2 --lalo-loc 1 0 1 0 --scalebar 0.3 0.80 0.05 --notitle' data, atr ,inps = view.prep_slice(cmd) ax, inps, im, cbar = view.plot_slice(ax, data, atr, inps) plt.show() """ inps = cmd_line_parse(cmd.split()[1:]) inps.argv = cmd.split()[1:] vprint(cmd) inps, atr = read_input_file_info(inps) inps = update_inps_with_file_metadata(inps, atr) inps.msk, inps.mask_file = pp.read_mask(inps.file, mask_file=inps.mask_file, datasetName=inps.dset[0], box=inps.pix_box, vmin=inps.mask_vmin, vmax=inps.mask_vmax, print_msg=inps.print_msg) # read data data, atr = readfile.read(inps.file, datasetName=inps.dset[0], box=inps.pix_box, print_msg=inps.print_msg) # reference in time if inps.ref_date: data -= readfile.read(inps.file, datasetName=inps.ref_date, box=inps.pix_box, print_msg=False)[0] # reference in space for unwrapPhase if (inps.key in ['ifgramStack'] and inps.dset[0].split('-')[0].startswith('unwrapPhase') and 'REF_Y' in atr.keys()): ref_y, ref_x = int(atr['REF_Y']), int(atr['REF_X']) ref_data = readfile.read(inps.file, datasetName=inps.dset[0], box=(ref_x, ref_y, ref_x+1, ref_y+1), print_msg=False)[0] data[data != 0.] -= ref_data # masking if inps.zero_mask: data = np.ma.masked_where(data == 0., data) if inps.msk is not None: data = np.ma.masked_where(inps.msk == 0., data) else: inps.msk = np.ones(data.shape, dtype=np.bool_) # update/save mask info if np.ma.is_masked(data): inps.msk *= ~data.mask inps.msk *= ~np.isnan(data.data) else: inps.msk *= ~np.isnan(data) data, inps = update_data_with_plot_inps(data, atr, inps) # matplotlib.Axes if auto_fig == True: figsize = [i/2.0 for i in inps.fig_size] subplot_kw = dict(projection=inps.map_proj_obj) if inps.map_proj_obj is not None else {} fig, ax = plt.subplots(figsize=figsize, num='Figure', subplot_kw=subplot_kw) return data, atr, inps, ax else: return data, atr, inps ################################################################################################## class viewer(): """Class for view.py Example: import matplotlib.pyplot as plt from mintpy.view import viewer cmd = 'view.py timeseries.h5' obj = viewer(cmd) obj.configure() obj.plot() """ def __init__(self, cmd=None, iargs=None): if cmd: iargs = cmd.split()[1:] self.cmd = cmd self.iargs =iargs return def configure(self): inps = cmd_line_parse(self.iargs) inps.argv = list(self.iargs) inps, self.atr = read_input_file_info(inps) inps = update_inps_with_file_metadata(inps, self.atr) # --update option self.flag = 'run' if inps.update_mode and not inps.disp_fig and run_or_skip(inps) == 'skip': self.flag = 'skip' # copy inps to self object for key, value in inps.__dict__.items(): setattr(self, key, value) # read mask self.msk, self.mask_file = pp.read_mask(self.file, mask_file=self.mask_file, datasetName=self.dset[0], box=self.pix_box, vmin=self.mask_vmin, vmax=self.mask_vmax, print_msg=self.print_msg) return self.flag def plot(self): # One Subplot if self.dsetNum == 1: vprint('reading data ...') # read data data, self.atr = readfile.read(self.file, datasetName=self.dset[0], box=self.pix_box, print_msg=False) # reference in time if self.ref_date: data -= readfile.read(self.file, datasetName=self.ref_date, box=self.pix_box, print_msg=False)[0] # reference in space for unwrapPhase if (self.key in ['ifgramStack'] and self.dset[0].split('-')[0].startswith('unwrapPhase') and 'REF_Y' in self.atr.keys()): ref_y, ref_x = int(self.atr['REF_Y']), int(self.atr['REF_X']) ref_data = readfile.read(self.file, datasetName=self.dset[0], box=(ref_x, ref_y, ref_x+1, ref_y+1), print_msg=False)[0] data[data != 0.] -= ref_data # masking if self.zero_mask: vprint('masking pixels with zero value') data = np.ma.masked_where(data == 0., data) if self.msk is not None: vprint('masking data') data = np.ma.masked_where(self.msk == 0., data) else: self.msk = np.ones(data.shape, dtype=np.bool_) # update/save mask info if np.ma.is_masked(data): self.msk *= ~data.mask self.msk *= ~np.isnan(data.data) else: self.msk *= ~np.isnan(data) # update data data, self = update_data_with_plot_inps(data, self.atr, self) # prepare figure subplot_kw = dict(projection=self.map_proj_obj) if self.map_proj_obj is not None else {} fig, ax = plt.subplots(figsize=self.fig_size, num='Figure', subplot_kw=subplot_kw) if not self.disp_whitespace: fig.subplots_adjust(left=0,right=1,bottom=0,top=1) # plot self = plot_slice(ax, data, self.atr, self)[1] # Save figure if self.save_fig: a = self.outfile[0] vprint('save figure to {} with dpi={}'.format(os.path.abspath(self.outfile[0]), self.fig_dpi)) if not self.disp_whitespace: fig.savefig(self.outfile[0], transparent=True, dpi=self.fig_dpi, pad_inches=0.0) else: fig.savefig(self.outfile[0], transparent=True, dpi=self.fig_dpi, bbox_inches='tight') if not self.disp_fig: fig.clf() # Multiple Subplots else: # warn single-subplot options opt_names = ['--show-gps', '--coastline', '--lalo-label', '--lalo-step', '--scalebar', '--pts-yx', '--pts-lalo', '--pts-file'] opt_names = list(set(opt_names) & set(self.iargs)) for opt_name in opt_names: print('WARNING: {} is NOT supported for multi-subplots, ignore it and continue.'.format(opt_name)) # prepare self = prepare4multi_subplots(self, metadata=self.atr) # plot self.dlim_all = [0., 0.] for j in range(1, self.fig_num + 1): plot_figure(j, self, metadata=self.atr) # stat if self.fig_num > 1: vprint('----------------------------------------') vprint('all data range: {} {}'.format(self.dlim_all, self.disp_unit)) if self.vlim: vprint('display range: {} {}'.format(self.vlim, self.disp_unit)) # Display Figure if self.disp_fig: vprint('showing ...') plt.show() return ######################################### Main Function ######################################## def view_main(iargs=None): obj = viewer(iargs=iargs) obj.configure() if obj.flag == 'run': obj.plot() return ################################################################################################## if __name__ == '__main__': # sys.argv.extend(['I:\MicroWorkspace\C-SAR\Deformation\Input\geo\geo_timeseries_ERA5_ramp_demErr.h5','--nodisplay']) # sys.argv.extend(['geo_velocity.h5', 'velocity', '--save','--nodisplay']) view_main(sys.argv[1:]) EXAMPLE = """example: view.py velocity.h5 view.py velocity.h5 velocity --wrap --wrap-range -2 2 -c cmy --lalo-label view.py velocity.h5 --ref-yx 210 566 #change reference pixel for display view.py velocity.h5 --sub-lat 31.05 31.10 --sub-lon 130.05 130.10 #subset in lalo / yx view.py timeseries.h5 view.py timeseries.h5 -m no #do not use auto mask view.py timeseries.h5 --ref-date 20101120 #change reference date view.py timeseries.h5 --ex drop_date.txt #exclude dates to plot view.py timeseries.h5 '*2017*' '*2018*' #all acquisitions in 2017 and 2018 view.py timeseries.h5 20200616_20200908 #reconstruct interferogram on the fly view.py ifgramStack.h5 coherence view.py ifgramStack.h5 unwrapPhase- #unwrapPhase only in the presence of unwrapPhase_bridging view.py ifgramStack.h5 -n 6 #the 6th slice view.py ifgramStack.h5 20171010_20171115 #all data related with 20171010_20171115 view.py ifgramStack.h5 'coherence*20171010*' #all coherence related with 20171010 view.py ifgramStack.h5 unwrapPhase-20070927_20100217 --zero-mask --wrap #wrapped phase view.py ifgramStack.h5 unwrapPhase-20070927_20100217 --mask ifgramStack.h5 #mask using connected components # GPS (for one subplot in geo-coordinates only) view.py geo_velocity_msk.h5 velocity --show-gps --gps-label #show locations of available GPS view.py geo_velocity_msk.h5 velocity --show-gps --gps-comp enu2los --ref-gps GV01 view.py geo_timeseries_ERA5_ramp_demErr.h5 20180619 --ref-date 20141213 --show-gps --gps-comp enu2los --ref-gps GV01 # Save and Output view.py velocity.h5 --save view.py velocity.h5 --nodisplay view.py geo_velocity.h5 velocity --nowhitespace """