def main(): parser = getparser() args = parser.parse_args() src_fn = args.src_fn new_ndv = args.new_ndv #Input argument is a string, which is not recognized by set_fill_value #Must use np.nan object if new_ndv == 'nan' or new_ndv == 'np.nan': new_ndv = np.nan else: new_ndv = float(new_ndv) #Output filename will have ndv appended if args.overwrite: out_fn = src_fn else: out_fn = os.path.splitext(src_fn)[0] + '_ndv.tif' ds = gdal.Open(src_fn) b = ds.GetRasterBand(1) #Extract old ndv old_ndv = iolib.get_ndv_b(b) print(src_fn) print("Replacing old ndv %s with new ndv %s" % (old_ndv, new_ndv)) #Load masked array bma = iolib.ds_getma(ds) #Set new fill value bma.set_fill_value(new_ndv) #Fill ma with new value and write out iolib.writeGTiff(bma.filled(), out_fn, ds, ndv=new_ndv)
def warp(src_ds, res=None, extent=None, t_srs=None, r='cubic', driver=mem_drv, dst_fn=None, dst_ndv=None, verbose=True): """Warp an input dataset with predetermined arguments specifying output res/extent/srs This is the function that actually calls gdal.ReprojectImage Parameters ---------- src_ds : gdal.Dataset object Dataset to be warped res : float Desired output resolution extent : list of float Desired output extent in t_srs coordinate system t_srs : osr.SpatialReference() Desired output spatial reference r : str Desired resampling algorithm driver : GDAL Driver to use for warp Either MEM or GTiff dst_fn : str Output filename (for disk warp) dst_ndv : float Desired output NoData Value Returns ------- dst_ds : gdal.Dataset object Warped dataset (either in memory or on disk) """ src_srs = geolib.get_ds_srs(src_ds) if t_srs is None: t_srs = geolib.get_ds_srs(src_ds) src_gt = src_ds.GetGeoTransform() #Note: get_res returns [x_res, y_res] #Could just use gt here and average x_res and y_res src_res = geolib.get_res(src_ds, t_srs=t_srs, square=True)[0] if res is None: res = src_res if extent is None: extent = geolib.ds_geom_extent(src_ds, t_srs=t_srs) #Note: GDAL Lanczos creates block artifacts #Wait for gdalwarp to support gaussian resampling #Want to use Lanczos for downsampling #if src_res < res: # gra = gdal.GRA_Lanczos #See http://blog.codinghorror.com/better-image-resizing/ # Suggests cubic for downsampling, bilinear for upsampling # gra = gdal.GRA_Cubic #Cubic for upsampling #elif src_res >= res: # gra = gdal.GRA_Bilinear gra = parse_rs_alg(r) #At this point, the resolution and extent values must be float #Extent must be list res = float(res) extent = [float(i) for i in extent] #Might want to move this to memwarp_multi, keep memwarp basic w/ gdal.GRA types #Create progress function prog_func = None if verbose: prog_func = gdal.TermProgress if dst_fn is None: #This is a dummy fn if only in mem, but can be accessed later via GetFileList() #Actually, no, doesn't look like the filename survivies dst_fn = '' #Compute output image dimensions dst_nl = int(round((extent[3] - extent[1])/res)) dst_ns = int(round((extent[2] - extent[0])/res)) #dst_nl = int(math.ceil((extent[3] - extent[1])/res)) #dst_ns = int(math.ceil((extent[2] - extent[0])/res)) #dst_nl = int(math.floor((extent[3] - extent[1])/res)) #dst_ns = int(math.floor((extent[2] - extent[0])/res)) if verbose: print('nl: %i ns: %i res: %0.3f' % (dst_nl, dst_ns, res)) #Create output dataset src_b = src_ds.GetRasterBand(1) src_dt = src_b.DataType src_nl = src_ds.RasterYSize src_ns = src_ds.RasterXSize dst_ds = driver.Create(dst_fn, dst_ns, dst_nl, src_ds.RasterCount, src_dt) dst_ds.SetProjection(t_srs.ExportToWkt()) #Might be an issue to use src_gt rotation terms here with arbitrary extent/res dst_gt = [extent[0], res, src_gt[2], extent[3], src_gt[4], -res] dst_ds.SetGeoTransform(dst_gt) #This will smooth the input before downsampling to prevent aliasing, fill gaps #Pretty inefficent, as we need to create another intermediate dataset gauss = False for n in range(1, src_ds.RasterCount+1): if dst_ndv is None: src_b = src_ds.GetRasterBand(n) src_ndv = iolib.get_ndv_b(src_b) dst_ndv = src_ndv b = dst_ds.GetRasterBand(n) b.SetNoDataValue(dst_ndv) b.Fill(dst_ndv) if gauss: from pygeotools.lib import filtlib #src_a = src_b.GetVirtualMemArray() #Compute resampling ratio to determine filter window size res_ratio = float(res)/src_res if verbose: print("Resampling factor: %0.3f" % res_ratio) #Might be more efficient to do iterative gauss filter with size 3, rather than larger windows f_size = math.floor(res_ratio/2.)*2+1 #This is conservative to avoid filling holes with noise #f_size = math.floor(res_ratio/2.)*2-1 if f_size <= 1: continue if verbose: print("Smoothing window size: %i" % f_size) #Create temp dataset to store filtered array - avoid overwriting original temp_ds = driver.Create('', src_ns, src_nl, src_ds.RasterCount, src_dt) temp_ds.SetProjection(src_srs.ExportToWkt()) temp_ds.SetGeoTransform(src_gt) temp_b = temp_ds.GetRasterBand(n) temp_b.SetNoDataValue(dst_ndv) temp_b.Fill(dst_ndv) src_a = iolib.b_getma(src_b) src_a = filtlib.gauss_fltr_astropy(src_a, size=f_size) #Want to run with maskfill, so only fills gaps, without expanding isolated points temp_b.WriteArray(src_a) src_ds = temp_ds #In theory, NN should be fine since we already smoothed. In practice, cubic still provides slightly better results #gra = gdal.GRA_NearestNeighbour """ if not verbose: #Suppress GDAL progress bar orig_stdout = sys.stdout sys.stdout = open(os.devnull, 'w') """ #Note: default maxerror=0.0, second 0.0 argument gdal.ReprojectImage(src_ds, dst_ds, src_srs.ExportToWkt(), t_srs.ExportToWkt(), gra, 0.0, 0.0, prog_func) """ if not verbose: sys.stdout.close() sys.stdout = orig_stdout """ #Note: this is now done in diskwarp #Write out to disk #if driver != mem_drv: # dst_ds.FlushCache() #Return GDAL dataset object in memory return dst_ds
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)