def plot_2dhist(ax, x, y, xlim=None, ylim=None, xint=None, yint=None, nbins=(128,128), log=False, maxline=True, trendline=False): from pygeotools.lib import malib #Should compute number of bins automatically based on input values, xlim and ylim common_mask = ~(malib.common_mask([x,y])) x = x[common_mask] y = y[common_mask] if xlim is None: #xlim = (x.min(), x.max()) xlim = malib.calcperc(x, (0.1, 99.9)) if ylim is None: #ylim = (y.min(), y.max()) ylim = malib.calcperc(y, (0.1, 99.9)) #Note, round to nearest meter here #xlim = np.rint(np.array(xlim)) xlim = np.array(xlim) ylim = np.array(ylim) if xint is not None: xedges = np.arange(xlim[0], xlim[1]+xint, xint) else: xedges = nbins[0] if yint is not None: yedges = np.arange(ylim[0], ylim[1]+yint, yint) else: yedges = nbins[1] H, xedges, yedges = np.histogram2d(x,y,range=[xlim,ylim],bins=[xedges, yedges]) #H, xedges, yedges = np.histogram2d(x,y,range=[xlim,ylim],bins=nbins) #H = np.rot90(H) #H = np.flipud(H) H = H.T #Mask any empty bins Hmasked = np.ma.masked_where(H==0,H) #Hmasked = H H_clim = malib.calcperc(Hmasked, (2,98)) if log: import matplotlib.colors as colors ax.pcolormesh(xedges,yedges,Hmasked,cmap='inferno',norm=colors.LogNorm(vmin=H_clim[0],vmax=H_clim[1])) else: ax.pcolormesh(xedges,yedges,Hmasked,cmap='inferno',vmin=H_clim[0],vmax=H_clim[1]) if maxline: #Add line for max values in each x bin Hmed_idx = np.ma.argmax(Hmasked, axis=0) ymax = (yedges[:-1]+np.diff(yedges))[Hmed_idx] ax.plot(xedges[:-1]+np.diff(xedges), ymax, color='dodgerblue',lw=1.0) if trendline: #Add trendline import scipy.stats y_slope, y_intercept, r_value, p_value, std_err = scipy.stats.linregress(x, y) y_f = y_slope * xlim + y_intercept ax.plot(xlim, y_f, color='limegreen', ls='--', lw=0.5)
def dz_fltr_ma(dem, refdem, perc=None, abs_dz_lim=(0,30), smooth=True): """Absolute elevation difference range filter using values from a source array and a reference array """ if smooth: refdem = gauss_fltr_astropy(refdem) dem = gauss_fltr_astropy(dem) dz = refdem - dem #This is True for invalid values in DEM, and should be masked demmask = np.ma.getmaskarray(dem) if perc: dz_perc = malib.calcperc(dz, perc) print("Applying dz percentile filter (%s%%, %s%%): (%0.1f, %0.1f)" % (perc[0], perc[1], dz_perc[0], dz_perc[1])) #This is True for invalid values perc_mask = ((dz < dz_perc[0]) | (dz > dz_perc[1])).filled(False) demmask = (demmask | perc_mask) if abs_dz_lim: #This is True for invalid values abs_dz_mask = ((np.abs(dz) < abs_dz_lim[0]) | (np.abs(dz) > abs_dz_lim[1])).filled(False) if True: cutoff = 150 abs_dz_lim = (0, 80) low = (refdem < cutoff).data abs_dz_mask[low] = ((np.abs(dz) < abs_dz_lim[0]) | (np.abs(dz) > abs_dz_lim[1])).filled(False)[low] demmask = (demmask | abs_dz_mask) out = np.ma.array(dem, mask=demmask, fill_value=dem.fill_value) return out
def perc_fltr(dem, perc=(1.0, 99.0)): """Percentile filter """ rangelim = malib.calcperc(dem, perc) print('Excluding values outside of percentile ({0:0.2f}, {1:0.2f}) range: {2:0.1f} to {3:0.1f} m'.format(*(perc + rangelim))) out = range_fltr(dem, rangelim) return out
def plot_2dhist(ax, x, y, xlim, ylim, log=False): bins = (100, 100) common_mask = ~(malib.common_mask([x, y])) x = x[common_mask] y = y[common_mask] H, xedges, yedges = np.histogram2d(x, y, range=[xlim, ylim], bins=bins) H = np.rot90(H) H = np.flipud(H) Hmasked = np.ma.masked_where(H == 0, H) Hmed_idx = np.ma.argmax(Hmasked, axis=0) ymax = (yedges[:-1] + np.diff(yedges))[Hmed_idx] #Hmasked = H H_clim = malib.calcperc(Hmasked, (2, 98)) if log: import matplotlib.colors as colors ax.pcolormesh(xedges, yedges, Hmasked, cmap='inferno', norm=colors.LogNorm(vmin=H_clim[0], vmax=H_clim[1])) else: ax.pcolormesh(xedges, yedges, Hmasked, cmap='inferno', vmin=H_clim[0], vmax=H_clim[1]) ax.plot(xedges[:-1] + np.diff(xedges), ymax, color='dodgerblue', lw=1.0)
def perc_fltr(dem, perc=(1.0, 99.0)): """Percentile filter """ rangelim = malib.calcperc(dem, perc) print('Excluding values outside of percentile range: {0:0.2f} to {1:0.2f}'.format(*perc)) out = range_fltr(dem, rangelim) return out
def get_bins(dem, bin_width=100.0): #Define min and max elevation minz, maxz = list(malib.calcperc(dem, perc=(0.01, 99.99))) minz = np.floor(minz / bin_width) * bin_width maxz = np.ceil(maxz / bin_width) * bin_width #Compute bin edges and centers bin_edges = np.arange(minz, maxz + bin_width, bin_width) bin_centers = bin_edges[:-1] + np.diff(bin_edges) / 2.0 return bin_edges, bin_centers
def get_clim(a, clim_perc=(2, 98)): """ Computer percentile stretch for input array """ #print("Colorbar limits (%0.1f-%0.1f%%)" % clim_perc) clim = malib.calcperc(a, clim_perc) if clim[0] == clim[1]: if clim[0] > a.fill_value: clim = (a.fill_value, clim[0]) else: clim = (clim[0], a.fill_value) return clim
def compute_offset_sad(dem1, dem2, pad=(9,9), plot=False): """Compute subpixel horizontal offset between input rasters using sum of absolute differences (SAD) method """ #This defines the search window size #Use half-pixel stride? #Note: stride is not properly implemented #stride = 1 #ref = dem1[::stride,::stride] #kernel = dem2[pad[0]:-pad[0]:stride, pad[1]:-pad[1]:stride] kernel = dem2[pad[0]:-pad[0], pad[1]:-pad[1]] #Want to pad evenly on both sides, so add +1 here m = np.zeros((pad[0]*2+1, pad[1]*2+1)) #Find integer pixel offset i = j = 0 for i in range(m.shape[0]): print(i) for j in range(m.shape[1]): print(j) ref = dem1[i:i+kernel.shape[0], j:j+kernel.shape[1]] diff = ref - kernel #Remove outliers beyond IQR diff_iqr = malib.calcperc(diff, (25,75)) diff = np.ma.masked_outside(diff, *diff_iqr) """ diff_med = np.ma.median(diff) diff_mad = malib.mad(diff) diff_madr = (diff_med - mad, diff_med + mad) diff = np.ma.masked_outside(diff, diff_madr) """ #Masked areas will decrease sum! Normalize by count of valid pixels m[i,j] = np.ma.abs(diff).sum()/diff.count() #Note, we're dealing with min SAD here, so want to provide -m for sub-pixel refinement m = -m int_argmax = np.array(np.unravel_index(m.argmax(), m.shape)) int_offset = int_argmax - pad sp_argmax = np.array(find_subpixel_peak_position(m, 'parabolic')) sp_offset = sp_argmax - pad if plot: plt.figure() plt.title('Sum of Absolute Differences') plt.imshow(m) plt.scatter(*sp_argmax[::-1]) #plt.show() return m, int_offset, sp_offset
def cummulative_profile(xma, yma, xbin_width, limit_x_perc=(1, 99)): """ compute binned statistics for independent variable with respect to dependendent variable Parameters ----------- xma: masked array independent variable (like slope) yma: masked array dependent variable (like elevation difference) xbin_width: int bin_width for independent variable limit_x_perc: tuple limit binning of independent variable to the given percentile (default: 1 to 99 %) Returns ----------- x_bins: np.array bin locations y_mean: np.array binned mean value for dependent variable y_meadian: np.array binned median value for dependent variable y_std: np.array binned standard deviation value for dependent varuiable y_perc: np.array binned percentage of variables within the bin """ # xclim get rids of outliers in the independent variable # we only look at the 1 to 99 percentile values by default xclim = malib.calcperc(xma, limit_x_perc) # this step computes common mask where pixels of both x and y variables are valid xma_lim = np.ma.masked_outside(xma, xclim[0], xclim[1]) cmask = malib.common_mask([xma_lim, yma]) # the common mask is used to flatten the required points in a 1-D array xma_c = np.ma.compressed(np.ma.array(xma_lim, mask=cmask)) yma_c = np.ma.compressed(np.ma.array(yma, mask=cmask)) # we then use pandas groupby to quickly compute binned statistics df = pd.DataFrame({'x': xma_c, 'y': yma_c}) df['x_rounded'] = (df['x'] + (xbin_width - 1)) // (xbin_width) * xbin_width grouped = df.groupby('x_rounded') df2 = grouped['y'].agg([np.mean, np.count_nonzero, np.median, np.std]) df2.reset_index(inplace=True) # variables are returned as numpy array x_bins = df2['x_rounded'].values y_mean = df2['mean'].values y_median = df2['median'].values y_std = df2['std'].values y_perc = (df2['count_nonzero'].values / np.sum(df2['count_nonzero'].values)) * 100 return x_bins, y_mean, y_median, y_std, y_perc
def aed(dem, res=None, bin_width=100.0): #Define min and max elevation minz, maxz= list(malib.calcperc(dem, perc=(0.01, 99.99))) minz = np.floor(minz/bin_width) * bin_width maxz = np.ceil(maxz/bin_width) * bin_width #Compute bin edges and centers bin_edges = np.arange(minz, maxz + bin_width, bin_width) bin_centers = bin_edges[:-1] + np.diff(bin_edges)/2.0 #Compress masked array to get only valid elevations demc = dem.compressed() #Compute histogram bin_counts, bin_edges = np.histogram(demc, bins=bin_edges) #Convert count to area bin_areas = bin_counts * res * res / 1E6 return bin_centers, bin_areas
def make_plot(m, fig_fn, label): f, ax = plt.subplots(figsize=(7, 7)) #plt.title('%s to %s' % (t1.strftime('%Y-%m-%d'), t2.strftime('%Y-%m-%d'))) perc = malib.calcperc(m, (2, 98)) cmap = 'inferno' imgplot = ax.imshow(m, cmap=cmap) imgplot.set_clim(*perc) imgplot.axes.get_xaxis().set_visible(False) imgplot.axes.get_yaxis().set_visible(False) imgplot.axes.patch.set_facecolor('0.5') cb = plt.colorbar(imgplot, orientation='vertical', extend='both', shrink=0.5) cb.set_label(label) return f, ax
def ndanimate(a): import matplotlib.animation as animation a = malib.checkma(a) #Compute constant scale clim = malib.calcperc(a) label = 'Elev. Diff. (m)' fig = plt.figure() ims = [] for i in a: #cmap = 'gist_rainbow_r' cmap = 'cpt_rainbow' im = plt.imshow(i, cmap=cmap, clim=clim) im.axes.patch.set_facecolor('black') #cbar = fig.colorbar(im, extend='both', shrink=0.5) #cbar.set_label(label) ims.append([im]) an = animation.ArtistAnimation(fig, ims, interval=100, blit=True) plt.show() return an
def map_plot(site_list, ds): a = iolib.ds_getma(ds) clim = malib.calcperc(a, (2, 98)) mX = site_list[:, 1] mY = site_list[:, 2] pX, pY = geolib.mapToPixel(mX, mY, ds.GetGeoTransform()) #f, ax = plt.subplots(1, figsize=(6,6), subplot_kw={'aspect':'equal', 'adjustable':'box-forced'}) f, ax = plt.subplots(1, figsize=(6, 6), subplot_kw={'aspect': 'equal'}) im = ax.imshow(a, vmin=clim[0], vmax=clim[1], cmap='inferno') ax.set_facecolor('0.5') from imview.lib import pltlib pltlib.add_scalebar(ax, geolib.get_res(ds)[0]) ax.scatter(pX, pY, s=16, facecolors='w', edgecolors='k') for i, lbl in enumerate(site_list[:, 0]): bbox = dict(boxstyle='round,pad=0.1', fc='k', alpha=0.7) ax.annotate(str(int(lbl)), xy=(pX[i], pY[i]), xytext=(0, 4), textcoords='offset points', fontsize=8, color='w', bbox=bbox) return f
def map_plot(gf, z_bin_edges, outdir, hs=True): #print("Generating map plot") f, axa = plt.subplots(1, 3, figsize=(10, 7.5)) #f.suptitle(gf.feat_fn) alpha = 1.0 if hs: #z1_hs = geolib.gdaldem_wrapper(gf.out_z1_fn, product='hs', returnma=True, verbose=False) #z2_hs = geolib.gdaldem_wrapper(gf.out_z2_fn, product='hs', returnma=True, verbose=False) z1_hs = gf.z1_hs z2_hs = gf.z2_hs hs_clim = malib.calcperc(z2_hs, (2, 98)) z1_hs_im = axa[0].imshow(z1_hs, cmap='gray', vmin=hs_clim[0], vmax=hs_clim[1]) z2_hs_im = axa[1].imshow(z2_hs, cmap='gray', vmin=hs_clim[0], vmax=hs_clim[1]) alpha = 0.5 z1_im = axa[0].imshow(gf.z1, cmap='cpt_rainbow', vmin=z_bin_edges[0], vmax=z_bin_edges[-1], alpha=alpha) z2_im = axa[1].imshow(gf.z2, cmap='cpt_rainbow', vmin=z_bin_edges[0], vmax=z_bin_edges[-1], alpha=alpha) axa[0].contour(gf.z1, [ gf.z1_ela, ], linewidths=0.5, linestyles=':', colors='w') axa[1].contour(gf.z2, [ gf.z2_ela, ], linewidths=0.5, linestyles=':', colors='w') #t1_title = int(np.round(gf.t1)) #t2_title = int(np.round(gf.t2)) t1_title = '%0.2f' % gf.t1 t2_title = '%0.2f' % gf.t2 #t1_title = gf.t1.strftime('%Y-%m-%d') #t2_title = gf.t2.strftime('%Y-%m-%d') axa[0].set_title(t1_title) axa[1].set_title(t2_title) axa[2].set_title('%s to %s (%0.2f yr)' % (t1_title, t2_title, gf.dt)) #dz_clim = (-10, 10) dz_clim = (-2.0, 2.0) dz_im = axa[2].imshow(gf.dhdt, cmap='RdBu', vmin=dz_clim[0], vmax=dz_clim[1]) for ax in axa: pltlib.hide_ticks(ax) ax.set_facecolor('k') sb_loc = pltlib.best_scalebar_location(gf.z1) pltlib.add_scalebar(axa[0], gf.res[0], location=sb_loc) pltlib.add_cbar(axa[0], z1_im, label='Elevation (m WGS84)') pltlib.add_cbar(axa[1], z2_im, label='Elevation (m WGS84)') pltlib.add_cbar(axa[2], dz_im, label='dh/dt (m/yr)') plt.tight_layout() #Make room for suptitle #plt.subplots_adjust(top=0.90) #print("Saving map plot") fig_fn = os.path.join(outdir, gf.feat_fn + '_mb_map.png') plt.savefig(fig_fn, bbox_inches='tight', dpi=300) plt.close(f)
def mb_calc(gf, z1_date=z1_date, z2_date=z2_date, verbose=verbose): #print("\n%i of %i: %s\n" % (n+1, len(glacfeat_list), gf.feat_fn)) print(gf.feat_fn) #This should already be handled by earlier attribute filter, but RGI area could be wrong #24k shp has area in m^2, RGI in km^2 #if gf.glac_area/1E6 < min_glac_area: if gf.glac_area < min_glac_area: if verbose: print("Glacier area below %0.1f km2 threshold" % min_glac_area) return None #Warp everything to common res/extent/proj ds_list = warplib.memwarp_multi_fn([z1_fn, z2_fn], res='min', \ extent=gf.glac_geom_extent, t_srs=aea_srs, verbose=verbose) if site == 'conus': #Add prism datasets prism_fn_list = [prism_ppt_annual_fn, prism_tmean_annual_fn] prism_fn_list.extend([ prism_ppt_summer_fn, prism_ppt_winter_fn, prism_tmean_summer_fn, prism_tmean_winter_fn ]) ds_list.extend(warplib.memwarp_multi_fn(prism_fn_list, res=ds_list[0], \ extent=gf.glac_geom_extent, t_srs=aea_srs, verbose=verbose)) if site == 'hma': #Add debris cover datasets #Should tar this up, and extract only necessary file #Downloaded from: http://mountainhydrology.org/data-nature-2017/ kra_nature_dir = '/nobackup/deshean/data/Kraaijenbrink_hma/regions/out' #This assumes that numbers are identical between RGI50 and RGI60 debris_class_fn = os.path.join( kra_nature_dir, 'RGI50-%s/classification.tif' % gf.glacnum) debris_thick_fn = os.path.join( kra_nature_dir, 'RGI50-%s/debris-thickness-50cm.tif' % gf.glacnum) ice_thick_fn = os.path.join(kra_nature_dir, 'RGI50-%s/ice-thickness.tif' % gf.glacnum) hma_fn_list = [] if os.path.exists(debris_class_fn): hma_fn_list.append(debris_class_fn) if os.path.exists(debris_thick_fn): hma_fn_list.append(debris_thick_fn) if os.path.exists(ice_thick_fn): hma_fn_list.append(ice_thick_fn) if len(hma_fn_list) > 0: #Add velocity hma_fn_list.extend([vx_fn, vy_fn]) ds_list.extend(warplib.memwarp_multi_fn(hma_fn_list, res=ds_list[0], \ extent=gf.glac_geom_extent, t_srs=aea_srs, verbose=verbose)) #Check to see if z2 is empty, as z1 should be continuous gf.z2 = iolib.ds_getma(ds_list[1]) if gf.z2.count() == 0: if verbose: print("No z2 pixels") return None glac_geom_mask = geolib.geom2mask(gf.glac_geom, ds_list[0]) gf.z1 = np.ma.array(iolib.ds_getma(ds_list[0]), mask=glac_geom_mask) #Apply SRTM penetration correction if z1_srtm_penetration_corr: gf.z1 = srtm_corr(gf.z1) if z2_srtm_penetration_corr: gf.z2 = srtm_corr(gf.z2) gf.z2 = np.ma.array(gf.z2, mask=glac_geom_mask) gf.dz = gf.z2 - gf.z1 if gf.dz.count() == 0: if verbose: print("No valid dz pixels") return None #Should add better filtering here #Elevation dependent abs. threshold filter? filter_outliers = True #Remove clearly bogus pixels if filter_outliers: bad_perc = (0.1, 99.9) #bad_perc = (1, 99) rangelim = malib.calcperc(gf.dz, bad_perc) gf.dz = np.ma.masked_outside(gf.dz, *rangelim) gf.res = geolib.get_res(ds_list[0]) valid_area = gf.dz.count() * gf.res[0] * gf.res[1] valid_area_perc = valid_area / gf.glac_area if valid_area_perc < min_valid_area_perc: if verbose: print( "Not enough valid pixels. %0.1f%% percent of glacier polygon area" % (100 * valid_area_perc)) return None #Filter dz - throw out abs differences >150 m #Compute dz, volume change, mass balance and stats gf.z1_stats = malib.get_stats(gf.z1) gf.z2_stats = malib.get_stats(gf.z2) z2_elev_med = gf.z2_stats[5] z2_elev_p16 = gf.z2_stats[11] z2_elev_p84 = gf.z2_stats[12] #Caluclate stats for aspect and slope using z2 #Requires GDAL 2.1+ gf.z2_aspect = np.ma.array(geolib.gdaldem_mem_ds(ds_list[1], processing='aspect', returnma=True), mask=glac_geom_mask) gf.z2_aspect_stats = malib.get_stats(gf.z2_aspect) z2_aspect_med = gf.z2_aspect_stats[5] gf.z2_slope = np.ma.array(geolib.gdaldem_mem_ds(ds_list[1], processing='slope', returnma=True), mask=glac_geom_mask) gf.z2_slope_stats = malib.get_stats(gf.z2_slope) z2_slope_med = gf.z2_slope_stats[5] #Rasterize source dates if z1_date is None: z1_date = get_date_a(ds_list[0], z1_date_shp_lyr, glac_geom_mask, z1_datefield) gf.t1 = z1_date.mean() else: gf.t1 = z1_date if z2_date is None: z2_date = get_date_a(ds_list[0], z2_date_shp_lyr, glac_geom_mask, z2_datefield) #Attempt to use YYYYMMDD string #z2_dta = np.datetime64(z2_date.astype("S8").tolist()) gf.t2 = z2_date.mean() else: gf.t2 = z2_date if isinstance(gf.t1, datetime): gf.t1 = timelib.dt2decyear(gf.t1) if isinstance(gf.t2, datetime): gf.t2 = timelib.dt2decyear(gf.t2) gf.t1 = float(gf.t1) gf.t2 = float(gf.t2) #Calculate dt grids #gf.dt = z2_date - z1_date #gf.dt = gf.dt.mean() #This should be decimal years gf.dt = gf.t2 - gf.t1 #if isinstance(gf.dt, timedelta): # gf.dt = gf.dt.total_seconds()/timelib.spy #Calculate dh/dt, in m/yr gf.dhdt = gf.dz / gf.dt gf.dhdt_stats = malib.get_stats(gf.dhdt) dhdt_mean = gf.dhdt_stats[3] dhdt_med = gf.dhdt_stats[5] rho_i = 0.91 rho_s = 0.50 rho_f = 0.60 #This is recommendation by Huss et al (2013) rho_is = 0.85 rho_sigma = 0.06 #Can estimate ELA values computed from hypsometry and typical AAR #For now, assume ELA is mean gf.z1_ela = None gf.z1_ela = gf.z1_stats[3] gf.z2_ela = gf.z2_stats[3] #Note: in theory, the ELA should get higher with mass loss #In practice, using mean and same polygon, ELA gets lower as glacier surface thins if verbose: print("ELA(t1): %0.1f" % gf.z1_ela) print("ELA(t2): %0.1f" % gf.z2_ela) if gf.z1_ela > gf.z2_ela: min_ela = gf.z2_ela max_ela = gf.z1_ela else: min_ela = gf.z1_ela max_ela = gf.z2_ela #Calculate mass balance map from dhdt gf.mb = gf.dhdt * rho_is """ # This attempted to assign different densities above and below ELA if gf.z1_ela is None: gf.mb = gf.dhdt * rho_is else: #Initiate with average density gf.mb = gf.dhdt*(rho_is + rho_f)/2. #Everything that is above ELA at t2 is elevation change over firn, use firn density accum_mask = (gf.z2 > gf.z2_ela).filled(0).astype(bool) gf.mb[accum_mask] = (gf.dhdt*rho_f)[accum_mask] #Everything that is below ELA at t1 is elevation change over ice, use ice density abl_mask = (gf.z1 <= gf.z1_ela).filled(0).astype(bool) gf.mb[abl_mask] = (gf.dhdt*rho_is)[abl_mask] #Everything in between, use average of ice and firn density #mb[(z1 > z1_ela) || (z2 <= z2_ela)] = dhdt*(rhois + rho_f)/2. #Linear ramp #rho_f + z2*((rho_is - rho_f)/(z2_ela - z1_ela)) #mb = np.where(dhdt < ela, dhdt*rho_i, dhdt*rho_s) """ #Use this for winter balance #mb = dhdt * rho_s gf.mb_stats = malib.get_stats(gf.mb) gf.mb_mean = gf.mb_stats[3] #Calculate uncertainty of total elevation change #TODO: Better spatial distribution characterization #Add slope-dependent component here dz_sigma = np.sqrt(z1_sigma**2 + z2_sigma**2) #Uncrtainty of dh/dt dhdt_sigma = dz_sigma / gf.dt #This is mb uncertainty map gf.mb_sigma = np.ma.abs(gf.mb) * np.sqrt((rho_sigma / rho_is)**2 + (dhdt_sigma / gf.dhdt)**2) gf.mb_sigma_stats = malib.get_stats(gf.mb_sigma) #This is average mb uncertainty gf.mb_mean_sigma = gf.mb_sigma_stats[3] #Now calculate mb for entire polygon area_sigma_perc = 0.09 gf.mb_mean_totalarea = gf.mb_mean * gf.glac_area #Already have area uncertainty as percentage, just use directly gf.mb_mean_totalarea_sigma = np.ma.abs(gf.mb_mean_totalarea) * np.sqrt( (gf.mb_mean_sigma / gf.mb_mean)**2 + area_sigma_perc**2) mb_sum = np.sum(gf.mb) * gf.res[0] * gf.res[1] outlist = [gf.glacnum, gf.cx, gf.cy, z2_elev_med, z2_elev_p16, z2_elev_p84, z2_slope_med, z2_aspect_med, \ gf.mb_mean, gf.mb_mean_sigma, gf.glac_area, gf.mb_mean_totalarea, gf.mb_mean_totalarea_sigma, \ gf.t1, gf.t2, gf.dt] if site == 'conus': prism_ppt_annual = np.ma.array(iolib.ds_getma(ds_list[2]), mask=glac_geom_mask) / 1000. prism_ppt_annual_stats = malib.get_stats(prism_ppt_annual) prism_ppt_annual_mean = prism_ppt_annual_stats[3] prism_tmean_annual = np.ma.array(iolib.ds_getma(ds_list[3]), mask=glac_geom_mask) prism_tmean_annual_stats = malib.get_stats(prism_tmean_annual) prism_tmean_annual_mean = prism_tmean_annual_stats[3] outlist.extend([prism_ppt_annual_mean, prism_tmean_annual_mean]) #This is mean monthly summer precip, need to multiply by nmonths to get cumulative n_summer = 4 prism_ppt_summer = n_summer * np.ma.array(iolib.ds_getma(ds_list[4]), mask=glac_geom_mask) / 1000. prism_ppt_summer_stats = malib.get_stats(prism_ppt_summer) prism_ppt_summer_mean = prism_ppt_summer_stats[3] n_winter = 8 prism_ppt_winter = n_winter * np.ma.array(iolib.ds_getma(ds_list[5]), mask=glac_geom_mask) / 1000. prism_ppt_winter_stats = malib.get_stats(prism_ppt_winter) prism_ppt_winter_mean = prism_ppt_winter_stats[3] prism_tmean_summer = np.ma.array(iolib.ds_getma(ds_list[6]), mask=glac_geom_mask) prism_tmean_summer_stats = malib.get_stats(prism_tmean_summer) prism_tmean_summer_mean = prism_tmean_summer_stats[3] prism_tmean_winter = np.ma.array(iolib.ds_getma(ds_list[7]), mask=glac_geom_mask) prism_tmean_winter_stats = malib.get_stats(prism_tmean_winter) prism_tmean_winter_mean = prism_tmean_winter_stats[3] outlist.extend([ prism_ppt_summer_mean, prism_ppt_winter_mean, prism_tmean_summer_mean, prism_tmean_winter_mean ]) if site == 'hma': #Classes are: 1 = clean ice, 2 = debris, 3 = pond #Load up debris cover maps, ice thickness if len(ds_list) > 2: gf.debris_class = np.ma.array(iolib.ds_getma(ds_list[2]), mask=glac_geom_mask) gf.debris_thick = np.ma.array(iolib.ds_getma(ds_list[3]), mask=glac_geom_mask) #Load ice thickness from glabtop2 gf.H = np.ma.array(iolib.ds_getma(ds_list[4]), mask=glac_geom_mask) #Load surface velocity maps from Dehecq gf.vx = np.ma.array(iolib.ds_getma(ds_list[5]), mask=glac_geom_mask) gf.vy = np.ma.array(iolib.ds_getma(ds_list[6]), mask=glac_geom_mask) gf.vm = np.ma.sqrt(gf.vx**2 + gf.vy**2) v_col_factor = 0.8 #Should smooth, better handling of data gaps gf.divU = np.gradient(v_col_factor * gf.vx)[1] + np.gradient( v_col_factor * gf.vy)[0] gf.divQ = gf.H * gf.divU #Compute debris/pond/clean percentages for entire polygon if gf.debris_class.count() > 0: gf.perc_clean = 100. * (gf.debris_class == 1).sum() / gf.debris_class.count() gf.perc_debris = 100. * (gf.debris_class == 2).sum() / gf.debris_class.count() gf.perc_pond = 100. * (gf.debris_class == 3).sum() / gf.debris_class.count() outlist.extend([ gf.H.mean(), gf.debris_thick.mean(), gf.perc_debris, gf.perc_pond, gf.perc_clean ]) if verbose: print('Mean mb: %0.2f +/- %0.2f mwe/yr' % (gf.mb_mean, gf.mb_mean_sigma)) print('Sum/Area mb: %0.2f mwe/yr' % (mb_sum / gf.glac_area)) print('Mean mb * Area: %0.2f +/- %0.2f mwe/yr' % (gf.mb_mean_totalarea, gf.mb_mean_totalarea_sigma)) print('Sum mb: %0.2f mwe/yr' % mb_sum) #print('-------------------------------') #Write to master list #out.append(outlist) #Write to temporary file #writer.writerow(outlist) #f.flush() if writeout and (gf.glac_area / 1E6 > min_glac_area_writeout): out_dz_fn = os.path.join(outdir, gf.feat_fn + '_dz.tif') iolib.writeGTiff(gf.dz, out_dz_fn, ds_list[0]) out_z1_fn = os.path.join(outdir, gf.feat_fn + '_z1.tif') iolib.writeGTiff(gf.z1, out_z1_fn, ds_list[0]) out_z2_fn = os.path.join(outdir, gf.feat_fn + '_z2.tif') iolib.writeGTiff(gf.z2, out_z2_fn, ds_list[0]) temp_fn = os.path.join(outdir, gf.feat_fn + '_z2_aspect.tif') iolib.writeGTiff(gf.z2_aspect, temp_fn, ds_list[0]) temp_fn = os.path.join(outdir, gf.feat_fn + '_z2_slope.tif') iolib.writeGTiff(gf.z2_slope, temp_fn, ds_list[0]) #Need to fix this - write out constant date arrays regardless of source #out_z1_date_fn = os.path.join(outdir, gf.feat_fn+'_ned_date.tif') #iolib.writeGTiff(z1_date, out_z1_date_fn, ds_list[0]) if site == 'conus': out_prism_ppt_annual_fn = os.path.join( outdir, gf.feat_fn + '_precip_annual.tif') iolib.writeGTiff(prism_ppt_annual, out_prism_ppt_annual_fn, ds_list[0]) out_prism_tmean_annual_fn = os.path.join( outdir, gf.feat_fn + '_tmean_annual.tif') iolib.writeGTiff(prism_tmean_annual, out_prism_tmean_annual_fn, ds_list[0]) out_prism_ppt_summer_fn = os.path.join( outdir, gf.feat_fn + '_precip_summer.tif') iolib.writeGTiff(prism_ppt_summer, out_prism_ppt_summer_fn, ds_list[0]) out_prism_ppt_winter_fn = os.path.join( outdir, gf.feat_fn + '_precip_winter.tif') iolib.writeGTiff(prism_ppt_winter, out_prism_ppt_winter_fn, ds_list[0]) out_prism_tmean_summer_fn = os.path.join( outdir, gf.feat_fn + '_tmean_summer.tif') iolib.writeGTiff(prism_tmean_summer, out_prism_tmean_summer_fn, ds_list[0]) out_prism_tmean_winter_fn = os.path.join( outdir, gf.feat_fn + '_tmean_winter.tif') iolib.writeGTiff(prism_tmean_winter, out_prism_tmean_winter_fn, ds_list[0]) if site == 'hma': if gf.H is not None: temp_fn = os.path.join(outdir, gf.feat_fn + '_H.tif') iolib.writeGTiff(gf.H, temp_fn, ds_list[0]) if gf.debris_thick is not None: temp_fn = os.path.join(outdir, gf.feat_fn + '_debris_thick.tif') iolib.writeGTiff(gf.debris_thick, temp_fn, ds_list[0]) if gf.debris_class is not None: temp_fn = os.path.join(outdir, gf.feat_fn + '_debris_class.tif') iolib.writeGTiff(gf.debris_class, temp_fn, ds_list[0]) if gf.vm is not None: temp_fn = os.path.join(outdir, gf.feat_fn + '_vm.tif') iolib.writeGTiff(gf.vm, temp_fn, ds_list[0]) if gf.divQ is not None: temp_fn = os.path.join(outdir, gf.feat_fn + '_divQ.tif') iolib.writeGTiff(gf.divQ, temp_fn, ds_list[0]) #Do AED for all #Compute mb using scaled AED vs. polygon #Check for valid pixel count vs. feature area, fill if appropriate if mb_plot and (gf.glac_area / 1E6 > min_glac_area_writeout): z_bin_edges = hist_plot(gf, outdir) gf.z1_hs = geolib.gdaldem_mem_ds(ds_list[0], processing='hillshade', returnma=True) gf.z2_hs = geolib.gdaldem_mem_ds(ds_list[1], processing='hillshade', returnma=True) map_plot(gf, z_bin_edges, outdir) return outlist, gf
#Try to pull out second timestamp from dz_fn dem2_ts = timelib.fn_getdatetime_list(dz_fn)[-1] outprefix = os.path.splitext(os.path.split(dz_fn)[1])[0] outprefix = os.path.join(args.outdir, outprefix) #Calculate water year wy = dem1_ts.year + 1 if dem1_ts.month >= 10: wy = dem1_ts.year #These need to be updated in geolib to use gdaldem API hs = geolib.gdaldem_mem_ds(dem1_ds, processing='hillshade', returnma=True) hs_clim = (1,255) dem_clim = malib.calcperc(dem1, (1,99)) res = geolib.get_res(dem1_ds)[0] if args.density is None: #Attempt to extract from nearby SNOTEL sites for dem_ts #Attempt to use model #Last resort, use constant value rho_s = 0.5 #rho_s = 0.4 #rho_s = 0.36 #Convert snow depth to swe swe = dz * rho_s if args.filter: print("Filtering SWE map")
#This will return warped, in-memory GDAL dataset objects #Can also resample all inputs to a lower resolution (res=256) ds_list = warplib.memwarp_multi_fn(dem_fn_list, extent='intersection', res='min') #Load datasets to NumPy arrays dem_2007, dem_2009 = [iolib.ds_getma(i) for i in ds_list] dem_list = [dem_2007, dem_2009] #dem_list = [iolib.ds_getma(i) for i in ds_list] import matplotlib matplotlib.pyplot.imshow(dem_2007) matplotlib.pyplot.imshow(dem_2009) titles = ['2007', '2009'] clim = malib.calcperc(dem_list[0], (2,98)) plot_panels(2, dem_list, clim, titles, 'inferno', 'Elevation (m WGS84)', fn='dem.png') # Its possible sometimes to get date times from TIFFS but apparently these DEMS don't have any datetimes assigned. # We can get the date times from the original *(unmerged) files though. [timelib.fn_getdatetime(fn) for fn in [ '/Users/elischwat/Downloads/datasetsA/lewis_2009/dtm/lewis_2009_dtm_44.tif', '/Users/elischwat/Downloads/datasetsA/rainier_2007/rainier_2007_dtm_12.tif', '/Users/elischwat/Downloads/datasetsA/rainier_2007/rainier_2007_dtm_13.tif', '/Users/elischwat/Downloads/datasetsA/rainier_2007/rainier_2007_dtm_14.tif', '/Users/elischwat/Downloads/datasetsA/rainier_2007/rainier_2007_dtm_7.tif', '/Users/elischwat/Downloads/datasetsA/rainier_2007/rainier_2007_dtm_8.tif', '/Users/elischwat/Downloads/datasetsA/rainier_2007/rainier_2007_dtm_9.tif'
def bma_fig(fig, bma, cmap='cpt_rainbow', clim=None, clim_perc=(2, 98), bg=None, bg_perc=(2, 98), n_subplt=1, subplt=1, label=None, title=None, contour_int=None, contour_fn=None, alpha=0.5, ticks=False, scalebar=None, ds=None, shp=None, imshow_kwargs={'interpolation': 'nearest'}, cbar_kwargs={'orientation': 'vertical'}, **kwargs): #We don't use the kwargs, just there to save parsing in main if clim is None: clim = pltlib.get_clim(bma, clim_perc=clim_perc) print("Colorbar limits: %0.3f %0.3f" % (clim[0], clim[1])) #Link all subplots for zoom/pan sharex = sharey = None if len(fig.get_axes()) > 0: sharex = sharey = fig.get_axes()[0] #Hack to catch situations with only 1 subplot, but a subplot number > 1 if n_subplt == 1: subplt = 1 #One row, multiple columns ax = fig.add_subplot(1, n_subplt, subplt, sharex=sharex, sharey=sharey) #This occupies the full figure #ax = fig.add_axes([0., 0., 1., 1., ]) #ax.patch.set_facecolor('black') ax.patch.set_facecolor('white') #Set appropriate nodata value color cmap_name = cmap cmap = pltlib.cmap_setndv(cmap_name) #ax.set_title("Band %i" % subplt, fontsize=10) if title is not None: ax.set_title(title) #If a background image is provided, plot it first if bg is not None: #Note, alpha=1 is opaque, 0 completely transparent #alpha = 0.6 bg_perc = (4, 96) bg_alpha = 1.0 #bg_clim = malib.calcperc(bg, bg_perc) bg_clim = (1, 255) bg_cmap_name = 'gray' bg_cmap = pltlib.cmap_setndv(bg_cmap_name, cmap_name) #bg_cmap = plt.get_cmap(bg_cmap_name) #if 'inferno' in cmap_name: # bg_cmap.set_bad('0.5', alpha=1) #else: # bg_cmap.set_bad('k', alpha=1) #Set the overlay bad values to completely transparent, otherwise darkens the bg cmap.set_bad(alpha=0) bgplot = ax.imshow(bg, cmap=bg_cmap, clim=bg_clim, alpha=bg_alpha) imgplot = ax.imshow(bma, alpha=alpha, cmap=cmap, clim=clim, **imshow_kwargs) else: imgplot = ax.imshow(bma, cmap=cmap, clim=clim, **imshow_kwargs) gt = None if ds is not None: gt = np.array(ds.GetGeoTransform()) gt_scale_factor = min( np.array([ds.RasterYSize, ds.RasterXSize]) / np.array(bma.shape, dtype=float)) gt[1] *= gt_scale_factor gt[5] *= gt_scale_factor ds_srs = geolib.get_ds_srs(ds) if ticks: scale_ticks(ax, ds) else: pltlib.hide_ticks(ax) xres = geolib.get_res(ds)[0] else: pltlib.hide_ticks(ax) #This forces the black line outlining the image subplot to snap to the actual image dimensions #depreciated in 2.2 #ax.set_adjustable('box-forced') if cbar_kwargs: #Should set the format based on dtype of input data #cbar_kwargs['format'] = '%i' #cbar_kwargs['format'] = '%0.1f' #cbar_kwargs['orientation'] = 'horizontal' #Determine whether we need to add extend triangles to colorbar cbar_kwargs['extend'] = pltlib.get_cbar_extend(bma, clim) #Add the colorbar to the axes cbar = pltlib.add_cbar(ax, imgplot, label=label, cbar_kwargs=cbar_kwargs) #Plot contours every contour_int interval and update colorbar appropriately if contour_int is not None: if contour_fn is not None: contour_bma = iolib.fn_getma(contour_fn) contour_bma_clim = malib.calcperc(contour_bma) else: contour_bma = bma contour_bma_clim = clim #PIG bed ridge contours #bma_clim = (-1300, -300) #Jak front shear margin contours #bma_clim = (2000, 4000) contour_bma_clim = (100, 250) cstart = int(np.floor(contour_bma_clim[0] / contour_int)) * contour_int cend = int(np.ceil(contour_bma_clim[1] / contour_int)) * contour_int #Turn off dashed negative (beds are below sea level) #matplotlib.rcParams['contour.negative_linestyle'] = 'solid' clvl = np.arange(cstart, cend + 1, contour_int) contour_prop = { 'levels': clvl, 'linestyle': '-', 'linewidths': 0.5, 'alpha': 1.0 } #contours = ax.contour(contour_bma, colors='k', **contour_prop) #contour_cmap = 'gray' contour_cmap = 'gray_r' #This prevents white contours contour_cmap_clim = (0, contour_bma_clim[-1]) contours = ax.contour(contour_bma, cmap=contour_cmap, vmin=contour_cmap_clim[0], \ vmax=contour_cmap_clim[-1], **contour_prop) #Add labels ax.clabel(contours, inline=True, inline_spacing=0, fontsize=4, fmt='%i') #Update the cbar with contour locations #cbar.add_lines(contours) #cbar.set_ticks(contours.levels) #Plot shape overlay, moved code to pltlib if shp is not None: pltlib.shp_overlay(ax, ds, shp, gt=gt, color='k') if scalebar: scale_ticks(ax, ds) sb_loc = pltlib.best_scalebar_location(bma) #Force scalebar position #sb_loc = 'lower right' pltlib.add_scalebar(ax, xres, location=sb_loc) if not ticks: pltlib.hide_ticks(ax) #Set up interactive display global gbma gbma = bma global ggt ggt = gt #Clicking on a subplot will make it active for z-coordinate display fig.canvas.mpl_connect('button_press_event', onclick) fig.canvas.mpl_connect('axes_enter_event', enter_axis) #Add support for interactive z-value display ax.format_coord = format_coord
def main(): parser = getparser() args = parser.parse_args() if args.seedmode == 'existing_velocity': if args.vx_fn is None or args.vy_fn is None: parser.error('"-seedmode existing_velocity" requires "-vx_fn" and "-vy_fn"') print('\n%s' % datetime.now()) print('%s UTC\n' % datetime.utcnow()) align = args.align seedmode = args.seedmode spr = args.refinement erode = args.erode #Correlator tile timeout #With proper seeding, correlation should be very fast #timeout = 360 timeout = 1200 threads = args.threads kernel = (args.kernel, args.kernel) #SGM correlator if spr > 3: #kernel = (7,7) kernel = (11,11) erode = 0 #Smooth the output F.tif smoothF = args.filter res = args.tr #Resample input to something easier to work with #res = 4.0 #Open input files fn1 = args.fn1 fn2 = args.fn2 if not iolib.fn_check(fn1) or not iolib.fn_check(fn2): sys.exit("Unable to locate input files") if args.outdir is not None: outdir = args.outdir else: outdir = '%s__%s_vmap_%sm_%ipx_spm%i' % (os.path.splitext(os.path.split(fn1)[1])[0], \ os.path.splitext(os.path.split(fn2)[1])[0], res, kernel[0], spr) #Note, can encounter filename length issues in boost, just use vmap prefix outprefix = '%s/vmap' % (outdir) if not os.path.exists(outdir): os.makedirs(outdir) #Check to see if inputs have geolocation and projection information ds1 = iolib.fn_getds(fn1) ds2 = iolib.fn_getds(fn2) if geolib.srs_check(ds1) and geolib.srs_check(ds2): ds1_clip_fn = os.path.join(outdir, os.path.splitext(os.path.basename(fn1))[0]+'_warp.tif') ds2_clip_fn = os.path.join(outdir, os.path.splitext(os.path.basename(fn2))[0]+'_warp.tif') if not os.path.exists(ds1_clip_fn) or not os.path.exists(ds2_clip_fn): #This should write out files to new subdir ds1_clip, ds2_clip = warplib.diskwarp_multi_fn([fn1, fn2], extent='intersection', res=res, r='average', outdir=outdir) ds1_clip = None ds2_clip = None #However, if inputs have identical extent/res/proj, then link to original files if not os.path.exists(ds1_clip_fn): os.symlink(os.path.abspath(fn1), ds1_clip_fn) if not os.path.exists(ds2_clip_fn): os.symlink(os.path.abspath(fn2), ds2_clip_fn) align = 'None' #Mask support - limit correlation only to rock/ice surfaces, no water/veg #This masks input images - guarantee we won't waste time correlating over vegetation #TODO: Add support to load arbitrary raster or shp mask if args.mask_input: ds1_masked_fn = os.path.splitext(ds1_clip_fn)[0]+'_masked.tif' ds2_masked_fn = os.path.splitext(ds2_clip_fn)[0]+'_masked.tif' if not os.path.exists(ds1_masked_fn) or not os.path.exists(ds2_masked_fn): #Load NLCD or bareground mask from demcoreg.dem_mask import get_lulc_mask ds1_clip = iolib.fn_getds(ds1_clip_fn) lulc_mask_fn = os.path.join(outdir, 'lulc_mask.tif') #if not os.path.exists(nlcd_mask_fn): lulc_mask = get_lulc_mask(ds1_clip, mask_glaciers=False, filter='not_forest') iolib.writeGTiff(lulc_mask, lulc_mask_fn, ds1_clip) ds1_clip = None #Now apply to original images #This could be problematic for huge inputs, see apply_mask.py #lulc_mask = lulc_mask.astype(int) for fn in (ds1_clip_fn, ds2_clip_fn): ds = iolib.fn_getds(fn) a = iolib.ds_getma(ds) a = np.ma.array(a, mask=~(lulc_mask)) if a.count() > 0: out_fn = os.path.splitext(fn)[0]+'_masked.tif' iolib.writeGTiff(a,out_fn,ds) a = None else: sys.exit("No unmasked pixels over bare earth") ds1_clip_fn = ds1_masked_fn ds2_clip_fn = ds2_masked_fn else: ds1_clip_fn = fn1 ds2_clip_fn = fn2 #Now let user specify alignment methods as option - don't hardcode #align = 'Homography' #align = 'AffineEpipolar' ds1 = None ds2 = None #Should have extra kwargs option here stereo_opt = get_stereo_opt(threads=threads, kernel=kernel, timeout=timeout, \ erode=erode, spr=spr, align=align) #Stereo arguments #Latest version of ASP should accept tif without camera models #stereo_args = [ds1_clip_fn, ds2_clip_fn, outprefix] #Nope - still need to provide dummy camera models, and they must be unique files #Use the dummy.tsai file bundled in the vmap repo dummy_tsai = os.path.join(os.path.split(os.path.realpath(__file__))[0], 'dummy.tsai') dummy_tsai2 = os.path.splitext(dummy_tsai)[0]+'2.tsai' if not os.path.exists(dummy_tsai2): dummy_tsai2 = os.symlink(dummy_tsai, os.path.splitext(dummy_tsai)[0]+'2.tsai') stereo_args = [ds1_clip_fn, ds2_clip_fn, dummy_tsai, dummy_tsai2, outprefix] #Run stereo_pprc if not os.path.exists(outprefix+'-R_sub.tif'): run_cmd('stereo_pprc', stereo_opt+stereo_args, msg='0: Preprocessing') #Copy proj info to outputs, this should happen automatically now? for ext in ('L', 'R', 'L_sub', 'R_sub', 'lMask', 'rMask', 'lMask_sub', 'rMask_sub'): geolib.copyproj(ds1_clip_fn, '%s-%s.tif' % (outprefix,ext)) #Prepare seeding for stereo_corr #TODO: these are untested after refactoring if not os.path.exists(outprefix+'_D_sub.tif'): #Don't need to do anything for default seed-mode 1 if seedmode == 'sparse_disp': #Sparse correlation of full-res images stereo_opt.extend(['--corr-seed-mode', '3']) sparse_disp_opt = [] sparse_disp_opt.extend(['--Debug', '--coarse', '512', '--fine', '256', '--no_epipolar_fltr']) sparse_disp_opt.extend(['-P', str(threads)]) sparse_disp_args = [outprefix+'-L.tif', outprefix+'-R.tif', outprefix] run_cmd('sparse_disp', sparse_disp_opt+sparse_disp_args, msg='0.5: D_sub generation') elif seedmode == 'existing_velocity': #User-input low-res velocity maps for seeding #TODO: Add functions that fetch best available velocities for Ant/GrIS or user-defined low-res velocities #Automatically query GoLive velocities here vx_fn = args.vx_fn vy_fn = args.vy_fn #Check for existence #HMA seeding vdir = '/nobackup/deshean/rpcdem/hma/velocity_jpl_amaury_2013-2015' vx_fn = os.path.join(vdir, 'PKH_WRS2_B8_2013_2015_snr5_n1_r170_res12.x_vel.TIF') vy_fn = os.path.join(vdir, 'PKH_WRS2_B8_2013_2015_snr5_n1_r170_res12.y_vel.TIF') if os.path.exists(vx_fn) and os.path.exists(vy_fn): ds1_clip = iolib.fn_getds(ds1_clip_fn) ds1_res = geolib.get_res(ds1_clip, square=True)[0] #Compute L_sub res - use this for output dimensions L_sub_fn = outprefix+'-L_sub.tif' L_sub_ds = gdal.Open(L_sub_fn) L_sub_x_scale = float(ds1_clip.RasterXSize) / L_sub_ds.RasterXSize L_sub_y_scale = float(ds1_clip.RasterYSize) / L_sub_ds.RasterYSize L_sub_scale = np.max([L_sub_x_scale, L_sub_y_scale]) L_sub_res = ds1_res * L_sub_scale #Since we are likely upsampling here, use cubicspline vx_ds_clip, vy_ds_clip = warplib.memwarp_multi_fn([vx_fn, vy_fn], extent=ds1_clip, \ t_srs=ds1_clip, res=L_sub_res, r='cubicspline') ds1_clip = None #Get vx and vy arrays vx = iolib.ds_getma(vx_ds_clip) vy = iolib.ds_getma(vy_ds_clip) #Determine time interval between inputs #Use to scaling of known low-res velocities t_factor = get_t_factor_fn(ds1_clip_fn, ds2_clip_fn, ds=vx_ds_clip) if t_factor is not None: #Compute expected offset in scaled pixels dx = (vx*t_factor)/L_sub_res dy = (vy*t_factor)/L_sub_res #Note: Joughin and Rignot's values are positive y up! #ASP is positive y down, so need to multiply these values by -1 #dy = -(vy*t_factor)/L_sub_res #Should smooth/fill dx and dy #If absolute search window is only 30x30 #Don't seed, just use fixed search window #search_window_area_thresh = 900 search_window_area_thresh = 0 search_window = np.array([dx.min(), dy.min(), dx.max(), dy.max()]) dx_p = calcperc(dx, perc=(0.5, 99.5)) dy_p = calcperc(dy, perc=(0.5, 99.5)) search_window = np.array([dx_p[0], dy_p[0], dx_p[1], dy_p[1]]) search_window_area = (search_window[2]-search_window[0]) * (search_window[3]-search_window[1]) if search_window_area < search_window_area_thresh: stereo_opt.extend(['--corr-seed-mode', '0']) stereo_opt.append('--corr-search') stereo_opt.extend([str(x) for x in search_window]) #pad_perc=0.1 #stereo_opt.extend(['--corr-sub-seed-percent', str(pad_perc)] #Otherwise, generate a D_sub map from low-res velocity else: stereo_opt.extend(['--corr-seed-mode', '3']) #This is relative to the D_sub scaled disparities d_sub_fn = L_sub_fn.split('-L_sub')[0]+'-D_sub.tif' gen_d_sub(d_sub_fn, dx, dy) #If the above didn't generate a D_sub.tif for seeding, run stereo_corr to generate Low-res D_sub.tif if not os.path.exists(outprefix+'-D_sub.tif'): newopt = ['--compute-low-res-disparity-only',] run_cmd('stereo_corr', newopt+stereo_opt+stereo_args, msg='1.1: Low-res Correlation') #Copy projection info to D_sub geolib.copyproj(outprefix+'-L_sub.tif', outprefix+'-D_sub.tif') #Mask D_sub to limit correlation over bare earth surfaces #This _should_ be a better approach than masking input images, but stereo_corr doesn't honor D_sub #Still need to mask input images before stereo_pprc #Left this in here for reference, or if this changes in ASP if False: D_sub_ds = gdal.Open(outprefix+'-D_sub.tif', gdal.GA_Update) #Mask support - limit correlation only to rock/ice surfaces, no water/veg from demcoreg.dem_mask import get_nlcd, mask_nlcd nlcd_fn = get_nlcd() nlcd_ds = warplib.diskwarp_multi_fn([nlcd_fn,], extent=D_sub_ds, res=D_sub_ds, t_srs=D_sub_ds, r='near', outdir=outdir)[0] #validmask = mask_nlcd(nlcd_ds, valid='rock+ice') validmask = mask_nlcd(nlcd_ds, valid='not_forest', mask_glaciers=False) nlcd_mask_fn = os.path.join(outdir, 'nlcd_validmask.tif') iolib.writeGTiff(validmask, nlcd_mask_fn, nlcd_ds) #Now apply to D_sub (band 3 is valid mask) #validmask = validmask.astype(int) for b in (1,2,3): dsub = iolib.ds_getma(D_sub_ds, b) dsub = np.ma.array(dsub, mask=~(validmask)) D_sub_ds.GetRasterBand(b).WriteArray(dsub.filled()) D_sub_ds = None #OK, finally run stereo_corr full-res integer correlation with appropriate seeding if not os.path.exists(outprefix+'-D.tif'): run_cmd('stereo_corr', stereo_opt+stereo_args, msg='1: Correlation') geolib.copyproj(ds1_clip_fn, outprefix+'-D.tif') #Run stereo_rfne if spr > 0: if not os.path.exists(outprefix+'-RD.tif'): run_cmd('stereo_rfne', stereo_opt+stereo_args, msg='2: Refinement') geolib.copyproj(ds1_clip_fn, outprefix+'-RD.tif') d_fn = make_ln(outdir, outprefix, '-RD.tif') else: ln_fn = outprefix+'-RD.tif' if os.path.lexists(ln_fn): os.remove(ln_fn) os.symlink(os.path.split(outprefix)[1]+'-D.tif', ln_fn) #Run stereo_fltr if not os.path.exists(outprefix+'-F.tif'): run_cmd('stereo_fltr', stereo_opt+stereo_args, msg='3: Filtering') geolib.copyproj(ds1_clip_fn, outprefix+'-F.tif') d_fn = make_ln(outdir, outprefix, '-F.tif') if smoothF and not os.path.exists(outprefix+'-F_smooth.tif'): print('Smoothing F.tif') from pygeotools.lib import filtlib #Fill holes and smooth F F_fill_fn = outprefix+'-F_smooth.tif' F_ds = gdal.Open(outprefix+'-F.tif', gdal.GA_ReadOnly) #import dem_downsample_fill #F_fill_ds = dem_downsample_fill.gdalfill_ds(F_fill_ds) print('Creating F_smooth.tif') F_fill_ds = iolib.gtif_drv.CreateCopy(F_fill_fn, F_ds, 0, options=iolib.gdal_opt) F_ds = None for n in (1, 2): print('Smoothing band %i' % n) b = F_fill_ds.GetRasterBand(n) b_fill_bma = iolib.b_getma(b) #b_fill_bma = iolib.b_getma(dem_downsample_fill.gdalfill(b)) #Filter extreme values (careful, could lose areas of valid data with fastest v) #b_fill_bma = filtlib.perc_fltr(b_fill_bma, perc=(0.01, 99.99)) #These filters remove extreme values and fill data gaps #b_fill_bma = filtlib.median_fltr_skimage(b_fill_bma, radius=7, erode=0) #b_fill_bma = filtlib.median_fltr(b_fill_bma, fsize=7, origmask=True) #Gaussian filter b_fill_bma = filtlib.gauss_fltr_astropy(b_fill_bma, size=9) b.WriteArray(b_fill_bma) F_fill_ds = None d_fn = make_ln(outdir, outprefix, '-F_smooth.tif') print('\n%s' % datetime.now()) print('%s UTC\n' % datetime.utcnow()) #If time interval is specified, convert pixel displacements to rates if args.dt != 'none': #Check if vm.tif already exists #Should probably just overwrite by default #if os.path.exists(os.path.splitext(d_fn)[0]+'_vm.tif'): # print("\nFound existing velocity magnitude map!\n" #else: #Generate output velocity products and figure #Requires that vmap repo is in PATH cmd = ['disp2v.py', d_fn] #Note: this will attempt to automatically determine control surfaces #disp2v.py will accept arbitrary mask, could pass through here if args.remove_offsets: cmd.append('-remove_offsets') cmd.extend(['-dt', args.dt]) print("Converting disparities to velocities") print(cmd) subprocess.call(cmd)
outdir = 'stack_anomaly' if not os.path.exists(outdir): os.makedirs(outdir) #dem_fn_list = glob.glob('*8m_trans_warp.tif') #stack = malib.DEMStack(dem_fn_list, med=True) #dem_ref_fn = 'rainier_allgood_mos-tile-0_warp.tif' #dem_ref = iolib.fn_getma(dem_ref_fn) stack_fn = sys.argv[1] #stack = malib.DEMStack(stack_fn=stack_fn, med=True) #dem_ref = stack.stack_med stack = malib.DEMStack(stack_fn=stack_fn) dem_ref = stack.stack_mean dem_ds = stack.get_ds() dem_clim = malib.calcperc(dem_ref, (2,98)) dem_fn_list = stack.fn_list anomaly_stack = stack.ma_stack - dem_ref anomaly_clim = np.max(np.abs(malib.calcperc(anomaly_stack, (1,99)))) anomaly_clim = (-anomaly_clim, anomaly_clim) #for dem_fn in [dem_ref_fn]+dem_fn_list: for n, dem_fn in enumerate(dem_fn_list): print('%i of %i: %s' % (n+1, len(dem_fn_list), dem_fn)) #print(dem_fn) #dem_ds = iolib.fn_getds(dem_fn) #dem = iolib.ds_getma(dem_ds) dem_fn = stack.fn_list[n] #title = dem_fn title = None dem = stack.ma_stack[n]
def make_map(mb_dissolve_df=None, glac_df_mb=None, agg_df=None, col=('mb_mwea', 'mean'), border_df=None, crs=crs, extent=None, hs=None, hs_extent=None, clim=None, labels='val', title=None): fig, ax = plt.subplots(figsize=(10, 8)) ax.set_aspect('equal') legend = add_legend(ax, sf=scaling_f) if title is not None: ax.set_title(title) if clim is None: #clim = (glac_df_mb[col].min(), glac_df_mb[col].max()) clim = malib.calcperc_sym(mb_dissolve_df[col], perc=(1, 99)) cmap = 'RdBu' if 'mb_mwea' in col: label = 'Mass Balance (m we/yr)' elif 'mb_Gta' in col: label = 'Mass Balance (Gt/yr)' elif 'meltwater' in col: label = 'Excess Meltwater Runoff (Gt/yr)' #Reverse, as these are negative values cmap = 'YlOrRd_r' #cmap = 'inferno' clim = malib.calcperc(mb_dissolve_df[col], perc=(0, 99)) elif 't1' in col: cmap = 'inferno' label = 'Source Date (year)' #This is cartopy-enabled axes #ax = plt.axes(projection=crs) #Currently unsupported for AEA #gl = ax.gridlines(draw_labels=True, linewidth=0.5, color='gray', alpha=0.5, linestyle='--') if hs is not None: print("Plotting image") hs_style = { 'cmap': 'gray', 'origin': 'upper', 'extent': cartopy_extent(hs_extent), 'transform': crs } ax.imshow(hs, **hs_style) if border_df is not None: print("Plotting borders") border_style = { 'facecolor': '0.65', 'edgecolor': 'k', 'linewidth': 0.7 } border_df.plot(ax=ax, **border_style) if agg_df is not None: print("Plotting agg boundaries") #This was to get colored regions #agg_style = {'cmap':'cpt_rainbow', 'edgecolor':'none', 'linewidth':0, 'alpha':0.05} agg_style = { 'cmap': 'summer', 'edgecolor': 'none', 'linewidth': 0, 'alpha': 0.05 } #agg_style = {'facecolor':'0.95','edgecolor':'k', 'linewidth':0.3, 'alpha':0.2} agg_df.plot(ax=ax, **agg_style) if glac_df_mb is not None: print("Plotting glacier polygons") glac_style = {'edgecolor': 'k', 'linewidth': 0.1, 'alpha': 0.2} #This plots mb color ramp for each glacier polygon #glac_ax = glac_df_mb.plot(ax=ax, column=col[0], cmap=cmap, vmin=clim[0], vmax=clim[1], **glac_style) #This plots outlines glac_ax = glac_df_mb.plot(ax=ax, facecolor='none', **glac_style) if agg_df is not None: agg_style = {'facecolor': 'none', 'edgecolor': 'w', 'linewidth': 0.5} agg_df.plot(ax=ax, **agg_style) #https://stackoverflow.com/questions/36008648/colorbar-on-geopandas # fake up the array of the scalar mappable so we can plot colorbar. Urgh... sc = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin=clim[0], vmax=clim[1])) sc._A = [] if mb_dissolve_df is not None: print("Plotting scatterplot of %s values" % (col, )) #Plot single values for region or basin x = mb_dissolve_df['centroid_x'] y = mb_dissolve_df['centroid_y'] #Scale by total glacier area in each polygon s = scaling_f * mb_dissolve_df[('Area_all', 'sum')] c = mb_dissolve_df[col] sc_style = { 'cmap': cmap, 'edgecolor': 'k', 'linewidth': 0.5, 'alpha': 0.8 } sc = ax.scatter(x, y, s, c, vmin=clim[0], vmax=clim[1], **sc_style) #Add labels text_kw = {'family': 'sans-serif', 'fontsize': 8, 'color': 'k'} if labels is not None: print("Adding annotations") for k, v in mb_dissolve_df.iterrows(): #lbl = '%0.2f +/- %0.2f' % (v[col], v[(col[0]+'_sigma',col[1])]) if labels == 'name+val': lbl = '%s\n%+0.2f' % (k, v[col]) else: lbl = '%+0.2f' % v[col] #ax.annotate(lbl, xy=(v['centroid_x'],v['centroid_y']), xytext=(1,0), textcoords='offset points', family='sans-serif', fontsize=6, color='darkgreen') txt = ax.annotate(lbl, xy=(v['centroid_x'], v['centroid_y']), ha='center', va='center', **text_kw) txt.set_path_effects([ path_effects.Stroke(linewidth=0.75, foreground='w'), path_effects.Normal() ]) #This is minx, miny, maxx, maxy if extent is None: #if glac_df_mb is not None: # extent = glac_df_mb.total_bounds #else: extent = mb_dissolve_df.total_bounds #For cartopy axes #ax.set_extent(cartopy_extent(extent), crs=crs) #Pad extent so labels fit within map #extent = geolib.pad_extent(extent, perc=0.01, uniform=True) ax.set_xlim(extent[0], extent[2]) ax.set_ylim(extent[1], extent[3]) #Adding colorbar doesn't work with the cartopy axes pltlib.add_cbar(ax, sc, label=label) pltlib.add_scalebar(ax, res=1) pltlib.hide_ticks(ax) plt.tight_layout() return fig
def main2(args): #Should check that files exist dem1_fn = args.ref_fn dem2_fn = args.src_fn mode = args.mode apply_mask = not args.nomask max_offset_m = args.max_offset tiltcorr = args.tiltcorr #These are tolerances (in meters) to stop iteration tol = args.tol min_dx = tol min_dy = tol min_dz = tol #Maximum number of iterations max_n = 10 outdir = args.outdir if outdir is None: outdir = os.path.splitext(dem2_fn)[0] + '_dem_align' if not os.path.exists(outdir): os.makedirs(outdir) outprefix = '%s_%s' % (os.path.splitext(os.path.split(dem2_fn)[-1])[0], \ os.path.splitext(os.path.split(dem1_fn)[-1])[0]) outprefix = os.path.join(outdir, outprefix) print("\nReference: %s" % dem1_fn) print("Source: %s" % dem2_fn) print("Mode: %s" % mode) print("Output: %s\n" % outprefix) dem2_ds = gdal.Open(dem2_fn, gdal.GA_ReadOnly) #Often the "ref" DEM is high-res lidar or similar #This is a shortcut to resample to match "source" DEM dem1_ds = warplib.memwarp_multi_fn([ dem1_fn, ], res=dem2_ds, extent=dem2_ds, t_srs=dem2_ds)[0] #dem1_ds = gdal.Open(dem1_fn, gdal.GA_ReadOnly) #Create a copy to be updated in place dem2_ds_align = iolib.mem_drv.CreateCopy('', dem2_ds, 0) #dem2_ds_align = dem2_ds #Iteration number n = 1 #Cumulative offsets dx_total = 0 dy_total = 0 dz_total = 0 #Now iteratively update geotransform and vertical shift while True: print("*** Iteration %i ***" % n) dx, dy, dz, static_mask, fig = compute_offset(dem1_ds, dem2_ds_align, dem2_fn, mode, max_offset_m, apply_mask=apply_mask) if n == 1: static_mask_orig = static_mask xyz_shift_str_iter = "dx=%+0.2fm, dy=%+0.2fm, dz=%+0.2fm" % (dx, dy, dz) print("Incremental offset: %s" % xyz_shift_str_iter) #Should make an animation of this converging if fig is not None: dst_fn = outprefix + '_%s_iter%i_plot.png' % (mode, n) print("Writing offset plot: %s" % dst_fn) fig.gca().set_title(xyz_shift_str_iter) fig.savefig(dst_fn, dpi=300, bbox_inches='tight', pad_inches=0.1) #Apply the horizontal shift to the original dataset dem2_ds_align = coreglib.apply_xy_shift(dem2_ds_align, dx, dy, createcopy=False) dem2_ds_align = coreglib.apply_z_shift(dem2_ds_align, dz, createcopy=False) dx_total += dx dy_total += dy dz_total += dz print("Cumulative offset: dx=%+0.2fm, dy=%+0.2fm, dz=%+0.2fm" % (dx_total, dy_total, dz_total)) #Fit plane to residuals and remove #Might be better to do this after converging """ if tiltcorr: print("Applying planar tilt correction") gt = dem2_ds_align.GetGeoTransform() #Need to compute diff_euler here #Copy portions of compute_offset, create new function vals, resid, coeff = geolib.ma_fitplane(diff_euler_align, gt, perc=(4, 96)) dem2_ds_align = coreglib.apply_z_shift(dem2_ds_align, -vals, createcopy=False) """ n += 1 print("\n") #If magnitude of shift in all directions is less than tol #if n > max_n or (abs(dx) <= min_dx and abs(dy) <= min_dy and abs(dz) <= min_dz): #If magnitude of shift is less than tol dm = np.sqrt(dx**2 + dy**2 + dz**2) if n > max_n or dm < tol: break #String to append to output filenames xyz_shift_str_cum = '_%s_x%+0.2f_y%+0.2f_z%+0.2f' % (mode, dx_total, dy_total, dz_total) if tiltcorr: xyz_shift_str_cum += "_tiltcorr" #Compute original elevation difference if True: dem1_clip_ds, dem2_clip_ds = warplib.memwarp_multi([dem1_ds, dem2_ds], \ res='max', extent='intersection', t_srs=dem2_ds) dem1_orig = iolib.ds_getma(dem1_clip_ds, 1) dem2_orig = iolib.ds_getma(dem2_clip_ds, 1) diff_euler_orig = dem2_orig - dem1_orig if not apply_mask: static_mask_orig = np.ma.getmaskarray(diff_euler_orig) diff_euler_orig_compressed = diff_euler_orig[~static_mask_orig] diff_euler_orig_stats = np.array( malib.print_stats(diff_euler_orig_compressed)) #Write out original eulerian difference map print( "Writing out original euler difference map for common intersection before alignment" ) dst_fn = outprefix + '_orig_dz_eul.tif' iolib.writeGTiff(diff_euler_orig, dst_fn, dem1_clip_ds) #Compute final elevation difference if True: dem1_clip_ds_align, dem2_clip_ds_align = warplib.memwarp_multi([dem1_ds, dem2_ds_align], \ res='max', extent='intersection', t_srs=dem2_ds_align) dem1_align = iolib.ds_getma(dem1_clip_ds_align, 1) dem2_align = iolib.ds_getma(dem2_clip_ds_align, 1) diff_euler_align = dem2_align - dem1_align if not apply_mask: static_mask = np.ma.getmaskarray(diff_euler_align) diff_euler_align_compressed = diff_euler_align[~static_mask] diff_euler_align_stats = np.array( malib.print_stats(diff_euler_align_compressed)) #Fit plane to residuals and remove if tiltcorr: print("Applying planar tilt correction") gt = dem1_clip_ds_align.GetGeoTransform() #Need to apply the mask here, so we're only fitting over static surfaces #Note that the origmask=False will compute vals for all x and y indices, which is what we want vals, resid, coeff = geolib.ma_fitplane(np.ma.array(diff_euler_align, mask=static_mask), \ gt, perc=(4, 96), origmask=False) #Remove planar offset from difference map diff_euler_align -= vals #Remove planar offset from aligned dem2 #Note: dimensions of ds and vals will be different as vals are computed for clipped intersection #Recompute planar offset for dem2_ds_align extent xgrid, ygrid = geolib.get_xy_grids(dem2_ds_align) vals = coeff[0] * xgrid + coeff[1] * ygrid + coeff[2] dem2_ds_align = coreglib.apply_z_shift(dem2_ds_align, -vals, createcopy=False) if not apply_mask: static_mask = np.ma.getmaskarray(diff_euler_align) diff_euler_align_compressed = diff_euler_align[~static_mask] diff_euler_align_stats = np.array( malib.print_stats(diff_euler_align_compressed)) print("Creating fitplane plot") fig, ax = plt.subplots(figsize=(6, 6)) fitplane_clim = malib.calcperc(vals, (2, 98)) im = ax.imshow(vals, cmap='cpt_rainbow', clim=fitplane_clim) res = float(geolib.get_res(dem2_clip_ds, square=True)[0]) pltlib.add_scalebar(ax, res=res) pltlib.hide_ticks(ax) pltlib.add_cbar(ax, im, label='Fit plane residuals (m)') fig.tight_layout() dst_fn1 = outprefix + '%s_align_dz_eul_fitplane.png' % xyz_shift_str_cum print("Writing out figure: %s" % dst_fn1) fig.savefig(dst_fn1, dpi=300, bbox_inches='tight', pad_inches=0.1) #Compute higher-order fits? #Could also attempt to model along-track and cross-track artifacts #Write out aligned eulerian difference map for clipped extent with vertial offset removed dst_fn = outprefix + '%s_align_dz_eul.tif' % xyz_shift_str_cum print( "Writing out aligned difference map with median vertical offset removed" ) iolib.writeGTiff(diff_euler_align, dst_fn, dem1_clip_ds) #Write out aligned dem_2 with vertial offset removed if True: dst_fn2 = outprefix + '%s_align.tif' % xyz_shift_str_cum print( "Writing out shifted dem2 with median vertical offset removed: %s" % dst_fn2) #Might be cleaner way to write out MEM ds directly to disk dem2_align = iolib.ds_getma(dem2_ds_align) iolib.writeGTiff(dem2_align, dst_fn2, dem2_ds_align) dem2_ds_align = None #Create output plot if True: print("Creating final plot") dem1_hs = geolib.gdaldem_mem_ma(dem1_orig, dem1_clip_ds, returnma=True) dem2_hs = geolib.gdaldem_mem_ma(dem2_orig, dem2_clip_ds, returnma=True) f, axa = plt.subplots(2, 3, figsize=(11, 8.5)) for ax in axa.ravel()[:-1]: ax.set_facecolor('k') pltlib.hide_ticks(ax) dem_clim = malib.calcperc(dem1_orig, (2, 98)) axa[0, 0].imshow(dem1_hs, cmap='gray') axa[0, 0].imshow(dem1_orig, cmap='cpt_rainbow', clim=dem_clim, alpha=0.6) res = float(geolib.get_res(dem1_clip_ds, square=True)[0]) pltlib.add_scalebar(axa[0, 0], res=res) axa[0, 0].set_title('Reference DEM') axa[0, 1].imshow(dem2_hs, cmap='gray') axa[0, 1].imshow(dem2_orig, cmap='cpt_rainbow', clim=dem_clim, alpha=0.6) axa[0, 1].set_title('Source DEM') axa[0, 2].imshow(~static_mask_orig, clim=(0, 1), cmap='gray') axa[0, 2].set_title('Surfaces for co-registration') dz_clim = malib.calcperc_sym(diff_euler_orig_compressed, (5, 95)) im = axa[1, 0].imshow(diff_euler_orig, cmap='RdBu', clim=dz_clim) pltlib.add_cbar(axa[1, 0], im, label=None) axa[1, 0].set_title('Elev. Diff. Before (m)') im = axa[1, 1].imshow(diff_euler_align, cmap='RdBu', clim=dz_clim) pltlib.add_cbar(axa[1, 1], im, label=None) axa[1, 1].set_title('Elev. Diff. After (m)') #Tried to insert Nuth fig here #ax_nuth.change_geometry(1,2,1) #f.axes.append(ax_nuth) bins = np.linspace(dz_clim[0], dz_clim[1], 128) axa[1, 2].hist(diff_euler_orig_compressed, bins, color='g', label='Before', alpha=0.5) axa[1, 2].hist(diff_euler_align_compressed, bins, color='b', label='After', alpha=0.5) axa[1, 2].axvline(0, color='k', linewidth=0.5, linestyle=':') axa[1, 2].set_xlabel('Elev. Diff. (m)') axa[1, 2].set_ylabel('Count (px)') axa[1, 2].set_title("Source - Reference") #axa[1,2].legend(loc='upper right') #before_str = 'Before\nmean: %0.2f\nstd: %0.2f\nmed: %0.2f\nnmad: %0.2f' % tuple(diff_euler_orig_stats[np.array((3,4,5,6))]) #after_str = 'After\nmean: %0.2f\nstd: %0.2f\nmed: %0.2f\nnmad: %0.2f' % tuple(diff_euler_align_stats[np.array((3,4,5,6))]) before_str = 'Before\nmed: %0.2f\nnmad: %0.2f' % tuple( diff_euler_orig_stats[np.array((5, 6))]) axa[1, 2].text(0.05, 0.95, before_str, va='top', color='g', transform=axa[1, 2].transAxes) after_str = 'After\nmed: %0.2f\nnmad: %0.2f' % tuple( diff_euler_align_stats[np.array((5, 6))]) axa[1, 2].text(0.65, 0.95, after_str, va='top', color='b', transform=axa[1, 2].transAxes) suptitle = '%s\nx: %+0.2fm, y: %+0.2fm, z: %+0.2fm' % ( os.path.split(outprefix)[-1], dx_total, dy_total, dz_total) f.suptitle(suptitle) f.tight_layout() plt.subplots_adjust(top=0.90) dst_fn = outprefix + '%s_align.png' % xyz_shift_str_cum print("Writing out figure: %s" % dst_fn) f.savefig(dst_fn, dpi=300, bbox_inches='tight', pad_inches=0.1) #Removing residual planar tilt can introduce additional slope/aspect dependent offset #Want to run another round of main dem_align after removing planar tilt if tiltcorr: print("\n Rerunning after applying tilt correction \n") #Create copy of original arguments import copy args2 = copy.copy(args) #Use aligned, tilt-corrected DEM as input src_fn for second round args2.src_fn = dst_fn2 #Assume we've already corrected most of the tilt during first round (also prevents endless loop) args2.tiltcorr = False main2(args2)
import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import ImageGrid from pygeotools.lib import iolib, timelib, geolib, malib from imview.lib import pltlib add_cbar = False #dem_fn_list = glob.glob('*32m.tif') #dem_ref_fn = 'rainier_allgood_mos-tile-0_warp.tif' dem_fn_list = sys.argv[1:] dems = np.ma.array([iolib.fn_getma_sub(fn) for fn in dem_fn_list]) dem_clim = malib.calcperc(dems, (2, 98)) w = 10.0 h = 7.5 f = plt.figure(figsize=(w, h)) n = len(dem_fn_list) ncols = int(np.ceil(np.sqrt((float(n) * w) / h))) nrows = int(np.ceil(float(n) / ncols)) #nrows = int(np.sqrt(n))+1 #ncols = nrows if add_cbar: grid = ImageGrid(f, 111, nrows_ncols=(nrows, ncols),
z_fltr_mask = glas_pts_fltr_mask[:,zcol] mX_fltr_mask, mY_fltr_mask, mZ = geolib.cT_helper(x_fltr_mask, y_fltr_mask, 0, pt_srs, geolib.get_ds_srs(dem_mask_ds)) pX_fltr_mask, pY_fltr_mask = geolib.mapToPixel(mX_fltr_mask, mY_fltr_mask, dem_mask_ds.GetGeoTransform()) pX_fltr_mask = np.atleast_1d(pX_fltr_mask) pY_fltr_mask = np.atleast_1d(pY_fltr_mask) dz = z_fltr_mask - samp[samp_idx,0] if True: print("Creating plot of %i output points" % x_fltr.shape[0]) fig_kw = {'figsize':(10,7.5)} fig, (ax1, ax2, ax3, ax4) = plt.subplots(1, 4, sharex=True, sharey=True, **fig_kw) #Plot DEM color shaded relief hs_ma = geolib.gdaldem_wrapper(dem_fn) hs_clim = malib.calcperc(hs_ma, perc=(0.5, 99.5)) dem_clim = malib.calcperc(dem_ma) ax1.imshow(hs_ma, cmap='gray', clim=hs_clim) im1 = ax1.imshow(dem_ma, cmap=cpt_rainbow, clim=dem_clim, alpha=0.5) cbar = pltlib.add_cbar(ax1, im1, label='DEM Elev. (m WGS84)') #Plot all color points over shaded relief im2 = ax2.imshow(hs_ma, cmap='gray', clim=hs_clim, alpha=0.5) #Plot all points in black sc2 = ax2.scatter(pX_fltr, pY_fltr, s=0.5, c='k', edgecolors='none') #Plot valid in color c = z_fltr_mask sc2 = ax2.scatter(pX_fltr_mask, pY_fltr_mask, s=0.5, c=c, cmap=cpt_rainbow, vmin=dem_clim[0], vmax=dem_clim[1], edgecolors='none') cbar = pltlib.add_cbar(ax2, sc2, label='Pt Elev. (m WGS84)') #Plot time
def bma_fig(fig, bma, cmap='cpt_rainbow', clim=None, clim_perc=(2,98), bg=None, bg_perc=(2,98), n_subplt=1, subplt=1, label=None, title=None, cint=None, alpha=0.5, ticks=False, scalebar=None, ds=None, shp=None, imshow_kwargs={'interpolation':'nearest'}, cbar_kwargs={'extend':'both', 'orientation':'vertical', 'shrink':0.7, 'fraction':0.12, 'pad':0.02}, **kwargs): #We don't use the kwargs, just there to save parsing in main if clim is None: clim = malib.calcperc(bma, clim_perc) #Deal with masked cases if clim[0] == clim[1]: if clim[0] > bma.fill_value: clim = (bma.fill_value, clim[0]) else: clim = (clim[0], bma.fill_value) print "Colorbar limits (%0.1f-%0.1f%%): %0.3f %0.3f" % (clim_perc[0], clim_perc[1], clim[0], clim[1]) else: print "Colorbar limits: %0.3f %0.3f" % (clim[0], clim[1]) #Link all subplots for zoom/pan sharex = sharey = None if len(fig.get_axes()) > 0: sharex = sharey = fig.get_axes()[0] #Hack to catch situations with only 1 subplot, but a subplot number > 1 if n_subplt == 1: subplt = 1 #One row, multiple columns ax = fig.add_subplot(1, n_subplt, subplt, sharex=sharex, sharey=sharey) #This occupies the full figure #ax = fig.add_axes([0., 0., 1., 1., ]) #ax.patch.set_facecolor('black') ax.patch.set_facecolor('white') cmap_name = cmap cmap = plt.get_cmap(cmap_name) if 'inferno' in cmap_name: #Use a gray background cmap.set_bad('0.5', alpha=1) else: #This sets the nodata background to opaque black cmap.set_bad('k', alpha=1) #cmap.set_bad('w', alpha=1) #ax.set_title("Band %i" % subplt, fontsize=10) if title is not None: ax.set_title(title) #If a background image is provided, plot it first if bg is not None: #Note, 1 is opaque, 0 completely transparent #alpha = 0.6 #bg_perc = (4,96) bg_perc = (0.05, 99.95) #bg_perc = (1, 99) bg_alpha = 1.0 #bg_alpha = 0.5 bg_clim = malib.calcperc(bg, bg_perc) bg_cmap_name = 'gray' bg_cmap = plt.get_cmap(bg_cmap_name) if 'inferno' in cmap_name: bg_cmap.set_bad('0.5', alpha=1) else: bg_cmap.set_bad('k', alpha=1) #Set the overlay bad values to completely transparent, otherwise darkens the bg cmap.set_bad(alpha=0) bgplot = ax.imshow(bg, cmap=bg_cmap, clim=bg_clim, alpha=bg_alpha) imgplot = ax.imshow(bma, alpha=alpha, cmap=cmap, clim=clim, **imshow_kwargs) else: imgplot = ax.imshow(bma, cmap=cmap, clim=clim, **imshow_kwargs) gt = None if ds is not None: gt = np.array(ds.GetGeoTransform()) gt_scale_factor = min(np.array([ds.RasterYSize, ds.RasterXSize])/np.array(bma.shape,dtype=float)) gt[1] *= gt_scale_factor gt[5] *= gt_scale_factor ds_srs = geolib.get_ds_srs(ds) if ticks: scale_ticks(ax, ds) else: pltlib.hide_ticks(ax) xres = geolib.get_res(ds)[0] else: pltlib.hide_ticks(ax) #This forces the black line outlining the image subplot to snap to the actual image dimensions ax.set_adjustable('box-forced') cbar = True if cbar: #Had to turn off the ax=ax for overlay to work #cbar = fig.colorbar(imgplot, ax=ax, extend='both', shrink=0.5) #Should set the format based on dtype of input data #cbar_kwargs['format'] = '%i' #cbar_kwargs['format'] = '%0.1f' #cbar_kwargs['orientation'] = 'horizontal' #cbar_kwargs['shrink'] = 0.8 cbar = pltlib.add_cbar(ax, imgplot, label=label, cbar_kwargs=cbar_kwargs) #Plot contours every cint interval and update colorbar appropriately if cint is not None: if bma_c is not None: bma_clim = malib.calcperc(bma_c) #PIG bed ridge contours #bma_clim = (-1300, -300) #Jak front shear margin contours #bma_clim = (2000, 4000) cstart = int(np.floor(bma_clim[0] / cint)) * cint cend = int(np.ceil(bma_clim[1] / cint)) * cint else: #cstart = int(np.floor(bma.min() / cint)) * cint #cend = int(np.ceil(bma.max() / cint)) * cint cstart = int(np.floor(clim[0] / cint)) * cint cend = int(np.ceil(clim[1] / cint)) * cint #Turn off dashed negative (beds are below sea level) #matplotlib.rcParams['contour.negative_linestyle'] = 'solid' clvl = np.arange(cstart, cend+1, cint) #contours = ax.contour(bma_c, colors='k', levels=clvl, alpha=0.5) contours = ax.contour(bma_c, cmap='gray', linestyle='--', levels=clvl, alpha=1.0) #Update the cbar with contour locations cbar.add_lines(contours) cbar.set_ticks(contours.levels) #Plot shape overlay, moved code to pltlib if shp is not None: pltlib.shp_overlay(ax, ds, shp, gt=gt) if scalebar: scale_ticks(ax, ds) pltlib.add_scalebar(ax, xres) if not ticks: pltlib.hide_ticks(ax) #imgplot.set_cmap(cmap) #imgplot.set_clim(clim) global gbma gbma = bma global ggt ggt = gt #Clicking on a subplot will make it active for z-coordinate display fig.canvas.mpl_connect('button_press_event', onclick) fig.canvas.mpl_connect('axes_enter_event', enter_axis) #Add support for interactive z-value display ax.format_coord = format_coord
def main(argv=None): parser = getparser() args = parser.parse_args() #Should check that files exist ref_dem_fn = args.ref_fn src_dem_fn = args.src_fn mode = args.mode mask_list = args.mask_list max_offset = args.max_offset max_dz = args.max_dz slope_lim = tuple(args.slope_lim) tiltcorr = args.tiltcorr polyorder = args.polyorder res = args.res #Maximum number of iterations max_iter = args.max_iter #These are tolerances (in meters) to stop iteration tol = args.tol min_dx = tol min_dy = tol min_dz = tol outdir = args.outdir if outdir is None: outdir = os.path.splitext(src_dem_fn)[0] + '_dem_align' if tiltcorr: outdir += '_tiltcorr' tiltcorr_done = False #Relax tolerance for initial round of co-registration #tiltcorr_tol = 0.1 #if tol < tiltcorr_tol: # tol = tiltcorr_tol if not os.path.exists(outdir): os.makedirs(outdir) outprefix = '%s_%s' % (os.path.splitext(os.path.split(src_dem_fn)[-1])[0], \ os.path.splitext(os.path.split(ref_dem_fn)[-1])[0]) outprefix = os.path.join(outdir, outprefix) print("\nReference: %s" % ref_dem_fn) print("Source: %s" % src_dem_fn) print("Mode: %s" % mode) print("Output: %s\n" % outprefix) src_dem_ds = gdal.Open(src_dem_fn) ref_dem_ds = gdal.Open(ref_dem_fn) #Get local cartesian coordinate system #local_srs = geolib.localtmerc_ds(src_dem_ds) #Use original source dataset coordinate system #Potentially issues with distortion and xyz/tiltcorr offsets for DEM with large extent local_srs = geolib.get_ds_srs(src_dem_ds) #local_srs = geolib.get_ds_srs(ref_dem_ds) #Resample to common grid ref_dem_res = float(geolib.get_res(ref_dem_ds, t_srs=local_srs, square=True)[0]) #Create a copy to be updated in place src_dem_ds_align = iolib.mem_drv.CreateCopy('', src_dem_ds, 0) src_dem_res = float(geolib.get_res(src_dem_ds, t_srs=local_srs, square=True)[0]) src_dem_ds = None #Resample to user-specified resolution ref_dem_ds, src_dem_ds_align = warplib.memwarp_multi([ref_dem_ds, src_dem_ds_align], \ extent='intersection', res=args.res, t_srs=local_srs, r='cubic') res = float(geolib.get_res(src_dem_ds_align, square=True)[0]) print("\nReference DEM res: %0.2f" % ref_dem_res) print("Source DEM res: %0.2f" % src_dem_res) print("Resolution for coreg: %s (%0.2f m)\n" % (args.res, res)) #Iteration number n = 1 #Cumulative offsets dx_total = 0 dy_total = 0 dz_total = 0 #Now iteratively update geotransform and vertical shift while True: print("*** Iteration %i ***" % n) dx, dy, dz, static_mask, fig = compute_offset(ref_dem_ds, src_dem_ds_align, src_dem_fn, mode, max_offset, \ mask_list=mask_list, max_dz=max_dz, slope_lim=slope_lim, plot=True) xyz_shift_str_iter = "dx=%+0.2fm, dy=%+0.2fm, dz=%+0.2fm" % (dx, dy, dz) print("Incremental offset: %s" % xyz_shift_str_iter) dx_total += dx dy_total += dy dz_total += dz xyz_shift_str_cum = "dx=%+0.2fm, dy=%+0.2fm, dz=%+0.2fm" % (dx_total, dy_total, dz_total) print("Cumulative offset: %s" % xyz_shift_str_cum) #String to append to output filenames xyz_shift_str_cum_fn = '_%s_x%+0.2f_y%+0.2f_z%+0.2f' % (mode, dx_total, dy_total, dz_total) #Should make an animation of this converging if n == 1: #static_mask_orig = static_mask if fig is not None: dst_fn = outprefix + '_%s_iter%02i_plot.png' % (mode, n) print("Writing offset plot: %s" % dst_fn) fig.gca().set_title("Incremental: %s\nCumulative: %s" % (xyz_shift_str_iter, xyz_shift_str_cum)) fig.savefig(dst_fn, dpi=300) #Apply the horizontal shift to the original dataset src_dem_ds_align = coreglib.apply_xy_shift(src_dem_ds_align, dx, dy, createcopy=False) #Should src_dem_ds_align = coreglib.apply_z_shift(src_dem_ds_align, dz, createcopy=False) n += 1 print("\n") #If magnitude of shift in all directions is less than tol #if n > max_iter or (abs(dx) <= min_dx and abs(dy) <= min_dy and abs(dz) <= min_dz): #If magnitude of shift is less than tol dm = np.sqrt(dx**2 + dy**2 + dz**2) dm_total = np.sqrt(dx_total**2 + dy_total**2 + dz_total**2) if dm_total > max_offset: sys.exit("Total offset exceeded specified max_offset (%0.2f m). Consider increasing -max_offset argument" % max_offset) #Stop iteration if n > max_iter or dm < tol: if fig is not None: dst_fn = outprefix + '_%s_iter%02i_plot.png' % (mode, n) print("Writing offset plot: %s" % dst_fn) fig.gca().set_title("Incremental:%s\nCumulative:%s" % (xyz_shift_str_iter, xyz_shift_str_cum)) fig.savefig(dst_fn, dpi=300) #Compute final elevation difference if True: ref_dem_clip_ds_align, src_dem_clip_ds_align = warplib.memwarp_multi([ref_dem_ds, src_dem_ds_align], \ res=res, extent='intersection', t_srs=local_srs, r='cubic') ref_dem_align = iolib.ds_getma(ref_dem_clip_ds_align, 1) src_dem_align = iolib.ds_getma(src_dem_clip_ds_align, 1) ref_dem_clip_ds_align = None diff_align = src_dem_align - ref_dem_align src_dem_align = None ref_dem_align = None #Get updated, final mask static_mask_final = get_mask(src_dem_clip_ds_align, mask_list, src_dem_fn) static_mask_final = np.logical_or(np.ma.getmaskarray(diff_align), static_mask_final) #Final stats, before outlier removal diff_align_compressed = diff_align[~static_mask_final] diff_align_stats = malib.get_stats_dict(diff_align_compressed, full=True) #Prepare filtered version for tiltcorr fit diff_align_filt = np.ma.array(diff_align, mask=static_mask_final) diff_align_filt = outlier_filter(diff_align_filt, f=3, max_dz=max_dz) #diff_align_filt = outlier_filter(diff_align_filt, perc=(12.5, 87.5), max_dz=max_dz) slope = get_filtered_slope(src_dem_clip_ds_align) diff_align_filt = np.ma.array(diff_align_filt, mask=np.ma.getmaskarray(slope)) diff_align_filt_stats = malib.get_stats_dict(diff_align_filt, full=True) #Fit 2D polynomial to residuals and remove #To do: add support for along-track and cross-track artifacts if tiltcorr and not tiltcorr_done: print("\n************") print("Calculating 'tiltcorr' 2D polynomial fit to residuals with order %i" % polyorder) print("************\n") gt = src_dem_clip_ds_align.GetGeoTransform() #Need to apply the mask here, so we're only fitting over static surfaces #Note that the origmask=False will compute vals for all x and y indices, which is what we want vals, resid, coeff = geolib.ma_fitpoly(diff_align_filt, order=polyorder, gt=gt, perc=(0,100), origmask=False) #vals, resid, coeff = geolib.ma_fitplane(diff_align_filt, gt, perc=(12.5, 87.5), origmask=False) #Should write out coeff or grid with correction vals_stats = malib.get_stats_dict(vals) #Want to have max_tilt check here #max_tilt = 4.0 #m #Should do percentage #vals.ptp() > max_tilt #Note: dimensions of ds and vals will be different as vals are computed for clipped intersection #Need to recompute planar offset for full src_dem_ds_align extent and apply xgrid, ygrid = geolib.get_xy_grids(src_dem_ds_align) valgrid = geolib.polyval2d(xgrid, ygrid, coeff) #For results of ma_fitplane #valgrid = coeff[0]*xgrid + coeff[1]*ygrid + coeff[2] src_dem_ds_align = coreglib.apply_z_shift(src_dem_ds_align, -valgrid, createcopy=False) if True: print("Creating plot of polynomial fit to residuals") fig, axa = plt.subplots(1,2, figsize=(8, 4)) dz_clim = malib.calcperc_sym(vals, (2, 98)) ax = pltlib.iv(diff_align_filt, ax=axa[0], cmap='RdBu', clim=dz_clim, \ label='Residual dz (m)', scalebar=False) ax = pltlib.iv(valgrid, ax=axa[1], cmap='RdBu', clim=dz_clim, \ label='Polyfit dz (m)', ds=src_dem_ds_align) #if tiltcorr: #xyz_shift_str_cum_fn += "_tiltcorr" tiltcorr_fig_fn = outprefix + '%s_polyfit.png' % xyz_shift_str_cum_fn print("Writing out figure: %s\n" % tiltcorr_fig_fn) fig.savefig(tiltcorr_fig_fn, dpi=300) print("Applying tilt correction to difference map") diff_align -= vals #Should iterate until tilts are below some threshold #For now, only do one tiltcorr tiltcorr_done=True #Now use original tolerance, and number of iterations tol = args.tol max_iter = n + args.max_iter else: break if True: #Write out aligned difference map for clipped extent with vertial offset removed align_diff_fn = outprefix + '%s_align_diff.tif' % xyz_shift_str_cum_fn print("Writing out aligned difference map with median vertical offset removed") iolib.writeGTiff(diff_align, align_diff_fn, src_dem_clip_ds_align) if True: #Write out fitered aligned difference map align_diff_filt_fn = outprefix + '%s_align_diff_filt.tif' % xyz_shift_str_cum_fn print("Writing out filtered aligned difference map with median vertical offset removed") iolib.writeGTiff(diff_align_filt, align_diff_filt_fn, src_dem_clip_ds_align) #Extract final center coordinates for intersection center_coord_ll = geolib.get_center(src_dem_clip_ds_align, t_srs=geolib.wgs_srs) center_coord_xy = geolib.get_center(src_dem_clip_ds_align) src_dem_clip_ds_align = None #Write out final aligned src_dem align_fn = outprefix + '%s_align.tif' % xyz_shift_str_cum_fn print("Writing out shifted src_dem with median vertical offset removed: %s" % align_fn) #Open original uncorrected dataset at native resolution src_dem_ds = gdal.Open(src_dem_fn) src_dem_ds_align = iolib.mem_drv.CreateCopy('', src_dem_ds, 0) #Apply final horizontal and vertial shift to the original dataset #Note: potentially issues if we used a different projection during coregistration! src_dem_ds_align = coreglib.apply_xy_shift(src_dem_ds_align, dx_total, dy_total, createcopy=False) src_dem_ds_align = coreglib.apply_z_shift(src_dem_ds_align, dz_total, createcopy=False) if tiltcorr: xgrid, ygrid = geolib.get_xy_grids(src_dem_ds_align) valgrid = geolib.polyval2d(xgrid, ygrid, coeff) #For results of ma_fitplane #valgrid = coeff[0]*xgrid + coeff[1]*ygrid + coeff[2] src_dem_ds_align = coreglib.apply_z_shift(src_dem_ds_align, -valgrid, createcopy=False) #Might be cleaner way to write out MEM ds directly to disk src_dem_full_align = iolib.ds_getma(src_dem_ds_align) iolib.writeGTiff(src_dem_full_align, align_fn, src_dem_ds_align) if True: #Output final aligned src_dem, masked so only best pixels are preserved #Useful if creating a new reference product #Can also use apply_mask.py print("Applying filter to shiftec src_dem") align_diff_filt_full_ds = warplib.memwarp_multi_fn([align_diff_filt_fn,], res=src_dem_ds_align, extent=src_dem_ds_align, \ t_srs=src_dem_ds_align)[0] align_diff_filt_full = iolib.ds_getma(align_diff_filt_full_ds) align_diff_filt_full_ds = None align_fn_masked = outprefix + '%s_align_filt.tif' % xyz_shift_str_cum_fn iolib.writeGTiff(np.ma.array(src_dem_full_align, mask=np.ma.getmaskarray(align_diff_filt_full)), \ align_fn_masked, src_dem_ds_align) src_dem_full_align = None src_dem_ds_align = None #Compute original elevation difference if True: ref_dem_clip_ds, src_dem_clip_ds = warplib.memwarp_multi([ref_dem_ds, src_dem_ds], \ res=res, extent='intersection', t_srs=local_srs, r='cubic') src_dem_ds = None ref_dem_ds = None ref_dem_orig = iolib.ds_getma(ref_dem_clip_ds) src_dem_orig = iolib.ds_getma(src_dem_clip_ds) #Needed for plotting ref_dem_hs = geolib.gdaldem_mem_ds(ref_dem_clip_ds, processing='hillshade', returnma=True, computeEdges=True) src_dem_hs = geolib.gdaldem_mem_ds(src_dem_clip_ds, processing='hillshade', returnma=True, computeEdges=True) diff_orig = src_dem_orig - ref_dem_orig #Only compute stats over valid surfaces static_mask_orig = get_mask(src_dem_clip_ds, mask_list, src_dem_fn) #Note: this doesn't include outlier removal or slope mask! static_mask_orig = np.logical_or(np.ma.getmaskarray(diff_orig), static_mask_orig) #For some reason, ASTER DEM diff have a spike near the 0 bin, could be an issue with masking? diff_orig_compressed = diff_orig[~static_mask_orig] diff_orig_stats = malib.get_stats_dict(diff_orig_compressed, full=True) #Prepare filtered version for comparison diff_orig_filt = np.ma.array(diff_orig, mask=static_mask_orig) diff_orig_filt = outlier_filter(diff_orig_filt, f=3, max_dz=max_dz) #diff_orig_filt = outlier_filter(diff_orig_filt, perc=(12.5, 87.5), max_dz=max_dz) slope = get_filtered_slope(src_dem_clip_ds) diff_orig_filt = np.ma.array(diff_orig_filt, mask=np.ma.getmaskarray(slope)) diff_orig_filt_stats = malib.get_stats_dict(diff_orig_filt, full=True) #Write out original difference map print("Writing out original difference map for common intersection before alignment") orig_diff_fn = outprefix + '_orig_diff.tif' iolib.writeGTiff(diff_orig, orig_diff_fn, ref_dem_clip_ds) src_dem_clip_ds = None ref_dem_clip_ds = None if True: align_stats_fn = outprefix + '%s_align_stats.json' % xyz_shift_str_cum_fn align_stats = {} align_stats['src_fn'] = src_dem_fn align_stats['ref_fn'] = ref_dem_fn align_stats['align_fn'] = align_fn align_stats['res'] = {} align_stats['res']['src'] = src_dem_res align_stats['res']['ref'] = ref_dem_res align_stats['res']['coreg'] = res align_stats['center_coord'] = {'lon':center_coord_ll[0], 'lat':center_coord_ll[1], \ 'x':center_coord_xy[0], 'y':center_coord_xy[1]} align_stats['shift'] = {'dx':dx_total, 'dy':dy_total, 'dz':dz_total, 'dm':dm_total} #This tiltcorr flag gets set to false, need better flag if tiltcorr: align_stats['tiltcorr'] = {} align_stats['tiltcorr']['coeff'] = coeff.tolist() align_stats['tiltcorr']['val_stats'] = vals_stats align_stats['before'] = diff_orig_stats align_stats['before_filt'] = diff_orig_filt_stats align_stats['after'] = diff_align_stats align_stats['after_filt'] = diff_align_filt_stats import json with open(align_stats_fn, 'w') as f: json.dump(align_stats, f) #Create output plot if True: print("Creating final plot") kwargs = {'interpolation':'none'} #f, axa = plt.subplots(2, 4, figsize=(11, 8.5)) f, axa = plt.subplots(2, 4, figsize=(16, 8)) for ax in axa.ravel()[:-1]: ax.set_facecolor('k') pltlib.hide_ticks(ax) dem_clim = malib.calcperc(ref_dem_orig, (2,98)) axa[0,0].imshow(ref_dem_hs, cmap='gray', **kwargs) im = axa[0,0].imshow(ref_dem_orig, cmap='cpt_rainbow', clim=dem_clim, alpha=0.6, **kwargs) pltlib.add_cbar(axa[0,0], im, arr=ref_dem_orig, clim=dem_clim, label=None) pltlib.add_scalebar(axa[0,0], res=res) axa[0,0].set_title('Reference DEM') axa[0,1].imshow(src_dem_hs, cmap='gray', **kwargs) im = axa[0,1].imshow(src_dem_orig, cmap='cpt_rainbow', clim=dem_clim, alpha=0.6, **kwargs) pltlib.add_cbar(axa[0,1], im, arr=src_dem_orig, clim=dem_clim, label=None) axa[0,1].set_title('Source DEM') #axa[0,2].imshow(~static_mask_orig, clim=(0,1), cmap='gray') axa[0,2].imshow(~static_mask, clim=(0,1), cmap='gray', **kwargs) axa[0,2].set_title('Surfaces for co-registration') dz_clim = malib.calcperc_sym(diff_orig_compressed, (5, 95)) im = axa[1,0].imshow(diff_orig, cmap='RdBu', clim=dz_clim) pltlib.add_cbar(axa[1,0], im, arr=diff_orig, clim=dz_clim, label=None) axa[1,0].set_title('Elev. Diff. Before (m)') im = axa[1,1].imshow(diff_align, cmap='RdBu', clim=dz_clim) pltlib.add_cbar(axa[1,1], im, arr=diff_align, clim=dz_clim, label=None) axa[1,1].set_title('Elev. Diff. After (m)') #tight_dz_clim = (-1.0, 1.0) tight_dz_clim = (-2.0, 2.0) #tight_dz_clim = (-10.0, 10.0) #tight_dz_clim = malib.calcperc_sym(diff_align_filt, (5, 95)) im = axa[1,2].imshow(diff_align_filt, cmap='RdBu', clim=tight_dz_clim) pltlib.add_cbar(axa[1,2], im, arr=diff_align_filt, clim=tight_dz_clim, label=None) axa[1,2].set_title('Elev. Diff. After (m)') #Tried to insert Nuth fig here #ax_nuth.change_geometry(1,2,1) #f.axes.append(ax_nuth) bins = np.linspace(dz_clim[0], dz_clim[1], 128) axa[1,3].hist(diff_orig_compressed, bins, color='g', label='Before', alpha=0.5) axa[1,3].hist(diff_align_compressed, bins, color='b', label='After', alpha=0.5) axa[1,3].set_xlim(*dz_clim) axa[1,3].axvline(0, color='k', linewidth=0.5, linestyle=':') axa[1,3].set_xlabel('Elev. Diff. (m)') axa[1,3].set_ylabel('Count (px)') axa[1,3].set_title("Source - Reference") before_str = 'Before\nmed: %0.2f\nnmad: %0.2f' % (diff_orig_stats['med'], diff_orig_stats['nmad']) axa[1,3].text(0.05, 0.95, before_str, va='top', color='g', transform=axa[1,3].transAxes, fontsize=8) after_str = 'After\nmed: %0.2f\nnmad: %0.2f' % (diff_align_stats['med'], diff_align_stats['nmad']) axa[1,3].text(0.65, 0.95, after_str, va='top', color='b', transform=axa[1,3].transAxes, fontsize=8) #This is empty axa[0,3].axis('off') suptitle = '%s\nx: %+0.2fm, y: %+0.2fm, z: %+0.2fm' % (os.path.split(outprefix)[-1], dx_total, dy_total, dz_total) f.suptitle(suptitle) f.tight_layout() plt.subplots_adjust(top=0.90) fig_fn = outprefix + '%s_align.png' % xyz_shift_str_cum_fn print("Writing out figure: %s" % fig_fn) f.savefig(fig_fn, dpi=300)
def main(): parser = get_parser() args = parser.parse_args() fn_list = args.fn_list print print "Reviewing %i images" % len(fn_list) print good = [] bad = [] good_fn = "good_list.txt" if args.prefix is not None: good_fn = args.prefix + '_' + good_fn good_f = open(good_fn, 'a', 0) bad_fn = "bad_list.txt" if args.prefix is not None: bad_fn = args.prefix + '_' + bad_fn bad_f = open(bad_fn, 'a', 0) fig = plt.figure() ax = fig.add_subplot(111) plt.ion() plt.show() #Use PIL Image basic = False for fn in fn_list: print fn plt.clf() if basic: im = mpimg.imread(fn) if im.ndim == 3: cmap = None plt.imshow(im, cmap=cmap) else: ds = gdal.Open(fn) a = iolib.gdal_getma_sub(ds) perc = malib.calcperc(a) cmap = 'cpt_rainbow' alpha = 1.0 if '_hs' in fn: cmap = 'gray' else: hs_fn = os.path.splitext(fn)[0] + '_hs.tif' if os.path.exists(hs_fn): hs_ds = gdal.Open(hs_fn) hs = iolib.gdal_getma_sub(hs_ds) hs_perc = malib.calcperc(hs) plt.imshow(hs, cmap='gray', clim=hs_perc) alpha = 0.5 plt.imshow(a, cmap=cmap, clim=perc, alpha=alpha) fig.canvas.draw() if query_yes_no("{} good?".format(fn)): good.append(fn) good_f.write("%s\n" % fn) else: bad.append(fn) bad_f.write("%s\n" % fn) plt.close() print print "Good: %i" % (len(good)) print good print print "Bad: %i" % (len(bad)) print bad print good_f.close() bad_f.close()
diff_euler_perc = 100.0 * diff_euler / dem1 dst_fn = os.path.join(outdir, outprefix + '_dz_eul_perc.tif') print dst_fn iolib.writeGTiff(diff_euler_perc, dst_fn, dem1_ds, ndv=diffndv) #def compute_dh_vs_z(ref_dem, dh, nbins=20, binwidth=None): if False: print "Compute dh/dt vs. elevation" nbins = 20 #binwidth = None binwidth = 50 ref_dem = dem1 dh = diff_euler if rates: dh = diff_euler / t_factor min, max = malib.calcperc(ref_dem) #If binwidth specified, override if binwidth is not None: nbins = None if nbins: edges = np.linspace(min, max, num=nbins) binwidth = edges[1] - edges[0] else: edges = np.arange(min, max, binwidth) diff_euler_bincenters = [] diff_euler_binvals = [] diff_euler_bincount = [] for i in range(edges.size - 1): print "%i of %i: %0.1f to %0.1f m" % (i, edges.size - 1, edges[i], edges[i + 1]) idx = np.logical_and((ref_dem > edges[i]).data,
continue glac_geom_mask = geolib.geom2mask(glac_geom, ds_list[0]) z1 = np.ma.array(iolib.ds_getma(ds_list[0]), mask=glac_geom_mask) z2 = np.ma.array(z2, mask=glac_geom_mask) dz = z2 - z1 if dz.count() == 0: print("No valid dz pixels") continue filter_outliers = True #Remove clearly bogus pixels if filter_outliers: #bad_perc = (0.1, 99.9) bad_perc = (1, 99) rangelim = malib.calcperc(dz, bad_perc) dz = np.ma.masked_outside(dz, *rangelim) ds_res = geolib.get_res(ds_list[0]) valid_area = dz.count() * ds_res[0] * ds_res[1] valid_area_perc = valid_area / glac_area min_valid_area_perc = 0.80 if valid_area_perc < min_valid_area_perc: print( "Not enough valid pixels. %0.1f%% percent of glacier polygon area" % (100 * valid_area_perc)) continue #Rasterize NED source dates if site == 'conus': z1_date_r_ds = iolib.mem_drv.CreateCopy('', ds_list[0])
def main(): parser = getparser() args = parser.parse_args() hs_overlay = args.hs_overlay kmz = args.kmz opacity = args.alpha cmap = args.cmap fn = args.fn print(fn) ds = gdal.Open(fn) b = ds.GetRasterBand(1) ndv = iolib.get_ndv_b(b) print("Loading input raster") a = iolib.b_getma(b) clim = args.clim if clim is None: clim = malib.calcperc(a, (2, 98)) print("Generating color ramp") cramp_fn = os.path.splitext(fn)[0]+'_ramp.txt' ncolors = 21 csteps = np.linspace(0, 1, ncolors) cm = plt.get_cmap(cmap) #Compute raster values between specified min/max vals = np.linspace(clim[0], clim[1], ncolors) #Compute rgba for these values on the given color ramp cvals = cm(csteps, bytes=True) #Combine into single array cramp = np.vstack((vals, cvals.T)).T #Set alpha to desired transparency cramp[:,-1] = opacity * 255 header = '#val r g b a' footer = 'nv %s %s %s 0' % (ndv, ndv, ndv) np.savetxt(cramp_fn, cramp, fmt='%f %i %i %i %i', header=header, footer=footer, comments='') print("Generating gdaldem color-relief tif") color_fn = os.path.splitext(fn)[0]+'_color.tif' if not os.path.exists(color_fn): #cmd = 'gdaldem color-relief -nearest_color_entry -alpha %s %s %s' % (fn, cramp_fn, color_fn) cmd = ['gdaldem', 'color-relief', '-alpha'] cmd.extend(iolib.gdal_opt_co) cmd.extend([fn, cramp_fn, color_fn]) print(' '.join(cmd)) subprocess.call(cmd, shell=False) if kmz: make_kmz(color_fn) if hs_overlay: print("Generating shaded relief") hs_fn = os.path.splitext(fn)[0]+'_hs_az315.tif' #Check to see if file exists, or if provided as input if not os.path.exists(hs_fn): cmd = ['gdaldem', 'hillshade'] #cmd.extend('-compute_edges') cmd.extend(iolib.gdal_opt_co) cmd.extend([fn, hs_fn]) print(' '.join(cmd)) subprocess.call(cmd, shell=False) print("Loading shaded relief and calculating percentile stretch") hs = iolib.fn_getma(hs_fn) hs_clim = malib.calcperc(hs, (1, 99)) #Since imagemagick was compiled with quantum depth 16, need to scale levels hs_clim = (hs_clim[0]*65535/255., hs_clim[1]*65535/255.) print("Generating color composite shaded relief") overlay_fn = os.path.splitext(color_fn)[0]+'_hs.tif' if not os.path.exists(overlay_fn): #Can also try hsvmerge.py #cmd = 'composite %s %s -dissolve "%i" %s' % (color_fn, hs_fn, opacity*100, overlay_fn) #This uses imagemagick composite function #For some reason, this level adjustment is not working #cmd = ['convert', hs_fn, color_fn, '-compose', 'dissolve', \ cmd = ['convert', hs_fn, '-level', '%i,%i' % hs_clim, color_fn, '-compose', 'dissolve', \ '-define', 'compose:args=%i' % int(opacity*100), '-composite', '-compress', 'LZW', overlay_fn] #cmd = ['composite', color_fn, hs_fn, '-dissolve', str(int(opacity*100)), '-compress', 'LZW', overlay_fn] print(' '.join(cmd)) subprocess.call(cmd, shell=False) print("Updating georeferencing metadata") out_ndv = 0 overlay_ds = gdal.Open(overlay_fn, gdal.GA_Update) overlay_ds.SetProjection(ds.GetProjection()) overlay_ds.SetGeoTransform(ds.GetGeoTransform()) for n in range(overlay_ds.RasterCount): overlay_ds.GetRasterBand(n+1).SetNoDataValue(out_ndv) overlay_ds = None #Rewrite with blocks and LZW-compression print("Creating tiled and compressed version") tmp_fn = '/tmp/temp_%s.tif' % os.getpid() cmd = ['gdal_translate',] cmd.extend(iolib.gdal_opt_co) cmd.extend((overlay_fn, tmp_fn)) print(' '.join(cmd)) subprocess.call(cmd, shell=False) shutil.move(tmp_fn, overlay_fn) if not os.path.exists(overlay_fn+'.ovr'): print("Generating overviews") cmd = ['gdaladdo', '-ro', '-r', 'average', '--config', \ 'COMPRESS_OVERVIEW', 'LZW', '--config', 'BIGTIFF_OVERVIEW', 'YES', \ overlay_fn, '2', '4', '8', '16', '32', '64'] print(' '.join(cmd)) subprocess.call(cmd, shell=False) if kmz: make_kmz(overlay_fn)