cmap='gray', vmin=clim[0], vmax=clim[1], s=36) ax.set_xlabel('Pit snow depth (m)') ax.set_ylabel('Stereo2SWE snow depth (m)') ax.plot([0, 3], [0, 3], color='r', linewidth=0.5) ax.set_xlim(0, max_depth) ax.set_ylim(0, max_depth) fig_fn = 'snowex_gm_snowdepth_scatter.pdf' f.savefig(fig_fn, dpi=300, bbox_inches='tight') #Snow depth histogram of signed diff f, ax = plt.subplots() ax.hist(depth_diff, bins=64, range=(-max_depth, max_depth), color='k') malib.print_stats(depth_diff) ax.set_xlabel('Snow Depth Diff., Pit - DEM (m)') ax.set_ylabel('Count') fig_fn = 'snowex_gm_snowdepth_diff_hist.pdf' f.savefig(fig_fn, dpi=300, bbox_inches='tight') #Map plot of snow depth f, ax = plt.subplots() ax.set_aspect('equal') ax.set_facecolor('k') ax.imshow(hs, cmap='gray') #clim = malib.calcperc(depth, (2,98)) clim = (0, 2) im = ax.imshow(snowdepth, cmap='inferno', clim=clim, alpha=0.7) ax.set_ylim(dem.shape[0], 0) ax.set_xlim(0, dem.shape[1])
xy_srs=xy_srs, pad='glas', count=True) samp_idx = ~(np.ma.getmaskarray(samp[:, 0])) nsamp = samp_idx.nonzero()[0].size if nsamp == 0: sys.exit("No valid samples") else: pts_mask = np.ma.hstack([ pts[samp_idx], samp[samp_idx], (pts[samp_idx, zcol] - samp[samp_idx, 0])[:, np.newaxis] ]) print("Sample difference (raster - point) statistics:") malib.print_stats(pts_mask[:, -1]) """ #Update header out_hdr_str = None if hdr is not None: out_hdr = hdr + ['samp_med','samp_nmad','samp_count'] out_hdr_str = ', '.join(fieldnames) #Format for new cols #fmt = ['%0.2f', '%0.2f', '%i'] #fmt = None """ out_pt_fn = os.path.splitext(r_fn)[0] + '_' + os.path.splitext( os.path.split(pt_fn)[-1])[0] + '_sample.csv' print("\nWriting out %i points with valid samples:\n%s\n" %
def main(): parser = getparser() args = parser.parse_args() dem_fn = args.dem_fn # Write out because they will be used to mask CHM writeall = True # Auto compute min TOA with gaussian mixture model compute_min_toa = True #Basename for output files out_fn_base = os.path.splitext(dem_fn)[0] #Need some checks on these param = args.filt_param if param is not None and len(param) == 1: param = param[0] # Get original DEM dem = iolib.fn_getma(dem_fn) print("\nLoading input DEM into masked array") dem_ds = iolib.fn_getds(dem_fn) toa_mask = None toa_tri_mask = None # probably not used by itself; done as part of toa_mask rough_mask = None slope_mask = None mask_list = [toa_tri_mask, toa_mask, rough_mask, slope_mask] if args.filtdz: print( "\nFilter with dz from ref DEM to remove cloud returns and blunders (shadows)..." ) print("Reference DEM: %s" % os.path.split(param[0])[1]) print("Absolute dz (+/-): %s \n" % param[2]) #May need to cast input ma as float32 so np.nan filling works dem = dem.astype(np.float32) #Difference filter, need to specify ref_fn and range #Could let the user compute their own dz, then just run a standard range or absrange filter ref_fn = param[0] ref_ds = warplib.memwarp_multi_fn([ ref_fn, ], res=dem_ds, extent=dem_ds, t_srs=dem_ds)[0] ref = iolib.ds_getma(ref_ds) param = map(float, param[1:]) # A dem that has been masked based on the dz filter dem = filtlib.dz_fltr_ma(dem, ref, rangelim=param) if writeall: out_fn = os.path.splitext(dem_fn)[0] + '_dzfilt.tif' print("Writing out %s\n" % out_fn) iolib.writeGTiff(dem, out_fn, src_ds=dem_ds, ndv=args.ndv) #Initialize a control mask that we'll update #True (1) represents "valid" unmasked pixel, False (0) represents "invalid" pixel to be masked controlmask = ~(np.ma.getmaskarray(dem)) # DEM masking: Each block returns a masked output (not a mask) # TOA: mask dark and/or smooth areas (shadows and/or water) # Roughness # Slope if args.toamask or args.toatrimask: print("\nCompute TOA from ortho...\n") toa_fn = get_toa_fn(dem_fn) print("\nWarp TOA to DEM...\n") toa_ds = warplib.memwarp_multi_fn([ toa_fn, ], res=dem_ds, extent=dem_ds, t_srs=dem_ds)[0] if args.toamask: if compute_min_toa: # Compute a good min TOA value m, s = get_min_gaus(toa_fn, 50, 4) min_toa = m + s min_toa = m else: min_toa = args.min_toa with open(os.path.join(os.path.split(toa_fn)[0], "min_toa.txt"), "w") as text_file: text_file.write(os.path.basename(__file__)) text_file.write( "\nMinimum TOA used for mask:\n{0}".format(min_toa)) # Should mask dark areas and dilate toa_mask = get_toa_mask(toa_ds, min_toa) #Dilate the mask if args.dilate_toa is not None: niter = args.dilate_toa print("Dilating TOA mask with %i iterations" % niter) from scipy import ndimage toa_mask = ~(ndimage.morphology.binary_dilation( ~toa_mask, iterations=niter)) controlmask = np.logical_and(toa_mask, controlmask) # Mask islands here controlmask = malib.mask_islands(controlmask, 5) if writeall: out_fn = out_fn_base + '_toamask.tif' print("Writing out %s\n" % out_fn) iolib.writeGTiff(toa_mask, out_fn, src_ds=dem_ds) if args.toatrimask: # Should mask smooth areas (measures local variance) toa_tri_mask = get_tri_mask(toa_ds, args.min_toatri) controlmask = np.logical_and(toa_tri_mask, controlmask) if writeall: out_fn = out_fn_base + '_toatrimask.tif' print("Writing out %s\n" % out_fn) iolib.writeGTiff(toa_tri_mask, out_fn, src_ds=dem_ds) if args.slopemask: slope_mask = get_slope_mask(dem_ds, args.max_slope) controlmask = np.logical_and(slope_mask, controlmask) if writeall: out_fn = out_fn_base + '_slopemask.tif' print("Writing out %s\n" % out_fn) iolib.writeGTiff(slope_mask, out_fn, src_ds=dem_ds) # CHM mask will be a subset of the Control mask; slope_mask, toa_mask, toa_tri_mask chmmask = controlmask print("Generating final CHM mask to apply later") out_fn = out_fn_base + '_chmmask.tif' print("Writing out %s\n" % out_fn) iolib.writeGTiff(chmmask, out_fn, src_ds=dem_ds) if args.roughmask: rough_mask = get_rough_mask(dem_ds, args.max_rough) controlmask = np.logical_and(rough_mask, controlmask) if writeall: out_fn = out_fn_base + '_roughmask.tif' print("Writing out %s\n" % out_fn) iolib.writeGTiff(rough_mask, out_fn, src_ds=dem_ds) print( "Generating final mask to use for reference surfaces, and applying to input DEM" ) #Now invert to use to create final masked array controlmask = ~controlmask #Dilate the mask if args.dilate_con is not None: niter = args.dilate_con print("Dilating control mask with %i iterations" % niter) from scipy import ndimage controlmask = ~(ndimage.morphology.binary_dilation(~controlmask, iterations=niter)) #Apply mask to original DEM - use these surfaces for co-registration newdem = np.ma.array(dem, mask=controlmask) if True: print("\nStats of valid DEM with maskes applied:") valid_stats = malib.print_stats(newdem) valid_stats_med = valid_stats[5] print("\nWriting DEM control surfaces:") dst_fn = os.path.splitext(dem_fn)[0] + '_control.tif' print(dst_fn) iolib.writeGTiff(newdem, dst_fn, dem_ds) return dst_fn
def main(): parser = getparser() args = parser.parse_args() #This is output ndv, avoid using 0 for differences diffndv = -9999 r1_fn = args.fn1 r2_fn = args.fn2 if r1_fn == r2_fn: sys.exit('Input filenames are identical') fn_list = [r1_fn, r2_fn] outdir = args.outdir if outdir is None: outdir = os.path.dirname(os.path.abspath(r1_fn)) if not os.path.exists(outdir): os.makedirs(outdir) outprefix = os.path.splitext(os.path.split(r1_fn)[1])[0]+'_'+os.path.splitext(os.path.split(r2_fn)[1])[0] #Compute dz/dt rate if possible, in m/yr if args.rate: #Extract basename #This was a hack to work with timestamp array filenames that have geoid offset applied adj = '' if '-adj' in r1_fn: adj = '-adj' r1_fn_base = re.sub(adj, '', os.path.splitext(r1_fn)[0]) r2_fn_base = re.sub(adj, '', os.path.splitext(r2_fn)[0]) #Attempt to load ordinal timestamp arrays (for mosaics) if present """ import glob t1_fn = glob.glob(r1_fn_base+'*_ts*.tif') t2_fn = glob.glob(r1_fn_base+'*_ts*.tif') t_unit = 'year' """ t1_fn = r1_fn_base+'_ts.tif' t2_fn = r2_fn_base+'_ts.tif' t_unit = 'day' if not os.path.exists(t1_fn) and not os.path.exists(t2_fn): #Try to find processed output from r_mosaic index #These are decimal years t1_fn = r1_fn_base+'index_ts.tif' t2_fn = r2_fn_base+'index_ts.tif' t_unit = 'year' print(t1_fn, t2_fn) if os.path.exists(t1_fn) and os.path.exists(t2_fn): fn_list.extend([t1_fn, t2_fn]) else: #Attempt to extract timestamps from input filenames t1 = timelib.fn_getdatetime(r1_fn) t2 = timelib.fn_getdatetime(r2_fn) if t1 is not None and t2 is not None and t1 != t2: dt = t2 - t1 year = timedelta(days=365.25) t_factor = abs(dt.total_seconds()/year.total_seconds()) print("Time differences is %s, dh/%0.3f" % (dt, t_factor)) else: print("Unable to extract timestamps for input images") args.rate = False print("Warping rasters to same res/extent/proj") #This will check input param for validity, could do beforehand ds_list = warplib.memwarp_multi_fn(fn_list, extent=args.te, res=args.tr, t_srs=args.t_srs, r='cubic') r1_ds = ds_list[0] r2_ds = ds_list[1] print("Loading input rasters into masked arrays") r1 = iolib.ds_getma(r1_ds, 1) r2 = iolib.ds_getma(r2_ds, 1) #Compute relative difference print("Computing raster difference") diff = r2 - r1 #Check to make sure inputs actually intersect if diff.count() == 0: sys.exit("No valid overlap between input rasters") if len(fn_list) == 4: t1_ds = ds_list[2] t2_ds = ds_list[3] print("Loading timestamps into masked arrays") t1 = iolib.ds_getma(t1_ds) t2 = iolib.ds_getma(t2_ds) #Compute dt in years t_factor = t2 - t1 if t_unit == 'day': t_factor /= 365.25 if True: print("Raster difference stats:") diff_stats = malib.print_stats(diff) diff_med = diff_stats[5] if True: print("Writing raster difference map") dst_fn = os.path.join(outdir, outprefix+'_diff.tif') print(dst_fn) iolib.writeGTiff(diff, dst_fn, r1_ds, ndv=diffndv) if args.rate: print("Writing rate map") dst_fn = os.path.join(outdir, outprefix+'_diff_rate.tif') print(dst_fn) iolib.writeGTiff(diff/t_factor, dst_fn, r1_ds, ndv=diffndv) if len(fn_list) == 4: print("Writing time difference map") dst_fn = os.path.join(outdir, outprefix+'_diff_dt.tif') print(dst_fn) iolib.writeGTiff(t_factor, dst_fn, r1_ds, ndv=diffndv) if False: print("Writing relative raster difference map") diff_rel = diff - diff_med dst_fn = os.path.join(outdir, outprefix+'_diff_rel.tif') print(dst_fn) iolib.writeGTiff(diff_rel, dst_fn, r1_ds, ndv=diffndv) if False: print("Writing out raster2 with median difference removed") dst_fn = os.path.splitext(r2_fn)[0]+'_med'+diff_med+'.tif' print(dst_fn) iolib.writeGTiff(r2 - diff_med, dst_fn, r1_ds, ndv=diffndv) if False: print("Writing raster difference percentage map (relative to raster1)") diff_perc = 100.0*diff/r1 dst_fn = os.path.join(outdir, outprefix+'_diff_perc.tif') print(dst_fn) iolib.writeGTiff(diff_perc, dst_fn, r1_ds, ndv=diffndv)
#! /usr/bin/env python from osgeo import gdal from pygeotools.lib import iolib from pygeotools.lib import geolib from pygeotools.lib import malib def dist(pos1, pos2): return np.sqrt((pos1[0] - pos2[0])**2 + (pos1[1] - pos2[1])**2) pos1 = [595396.48277,5181880.22677] pos2 = [596168.611,5182875.521] fn = ('rainierlidar_wgs84_shpclip.tif') ds = iolib.fn_getds(fn) dem = iolib.ds_getma(ds) x, y = geolib.get_xy_grids(ds) d = dist(pos1, pos2) grid = np.array([x, y]) b = dist(pos1, grid) c = dist(pos2, grid) conv = np.rad2deg(np.arccos((b**2 + c**2 - d**2)/(2*b*c))) conv_m = np.ma.array(conv, mask=dem.mask) malib.print_stats(conv_m)
def main(): #This is defualt filter size (pixels) size = 19 #Compute and print stats before/after stats = False #Need to clean up with argparse #Accept filter list, filter size as argument if len(sys.argv) == 2: dem_fn = sys.argv[1] elif len(sys.argv) == 3: dem_fn = sys.argv[1] size = int(sys.argv[2]) else: sys.exit("Usage is %s dem [size]" % sys.argv[0]) print("Loading DEM into masked array") dem_ds = iolib.fn_getds(dem_fn) dem = iolib.ds_getma(dem_ds, 1) #Cast input ma as float32 so np.nan filling works #dem = dem.astype(np.float32) dem_fltr = dem #Should accept arbitrary number of ordered filter operations as cli argument #filt_list = ['gauss_fltr_astropy',] #for filt in filt_list: # dem_fltr = filtlib.filt(dem_fltr, size=size, kwargs) #Percentile filter #dem_fltr = filtlib.perc_fltr(dem, perc=(15.865, 84.135)) #dem_fltr = filtlib.perc_fltr(dem, perc=(2.275, 97.725)) #dem_fltr = filtlib.perc_fltr(dem, perc=(0.135, 99.865)) #dem_fltr = filtlib.perc_fltr(dem, perc=(0, 99.73)) #dem_fltr = filtlib.perc_fltr(dem, perc=(0, 95.45)) #dem_fltr = filtlib.perc_fltr(dem, perc=(0, 68.27)) #Difference filter, need to specify refdem_fn #dem_fltr = filtlib.dz_fltr(dem_fn, refdem_fn, abs_dz_lim=(0,30)) #Absolute range filter #dem_fltr = filtlib.range_fltr(dem_fltr, (15, 9999)) #Median filter #dem_fltr = filtlib.rolling_fltr(dem_fltr, f=np.nanmedian, size=size) #dem_fltr = filtlib.median_fltr(dem_fltr, fsize=size, origmask=True) #dem_fltr = filtlib.median_fltr_skimage(dem_fltr, radius=4, origmask=True) #High pass filter #dem_fltr = filtlib.highpass(dem_fltr, size=size) #Gaussian filter (default) dem_fltr = filtlib.gauss_fltr_astropy(dem_fltr, size=size, origmask=True, fill_interior=False) if stats: print("Input DEM stats:") malib.print_stats(dem) print("Filtered DEM stats:") malib.print_stats(dem_fltr) dst_fn = os.path.splitext(dem_fn)[0]+'_filt%ipx.tif' % size print("Writing out filtered DEM: %s" % dst_fn) #Note: writeGTiff writes dem_fltr.filled() iolib.writeGTiff(dem_fltr, dst_fn, dem_ds)
def main(): parser = getparser() args = parser.parse_args() #This is output ndv, avoid using 0 for differences diffndv = np.nan dem1_fn = args.dem1_fn dem2_fn = args.dem2_fn if dem1_fn == dem2_fn: sys.exit('Input filenames are identical') fn_list = [dem1_fn, dem2_fn] print("Warping DEMs to same res/extent/proj") dem1_ds, dem2_ds = warplib.memwarp_multi_fn(fn_list, extent=args.te, res=args.tr, t_srs=args.t_srs) print("Loading input DEMs into masked arrays") dem1 = iolib.ds_getma(dem1_ds) dem2 = iolib.ds_getma(dem2_ds) outdir = args.outdir if outdir is None: outdir = os.path.split(dem1_fn)[0] outprefix = os.path.splitext( os.path.split(dem1_fn)[1])[0] + '_' + os.path.splitext( os.path.split(dem2_fn)[1])[0] #Extract basename adj = '' if '-adj' in dem1_fn: adj = '-adj' dem1_fn_base = re.sub(adj, '', os.path.splitext(dem1_fn)[0]) dem2_fn_base = re.sub(adj, '', os.path.splitext(dem2_fn)[0]) #Check to make sure inputs actually intersect #Masked pixels are True if not np.any(~dem1.mask * ~dem2.mask): sys.exit("No valid overlap between input data") #Compute common mask print("Generating common mask") common_mask = malib.common_mask([dem1, dem2]) #Compute relative elevation difference with Eulerian approach print("Computing elevation difference with Eulerian approach") diff_euler = np.ma.array(dem2 - dem1, mask=common_mask) #Absolute range filter on the difference # removes differences outside of a range that likely arent related to canopy heights diff_euler = range_fltr(diff_euler, (-3, 30)) if True: print("Eulerian elevation difference stats:") diff_euler_stats = malib.print_stats(diff_euler) diff_euler_med = diff_euler_stats[5] if True: print("Writing Eulerian elevation difference map") dst_fn = os.path.join(outdir, outprefix + '_dz_eul.tif') print(dst_fn) iolib.writeGTiff(diff_euler, dst_fn, dem1_ds, ndv=diffndv) if False: print("Writing Eulerian relative elevation difference map") diff_euler_rel = diff_euler - diff_euler_med dst_fn = os.path.join(outdir, outprefix + '_dz_eul_rel.tif') print(dst_fn) iolib.writeGTiff(diff_euler_rel, dst_fn, dem1_ds, ndv=diffndv) if False: print("Writing out DEM2 with median elevation difference removed") dst_fn = os.path.splitext( dem2_fn)[0] + '_med' + diff_euler_med + '.tif' print(dst_fn) iolib.writeGTiff(dem2 - diff_euler_med, dst_fn, dem1_ds, ndv=diffndv) if False: print("Writing Eulerian elevation difference percentage map") 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) return dst_fn