def merge( ctx, files, output, driver, bounds, res, resampling, nodata, dtype, bidx, overwrite, precision, creation_options, ): """Copy valid pixels from input files to an output file. All files must have the same number of bands, data type, and coordinate reference system. Input files are merged in their listed order using the reverse painter's algorithm. If the output file exists, its values will be overwritten by input values. Geospatial bounds and resolution of a new output file in the units of the input file coordinate reference system may be provided and are otherwise taken from the first input file. Note: --res changed from 2 parameters in 0.25. \b --res 0.1 0.1 => --res 0.1 (square) --res 0.1 0.2 => --res 0.1 --res 0.2 (rectangular) """ from rasterio.merge import merge as merge_tool output, files = resolve_inout(files=files, output=output, overwrite=overwrite) resampling = Resampling[resampling] if driver: creation_options.update(driver=driver) with ctx.obj["env"]: merge_tool( files, bounds=bounds, res=res, nodata=nodata, dtype=dtype, indexes=(bidx or None), resampling=resampling, dst_path=output, dst_kwds=creation_options, )
def mergeRasters(files, outfile): """ Function takes a given list of file paths and merges to given output file """ print("Beginning merge of :\n\t%s" % ("\n\t".join(files))) with rio.open(files[0]) as ras: dtype = ras.read().dtype print(dtype) sources = [rio.open(f) for f in files] merged_array, output_transform = merge_tool(sources) profile = sources[0].profile profile['transform'] = output_transform profile['height'] = merged_array.shape[1] profile['width'] = merged_array.shape[2] profile.update(dtype=np.float32) #print(merged_array.dtype) #merged_array = merged_array.astype(dtype) #print(merged_array.dtype) # print(profile) print("Writing merged rasters out to %s outfile...") with rio.open(outfile, 'w', **profile) as dst: dst.write(merged_array.astype(np.float32)) return outfile
def merge(ctx, files, output, driver, bounds, res, resampling, nodata, bidx, overwrite, precision, creation_options): """Copy valid pixels from input files to an output file. All files must have the same number of bands, data type, and coordinate reference system. Input files are merged in their listed order using the reverse painter's algorithm. If the output file exists, its values will be overwritten by input values. Geospatial bounds and resolution of a new output file in the units of the input file coordinate reference system may be provided and are otherwise taken from the first input file. Note: --res changed from 2 parameters in 0.25. \b --res 0.1 0.1 => --res 0.1 (square) --res 0.1 0.2 => --res 0.1 --res 0.2 (rectangular) """ from rasterio.merge import merge as merge_tool output, files = resolve_inout(files=files, output=output, overwrite=overwrite) resampling = Resampling[resampling] # get integer code for method with ctx.obj['env']: datasets = [rasterio.open(f) for f in files] dest, output_transform = merge_tool(datasets, bounds=bounds, res=res, nodata=nodata, precision=precision, indexes=(bidx or None), resampling=resampling) profile = datasets[0].profile profile['transform'] = output_transform profile['height'] = dest.shape[1] profile['width'] = dest.shape[2] profile['driver'] = driver profile['count'] = dest.shape[0] if nodata is not None: profile['nodata'] = nodata profile.update(**creation_options) with rasterio.open(output, 'w', **profile) as dst: dst.write(dest) # uses the colormap in the first input raster. try: colormap = datasets[0].colormap(1) dst.write_colormap(1, colormap) except ValueError: pass
def merge_rasters(*in_paths, out_path): dest, xform = merge_tool([str(d) for d in in_paths]) with rasterio.open(in_paths[0], "r") as first: profile = first.profile profile["transform"] = xform profile["height"] = dest.shape[1] profile["width"] = dest.shape[2] profile["count"] = dest.shape[0] with rasterio.open(out_path, "w", **profile) as dst: dst.write(dest)
def merge(ctx, files, output, driver, bounds, res, nodata, bidx, overwrite, precision, creation_options): """Copy valid pixels from input files to an output file. All files must have the same number of bands, data type, and coordinate reference system. Input files are merged in their listed order using the reverse painter's algorithm. If the output file exists, its values will be overwritten by input values. Geospatial bounds and resolution of a new output file in the units of the input file coordinate reference system may be provided and are otherwise taken from the first input file. Note: --res changed from 2 parameters in 0.25. \b --res 0.1 0.1 => --res 0.1 (square) --res 0.1 0.2 => --res 0.1 --res 0.2 (rectangular) """ from rasterio.merge import merge as merge_tool output, files = resolve_inout( files=files, output=output, overwrite=overwrite) with ctx.obj['env']: datasets = [rasterio.open(f) for f in files] dest, output_transform = merge_tool(datasets, bounds=bounds, res=res, nodata=nodata, precision=precision, indexes=(bidx or None)) profile = datasets[0].profile profile['transform'] = output_transform profile['height'] = dest.shape[1] profile['width'] = dest.shape[2] profile['driver'] = driver profile['count'] = dest.shape[0] if nodata is not None: profile['nodata'] = nodata profile.update(**creation_options) with rasterio.open(output, 'w', **profile) as dst: dst.write(dest) # uses the colormap in the first input raster. try: colormap = datasets[0].colormap(1) dst.write_colormap(1, colormap) except ValueError: pass
def merge(ctx, files, output, driver, bounds, res, nodata, force_overwrite, precision, creation_options): """Copy valid pixels from input files to an output file. All files must have the same number of bands, data type, and coordinate reference system. Input files are merged in their listed order using the reverse painter's algorithm. If the output file exists, its values will be overwritten by input values. Geospatial bounds and resolution of a new output file in the units of the input file coordinate reference system may be provided and are otherwise taken from the first input file. Note: --res changed from 2 parameters in 0.25. \b --res 0.1 0.1 => --res 0.1 (square) --res 0.1 0.2 => --res 0.1 --res 0.2 (rectangular) """ from rasterio.merge import merge as merge_tool verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 output, files = resolve_inout(files=files, output=output, force_overwrite=force_overwrite) with Env(CPL_DEBUG=verbosity > 2) as env: sources = [rasterio.open(f) for f in files] dest, output_transform = merge_tool(sources, bounds=bounds, res=res, nodata=nodata, precision=precision) profile = sources[0].profile profile.pop('affine') profile['transform'] = output_transform profile['height'] = dest.shape[1] profile['width'] = dest.shape[2] profile['driver'] = driver profile.update(**creation_options) with rasterio.open(output, 'w', **profile) as dst: dst.write(dest)
def merge_tiles(alldirs, desired_dir, file_patterns, epsg=None): out_name = 'MOD09GA_{varname}_{date}_HMA{epsg}.tif'.format print('Merging tiles ...') for d in bar(alldirs): gtiffs = glob.glob(os.path.join(os.path.abspath(d), file_patterns)) date = datetime.datetime.strptime(d, 'modscag-historic/%Y/%j') with rio.Env(): output = out_name(varname=file_patterns.replace('*', '').replace( '.tif', ''), date='{:%Y_%m_%d}'.format(date), epsg='') sources = [rio.open(f) for f in gtiffs] data, output_transform = merge_tool(sources) profile = sources[0].profile profile.pop('affine') profile['transform'] = output_transform profile['height'] = data.shape[1] profile['width'] = data.shape[2] profile['driver'] = 'GTiff' profile['nodata'] = 255 print('Merged Profile:') print(profile) with rio.open(os.path.join(desired_dir, output), 'w', **profile) as dst: dst.write(data) if epsg: try: reproj_out = out_name(varname=file_patterns.replace( '*', '').replace('.tif', ''), date='{:%Y_%m_%d}'.format(date), epsg='_{}'.format(epsg)) print(output) reproj_tiff(os.path.join(desired_dir, output), os.path.join(desired_dir, reproj_out), epsg) except: print('Invalid EPSG Code. Go to http://epsg.io/') if os.path.exists(os.path.join(desired_dir, d)): shutil.rmtree(os.path.join(desired_dir, d)) shutil.copytree(d, os.path.join(desired_dir, d)) # Cleanup.. shutil.rmtree(os.path.dirname(os.path.dirname(alldirs[0])))
def mergeTile(today, merged, Gtiff_files): output = os.path.join(merged, '%s.tif' % (today)) with rio.Env(): sources = [rio.open(f) for f in Gtiff_files] data, output_transform = merge_tool(sources) profile = sources[0].profile profile.pop('affine') profile['transform'] = output_transform profile['height'] = data.shape[1] profile['width'] = data.shape[2] profile['driver'] = 'GTiff' print(profile) with rio.open(output, 'w', **profile) as dst: dst.write(data)
def mergeTile(today, merged, Gtiff_files): output = os.path.join(merged,'%s.tif' % (today)) with rio.Env(): sources = [rio.open(f) for f in Gtiff_files] data, output_transform = merge_tool(sources) profile = sources[0].profile profile.pop('affine') profile['transform'] = output_transform profile['height'] = data.shape[1] profile['width'] = data.shape[2] profile['driver'] = 'GTiff' print(profile) with rio.open(output, 'w', **profile) as dst: dst.write(data)
def merge(ctx, files, output, driver, bounds, res, nodata, force_overwrite, precision, creation_options): """Copy valid pixels from input files to an output file. All files must have the same number of bands, data type, and coordinate reference system. Input files are merged in their listed order using the reverse painter's algorithm. If the output file exists, its values will be overwritten by input values. Geospatial bounds and resolution of a new output file in the units of the input file coordinate reference system may be provided and are otherwise taken from the first input file. Note: --res changed from 2 parameters in 0.25. \b --res 0.1 0.1 => --res 0.1 (square) --res 0.1 0.2 => --res 0.1 --res 0.2 (rectangular) """ from rasterio.merge import merge as merge_tool verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 output, files = resolve_inout( files=files, output=output, force_overwrite=force_overwrite) with Env(CPL_DEBUG=verbosity > 2) as env: sources = [rasterio.open(f) for f in files] dest, output_transform = merge_tool(sources, bounds=bounds, res=res, nodata=nodata, precision=precision) profile = sources[0].profile profile.pop('affine') profile['transform'] = output_transform profile['height'] = dest.shape[1] profile['width'] = dest.shape[2] profile['driver'] = driver profile.update(**creation_options) with rasterio.open(output, 'w', **profile) as dst: dst.write(dest)
def merge_rasters(raster_input_folder, output_raster): input_files = [os.path.join(raster_input_folder, f) for f in os.listdir(raster_input_folder) if os.path.isfile(os.path.join(raster_input_folder, f))] sources = [rasterio.open(f) for f in input_files] dest, output_transform = merge_tool(sources) profile = sources[0].profile profile.pop('affine') profile['transform'] = output_transform profile['height'] = dest.shape[1] profile['width'] = dest.shape[2] with rasterio.open(output_raster, 'w', **profile) as dst: dst.write(dest)
def define_glacier_region(gdir, entity=None): """ Very first task: define the glacier's local grid. Defines the local projection (Transverse Mercator), centered on the glacier. There is some options to set the resolution of the local grid. It can be adapted depending on the size of the glacier with:: dx (m) = d1 * AREA (km) + d2 ; clipped to dmax or be set to a fixed value. See ``params.cfg`` for setting these options. Default values of the adapted mode lead to a resolution of 50 m for Hintereisferner, which is approx. 8 km2 large. After defining the grid, the topography and the outlines of the glacier are transformed into the local projection. The default interpolation for the topography is `cubic`. Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data entity : geopandas GeoSeries the glacier geometry to process """ # choose a spatial resolution with respect to the glacier area dxmethod = cfg.PARAMS['grid_dx_method'] area = gdir.rgi_area_km2 if dxmethod == 'linear': dx = np.rint(cfg.PARAMS['d1'] * area + cfg.PARAMS['d2']) elif dxmethod == 'square': dx = np.rint(cfg.PARAMS['d1'] * np.sqrt(area) + cfg.PARAMS['d2']) elif dxmethod == 'fixed': dx = np.rint(cfg.PARAMS['fixed_dx']) else: raise ValueError('grid_dx_method not supported: {}'.format(dxmethod)) # Additional trick for varying dx if dxmethod in ['linear', 'square']: dx = np.clip(dx, cfg.PARAMS['d2'], cfg.PARAMS['dmax']) log.debug('%s: area %.2f km, dx=%.1f', gdir.rgi_id, area, dx) # Make a local glacier map proj_params = dict(name='tmerc', lat_0=0., lon_0=gdir.cenlon, k=0.9996, x_0=0, y_0=0, datum='WGS84') proj4_str = "+proj={name} +lat_0={lat_0} +lon_0={lon_0} +k={k} " \ "+x_0={x_0} +y_0={y_0} +datum={datum}".format(**proj_params) proj_in = pyproj.Proj("+init=EPSG:4326", preserve_units=True) proj_out = pyproj.Proj(proj4_str, preserve_units=True) project = partial(pyproj.transform, proj_in, proj_out) # transform geometry to map geometry = shapely.ops.transform(project, entity['geometry']) geometry = _check_geometry(geometry) xx, yy = geometry.exterior.xy # Corners, incl. a buffer of N pix ulx = np.min(xx) - cfg.PARAMS['border'] * dx lrx = np.max(xx) + cfg.PARAMS['border'] * dx uly = np.max(yy) + cfg.PARAMS['border'] * dx lry = np.min(yy) - cfg.PARAMS['border'] * dx # n pixels nx = np.int((lrx - ulx) / dx) ny = np.int((uly - lry) / dx) # Back to lon, lat for DEM download/preparation tmp_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), x0y0=(ulx, uly), dxdy=(dx, -dx), pixel_ref='corner') minlon, maxlon, minlat, maxlat = tmp_grid.extent_in_crs(crs=salem.wgs84) # save transformed geometry to disk entity = entity.copy() entity['geometry'] = geometry # Avoid fiona bug: https://github.com/Toblerity/Fiona/issues/365 for k, s in entity.iteritems(): if type(s) in [np.int32, np.int64]: entity[k] = int(s) towrite = gpd.GeoDataFrame(entity).T towrite.crs = proj4_str # Delete the source before writing if 'DEM_SOURCE' in towrite: del towrite['DEM_SOURCE'] towrite.to_file(gdir.get_filepath('outlines')) # Also transform the intersects if necessary gdf = cfg.PARAMS['intersects_gdf'] gdf = gdf.loc[(gdf.RGIId_1 == gdir.rgi_id) | (gdf.RGIId_2 == gdir.rgi_id)] if len(gdf) > 0: gdf = salem.transform_geopandas(gdf, to_crs=proj_out) if hasattr(gdf.crs, 'srs'): # salem uses pyproj gdf.crs = gdf.crs.srs gdf.to_file(gdir.get_filepath('intersects')) # Open DEM source = entity.DEM_SOURCE if hasattr(entity, 'DEM_SOURCE') else None dem_list, dem_source = get_topo_file((minlon, maxlon), (minlat, maxlat), rgi_region=gdir.rgi_region, source=source) log.debug('%s: DEM source: %s', gdir.rgi_id, dem_source) # A glacier area can cover more than one tile: if len(dem_list) == 1: dem_dss = [rasterio.open(dem_list[0])] # if one tile, just open it dem_data = rasterio.band(dem_dss[0], 1) src_transform = dem_dss[0].transform else: dem_dss = [rasterio.open(s) for s in dem_list] # list of rasters dem_data, src_transform = merge_tool(dem_dss) # merged rasters # Use Grid properties to create a transform (see rasterio cookbook) dst_transform = rasterio.transform.from_origin( ulx, uly, dx, dx # sign change (2nd dx) is done by rasterio.transform ) # Set up profile for writing output profile = dem_dss[0].profile profile.update({ 'crs': proj4_str, 'transform': dst_transform, 'width': nx, 'height': ny }) # Could be extended so that the cfg file takes all Resampling.* methods if cfg.PARAMS['topo_interp'] == 'bilinear': resampling = Resampling.bilinear elif cfg.PARAMS['topo_interp'] == 'cubic': resampling = Resampling.cubic else: raise ValueError('{} interpolation not understood'.format( cfg.PARAMS['topo_interp'])) dem_reproj = gdir.get_filepath('dem') with rasterio.open(dem_reproj, 'w', **profile) as dest: dst_array = np.empty((ny, nx), dtype=dem_dss[0].dtypes[0]) reproject( # Source parameters source=dem_data, src_crs=dem_dss[0].crs, src_transform=src_transform, # Destination parameters destination=dst_array, dst_transform=dst_transform, dst_crs=proj4_str, # Configuration resampling=resampling) dest.write(dst_array, 1) for dem_ds in dem_dss: dem_ds.close() # Glacier grid x0y0 = (ulx + dx / 2, uly - dx / 2) # To pixel center coordinates glacier_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), dxdy=(dx, -dx), x0y0=x0y0) glacier_grid.to_json(gdir.get_filepath('glacier_grid')) gdir.write_pickle(dem_source, 'dem_source') # Looks in the database if the glacier has divides. gdf = cfg.PARAMS['divides_gdf'] if gdir.rgi_id in gdf.index.values: divdf = [g for g in gdf.loc[gdir.rgi_id].geometry] # Reproject the shape def proj(lon, lat): return salem.transform_proj(salem.wgs84, gdir.grid.proj, lon, lat) divdf = [shapely.ops.transform(proj, g) for g in divdf] # Keep only the ones large enough log.debug('%s: divide candidates: %d', gdir.rgi_id, len(divdf)) divdf = [g for g in divdf if (g.area >= (25 * dx**2))] log.debug('%s: number of divides: %d', gdir.rgi_id, len(divdf)) # Write the directories and the files for i, g in enumerate(divdf): _dir = os.path.join(gdir.dir, 'divide_{0:0=2d}'.format(i + 1)) if not os.path.exists(_dir): os.makedirs(_dir) # File entity['geometry'] = g towrite = gpd.GeoDataFrame(entity).T towrite.crs = proj4_str towrite.to_file(os.path.join(_dir, cfg.BASENAMES['outlines'])) else: # Make a single directory and link the files log.debug('%s: number of divides: %d', gdir.rgi_id, 1) _dir = os.path.join(gdir.dir, 'divide_01') if not os.path.exists(_dir): os.makedirs(_dir) linkname = os.path.join(_dir, cfg.BASENAMES['outlines']) sourcename = gdir.get_filepath('outlines') for ending in ['.cpg', '.dbf', '.shp', '.shx', '.prj']: _s = sourcename.replace('.shp', ending) _l = linkname.replace('.shp', ending) if os.path.exists(_s): try: # we are on UNIX os.link(_s, _l) except AttributeError: # we are on windows copyfile(_s, _l)
def define_nonrgi_glacier_region(gdir: NonRGIGlacierDirectory): """ Very first task: define the glacier's local grid. Defines the local projection (Transverse Mercator), centered on the glacier. The resolution of the local grid is dx. After defining the grid, the topography is transformed into the local projection. The default interpolation for the topography is `cubic`. Parameters ---------- gdir : :py:class:`oggm.NonRGIGlacierDirectory` where to write the data dx : float grid spacing """ xx, yy = gdir.extent_ll dx = gdir.case.dx # Make a local glacier map proj_params = dict(name='tmerc', lat_0=0., lon_0=gdir.cenlon, k=0.9996, x_0=0, y_0=0, datum='WGS84') proj4_str = "+proj={name} +lat_0={lat_0} +lon_0={lon_0} +k={k} " \ "+x_0={x_0} +y_0={y_0} +datum={datum}".format(**proj_params) # proj_in = pyproj.Proj("+init=EPSG:4326", preserve_units=True) proj_out = pyproj.Proj(proj4_str, preserve_units=True) merc_xx, merc_yy = salem.transform_proj(salem.wgs84, proj_out, xx, yy) # Corners, incl. a buffer of N pix ulx = np.min(merc_xx) lrx = np.max(merc_xx) uly = np.max(merc_yy) lry = np.min(merc_yy) # n pixels nx = np.int((lrx - ulx) / dx) ny = np.int((uly - lry) / dx) # Back to lon, lat for DEM download/preparation tmp_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), x0y0=(ulx, uly), dxdy=(dx, -dx), pixel_ref='corner') minlon, maxlon, minlat, maxlat = tmp_grid.extent_in_crs(crs=salem.wgs84) dem_list, dem_source = get_topo_file((minlon, maxlon), (minlat, maxlat), rgi_region=None, rgi_subregion=None, source='DEM3') log.debug('(%s) DEM source: %s', gdir.name, dem_source) # A glacier area can cover more than one tile: if len(dem_list) == 1: dem_dss = [rasterio.open(dem_list[0])] # if one tile, just open it dem_data = rasterio.band(dem_dss[0], 1) if LooseVersion(rasterio.__version__) >= LooseVersion('1.0'): src_transform = dem_dss[0].transform else: src_transform = dem_dss[0].affine else: dem_dss = [rasterio.open(s) for s in dem_list] # list of rasters dem_data, src_transform = merge_tool(dem_dss) # merged rasters # Use Grid properties to create a transform (see rasterio cookbook) dst_transform = rasterio.transform.from_origin( ulx, uly, dx, dx # sign change (2nd dx) is done by rasterio.transform ) # Set up profile for writing output profile = dem_dss[0].profile profile.update({ 'crs': proj4_str, 'transform': dst_transform, 'width': nx, 'height': ny }) # Could be extended so that the cfg file takes all Resampling.* methods if cfg.PARAMS['topo_interp'] == 'bilinear': resampling = Resampling.bilinear elif cfg.PARAMS['topo_interp'] == 'cubic': resampling = Resampling.cubic else: raise ValueError('{} interpolation not understood'.format( cfg.PARAMS['topo_interp'])) dem_reproj = gdir.get_filepath('dem') with rasterio.open(dem_reproj, 'w', **profile) as dest: dst_array = np.empty((ny, nx), dtype=dem_dss[0].dtypes[0]) reproject( # Source parameters source=dem_data, src_crs=dem_dss[0].crs, src_transform=src_transform, # Destination parameters destination=dst_array, dst_transform=dst_transform, dst_crs=proj4_str, # Configuration resampling=resampling) # TODO: ugly if gdir.case.name == 'Borden Peninsula': print('Anti icepatch used') dst_array[32, 27] = gdir.case.ela_h - 5 dst_array[:2, -4:] = gdir.case.ela_h - 5 if gdir.case.name == 'Borden Peninsula HR': print('Anti icepatch HR used') dst_array[-21:-16, 32:38] = gdir.case.ela_h - 5 dst_array[-8:-2, 88:98] = gdir.case.ela_h - 5 dst_array[:-109, 120:] = gdir.case.ela_h - 5 dest.write(dst_array, 1) for dem_ds in dem_dss: dem_ds.close() # Glacier grid x0y0 = (ulx + dx / 2, uly - dx / 2) # To pixel center coordinates glacier_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), dxdy=(dx, -dx), x0y0=x0y0) glacier_grid.to_json(gdir.get_filepath('glacier_grid')) # Write DEM source info source_txt = DEM_SOURCE_INFO.get(dem_source, dem_source) with open(gdir.get_filepath('dem_source'), 'w') as fw: fw.write(source_txt)
def define_glacier_region(gdir, entity=None): """Very first task: define the glacier's local grid. Defines the local projection (Transverse Mercator), centered on the glacier. There is some options to set the resolution of the local grid. It can be adapted depending on the size of the glacier with:: dx (m) = d1 * AREA (km) + d2 ; clipped to dmax or be set to a fixed value. See ``params.cfg`` for setting these options. Default values of the adapted mode lead to a resolution of 50 m for Hintereisferner, which is approx. 8 km2 large. After defining the grid, the topography and the outlines of the glacier are transformed into the local projection. The default interpolation for the topography is `cubic`. Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data entity : geopandas.GeoSeries the glacier geometry to process """ # Make a local glacier map proj_params = dict(name='tmerc', lat_0=0., lon_0=gdir.cenlon, k=0.9996, x_0=0, y_0=0, datum='WGS84') proj4_str = "+proj={name} +lat_0={lat_0} +lon_0={lon_0} +k={k} " \ "+x_0={x_0} +y_0={y_0} +datum={datum}".format(**proj_params) proj_in = pyproj.Proj("+init=EPSG:4326", preserve_units=True) proj_out = pyproj.Proj(proj4_str, preserve_units=True) project = partial(pyproj.transform, proj_in, proj_out) # transform geometry to map geometry = shapely.ops.transform(project, entity['geometry']) geometry = multi_to_poly(geometry, gdir=gdir) xx, yy = geometry.exterior.xy # Save transformed geometry to disk entity = entity.copy() entity['geometry'] = geometry # Avoid fiona bug: https://github.com/Toblerity/Fiona/issues/365 for k, s in entity.iteritems(): if type(s) in [np.int32, np.int64]: entity[k] = int(s) towrite = gpd.GeoDataFrame(entity).T towrite.crs = proj4_str # Delete the source before writing if 'DEM_SOURCE' in towrite: del towrite['DEM_SOURCE'] # Define glacier area to use area = entity['Area'] # Do we want to use the RGI area or ours? if not cfg.PARAMS['use_rgi_area']: area = geometry.area * 1e-6 entity['Area'] = area towrite['Area'] = area # Write shapefile gdir.write_shapefile(towrite, 'outlines') # Also transform the intersects if necessary gdf = cfg.PARAMS['intersects_gdf'] if len(gdf) > 0: gdf = gdf.loc[((gdf.RGIId_1 == gdir.rgi_id) | (gdf.RGIId_2 == gdir.rgi_id))] if len(gdf) > 0: gdf = salem.transform_geopandas(gdf, to_crs=proj_out) if hasattr(gdf.crs, 'srs'): # salem uses pyproj gdf.crs = gdf.crs.srs gdir.write_shapefile(gdf, 'intersects') else: # Sanity check if cfg.PARAMS['use_intersects']: raise InvalidParamsError('You seem to have forgotten to set the ' 'intersects file for this run. OGGM ' 'works better with such a file. If you ' 'know what your are doing, set ' "cfg.PARAMS['use_intersects'] = False to " "suppress this error.") # 6. choose a spatial resolution with respect to the glacier area dxmethod = cfg.PARAMS['grid_dx_method'] if dxmethod == 'linear': dx = np.rint(cfg.PARAMS['d1'] * area + cfg.PARAMS['d2']) elif dxmethod == 'square': dx = np.rint(cfg.PARAMS['d1'] * np.sqrt(area) + cfg.PARAMS['d2']) elif dxmethod == 'fixed': dx = np.rint(cfg.PARAMS['fixed_dx']) else: raise InvalidParamsError('grid_dx_method not supported: {}' .format(dxmethod)) # Additional trick for varying dx if dxmethod in ['linear', 'square']: dx = np.clip(dx, cfg.PARAMS['d2'], cfg.PARAMS['dmax']) log.debug('(%s) area %.2f km, dx=%.1f', gdir.rgi_id, area, dx) # Safety check border = cfg.PARAMS['border'] if border > 1000: raise InvalidParamsError("You have set a cfg.PARAMS['border'] value " "of {}. ".format(cfg.PARAMS['border']) + 'This a very large value, which is ' 'currently not supported in OGGM.') # For tidewater glaciers we force border to 10 if gdir.is_tidewater and cfg.PARAMS['clip_tidewater_border']: border = 10 # Corners, incl. a buffer of N pix ulx = np.min(xx) - border * dx lrx = np.max(xx) + border * dx uly = np.max(yy) + border * dx lry = np.min(yy) - border * dx # n pixels nx = np.int((lrx - ulx) / dx) ny = np.int((uly - lry) / dx) # Back to lon, lat for DEM download/preparation tmp_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), x0y0=(ulx, uly), dxdy=(dx, -dx), pixel_ref='corner') minlon, maxlon, minlat, maxlat = tmp_grid.extent_in_crs(crs=salem.wgs84) # Open DEM source = entity.DEM_SOURCE if hasattr(entity, 'DEM_SOURCE') else None dem_list, dem_source = get_topo_file((minlon, maxlon), (minlat, maxlat), rgi_region=gdir.rgi_region, rgi_subregion=gdir.rgi_subregion, source=source) log.debug('(%s) DEM source: %s', gdir.rgi_id, dem_source) log.debug('(%s) N DEM Files: %s', gdir.rgi_id, len(dem_list)) # A glacier area can cover more than one tile: if len(dem_list) == 1: dem_dss = [rasterio.open(dem_list[0])] # if one tile, just open it dem_data = rasterio.band(dem_dss[0], 1) if LooseVersion(rasterio.__version__) >= LooseVersion('1.0'): src_transform = dem_dss[0].transform else: src_transform = dem_dss[0].affine else: dem_dss = [rasterio.open(s) for s in dem_list] # list of rasters dem_data, src_transform = merge_tool(dem_dss) # merged rasters # Use Grid properties to create a transform (see rasterio cookbook) dst_transform = rasterio.transform.from_origin( ulx, uly, dx, dx # sign change (2nd dx) is done by rasterio.transform ) # Set up profile for writing output profile = dem_dss[0].profile profile.update({ 'crs': proj4_str, 'transform': dst_transform, 'width': nx, 'height': ny }) # Could be extended so that the cfg file takes all Resampling.* methods if cfg.PARAMS['topo_interp'] == 'bilinear': resampling = Resampling.bilinear elif cfg.PARAMS['topo_interp'] == 'cubic': resampling = Resampling.cubic else: raise InvalidParamsError('{} interpolation not understood' .format(cfg.PARAMS['topo_interp'])) dem_reproj = gdir.get_filepath('dem') profile.pop('blockxsize', None) profile.pop('blockysize', None) profile.pop('compress', None) nodata = dem_dss[0].meta.get('nodata', None) if source == 'TANDEM' and nodata is None: # badly tagged geotiffs, let's do it ourselves nodata = -32767 with rasterio.open(dem_reproj, 'w', **profile) as dest: dst_array = np.empty((ny, nx), dtype=dem_dss[0].dtypes[0]) reproject( # Source parameters source=dem_data, src_crs=dem_dss[0].crs, src_transform=src_transform, src_nodata=nodata, # Destination parameters destination=dst_array, dst_transform=dst_transform, dst_crs=proj4_str, dst_nodata=nodata, # Configuration resampling=resampling) dest.write(dst_array, 1) for dem_ds in dem_dss: dem_ds.close() # Glacier grid x0y0 = (ulx+dx/2, uly-dx/2) # To pixel center coordinates glacier_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), dxdy=(dx, -dx), x0y0=x0y0) glacier_grid.to_json(gdir.get_filepath('glacier_grid')) # Write DEM source info gdir.add_to_diagnostics('dem_source', dem_source) source_txt = DEM_SOURCE_INFO.get(dem_source, dem_source) with open(gdir.get_filepath('dem_source'), 'w') as fw: fw.write(source_txt) fw.write('\n\n') fw.write('# Data files\n\n') for fname in dem_list: fw.write('{}\n'.format(os.path.basename(fname)))
def define_glacier_region(gdir, entity=None): """ Very first task: define the glacier's local grid. Defines the local projection (Transverse Mercator), centered on the glacier. There is some options to set the resolution of the local grid. It can be adapted depending on the size of the glacier with:: dx (m) = d1 * AREA (km) + d2 ; clipped to dmax or be set to a fixed value. See ``params.cfg`` for setting these options. Default values of the adapted mode lead to a resolution of 50 m for Hintereisferner, which is approx. 8 km2 large. After defining the grid, the topography and the outlines of the glacier are transformed into the local projection. The default interpolation for the topography is `cubic`. Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data entity : geopandas GeoSeries the glacier geometry to process """ # choose a spatial resolution with respect to the glacier area dxmethod = cfg.PARAMS['grid_dx_method'] area = gdir.rgi_area_km2 if dxmethod == 'linear': dx = np.rint(cfg.PARAMS['d1'] * area + cfg.PARAMS['d2']) elif dxmethod == 'square': dx = np.rint(cfg.PARAMS['d1'] * np.sqrt(area) + cfg.PARAMS['d2']) elif dxmethod == 'fixed': dx = np.rint(cfg.PARAMS['fixed_dx']) else: raise ValueError('grid_dx_method not supported: {}'.format(dxmethod)) # Additional trick for varying dx if dxmethod in ['linear', 'square']: dx = np.clip(dx, cfg.PARAMS['d2'], cfg.PARAMS['dmax']) log.debug('%s: area %.2f km, dx=%.1f', gdir.rgi_id, area, dx) # Make a local glacier map proj_params = dict(name='tmerc', lat_0=0., lon_0=gdir.cenlon, k=0.9996, x_0=0, y_0=0, datum='WGS84') proj4_str = "+proj={name} +lat_0={lat_0} +lon_0={lon_0} +k={k} " \ "+x_0={x_0} +y_0={y_0} +datum={datum}".format(**proj_params) proj_in = pyproj.Proj("+init=EPSG:4326", preserve_units=True) proj_out = pyproj.Proj(proj4_str, preserve_units=True) project = partial(pyproj.transform, proj_in, proj_out) # transform geometry to map geometry = shapely.ops.transform(project, entity['geometry']) geometry = _check_geometry(geometry) xx, yy = geometry.exterior.xy # Corners, incl. a buffer of N pix ulx = np.min(xx) - cfg.PARAMS['border'] * dx lrx = np.max(xx) + cfg.PARAMS['border'] * dx uly = np.max(yy) + cfg.PARAMS['border'] * dx lry = np.min(yy) - cfg.PARAMS['border'] * dx # n pixels nx = np.int((lrx - ulx) / dx) ny = np.int((uly - lry) / dx) # Back to lon, lat for DEM download/preparation tmp_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), x0y0=(ulx, uly), dxdy=(dx, -dx), pixel_ref='corner') minlon, maxlon, minlat, maxlat = tmp_grid.extent_in_crs(crs=salem.wgs84) # save transformed geometry to disk entity = entity.copy() entity['geometry'] = geometry # Avoid fiona bug: https://github.com/Toblerity/Fiona/issues/365 for k, s in entity.iteritems(): if type(s) in [np.int32, np.int64]: entity[k] = int(s) towrite = gpd.GeoDataFrame(entity).T towrite.crs = proj4_str # Delete the source before writing if 'DEM_SOURCE' in towrite: del towrite['DEM_SOURCE'] towrite.to_file(gdir.get_filepath('outlines')) # Also transform the intersects if necessary gdf = cfg.PARAMS['intersects_gdf'] gdf = gdf.loc[(gdf.RGIId_1 == gdir.rgi_id) | (gdf.RGIId_2 == gdir.rgi_id)] if len(gdf) > 0: gdf = salem.transform_geopandas(gdf, to_crs=proj_out) if hasattr(gdf.crs, 'srs'): # salem uses pyproj gdf.crs = gdf.crs.srs gdf.to_file(gdir.get_filepath('intersects')) # Open DEM source = entity.DEM_SOURCE if hasattr(entity, 'DEM_SOURCE') else None dem_list, dem_source = get_topo_file((minlon, maxlon), (minlat, maxlat), rgi_region=gdir.rgi_region, source=source) log.debug('%s: DEM source: %s', gdir.rgi_id, dem_source) # A glacier area can cover more than one tile: if len(dem_list) == 1: dem_dss = [rasterio.open(dem_list[0])] # if one tile, just open it dem_data = rasterio.band(dem_dss[0], 1) if LooseVersion(rasterio.__version__) >= LooseVersion('1.0'): src_transform = dem_dss[0].transform else: src_transform = dem_dss[0].affine else: dem_dss = [rasterio.open(s) for s in dem_list] # list of rasters dem_data, src_transform = merge_tool(dem_dss) # merged rasters # Use Grid properties to create a transform (see rasterio cookbook) dst_transform = rasterio.transform.from_origin( ulx, uly, dx, dx # sign change (2nd dx) is done by rasterio.transform ) # Set up profile for writing output profile = dem_dss[0].profile profile.update({ 'crs': proj4_str, 'transform': dst_transform, 'width': nx, 'height': ny }) # Could be extended so that the cfg file takes all Resampling.* methods if cfg.PARAMS['topo_interp'] == 'bilinear': resampling = Resampling.bilinear elif cfg.PARAMS['topo_interp'] == 'cubic': resampling = Resampling.cubic else: raise ValueError('{} interpolation not understood' .format(cfg.PARAMS['topo_interp'])) dem_reproj = gdir.get_filepath('dem') with rasterio.open(dem_reproj, 'w', **profile) as dest: dst_array = np.empty((ny, nx), dtype=dem_dss[0].dtypes[0]) reproject( # Source parameters source=dem_data, src_crs=dem_dss[0].crs, src_transform=src_transform, # Destination parameters destination=dst_array, dst_transform=dst_transform, dst_crs=proj4_str, # Configuration resampling=resampling) dest.write(dst_array, 1) for dem_ds in dem_dss: dem_ds.close() # Glacier grid x0y0 = (ulx+dx/2, uly-dx/2) # To pixel center coordinates glacier_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), dxdy=(dx, -dx), x0y0=x0y0) glacier_grid.to_json(gdir.get_filepath('glacier_grid')) gdir.write_pickle(dem_source, 'dem_source') # Looks in the database if the glacier has divides. gdf = cfg.PARAMS['divides_gdf'] if gdir.rgi_id in gdf.index.values: div_gdf = gdf.loc[gdir.rgi_id] # Compute the intersections between them for later bedshapes gdf_inter = polygon_intersections(div_gdf) if len(gdf_inter) > 0: if hasattr(div_gdf.crs, 'srs'): # salem uses pyproj gdf_inter.crs = div_gdf.crs.srs else: gdf_inter.crs = div_gdf.crs gdf_inter.to_file(gdir.get_filepath('divides_intersects')) # Ok go on divlist = [g for g in div_gdf.geometry] # Reproject the shape def proj(lon, lat): return salem.transform_proj(salem.wgs84, gdir.grid.proj, lon, lat) divlist = [shapely.ops.transform(proj, g) for g in divlist] # Keep only the ones large enough log.debug('%s: divide candidates: %d', gdir.rgi_id, len(divlist)) divlist = [g for g in divlist if (g.area >= (50 * dx ** 2))] log.debug('%s: number of divides: %d', gdir.rgi_id, len(divlist)) divlist = np.asarray(divlist) # Sort them by area divlist = divlist[np.argsort([g.area for g in divlist])[::-1]] # Write the directories and the files for i, g in enumerate(divlist): _dir = os.path.join(gdir.dir, 'divide_{0:0=2d}'.format(i + 1)) if not os.path.exists(_dir): os.makedirs(_dir) # File entity['geometry'] = g towrite = gpd.GeoDataFrame(entity).T towrite.crs = proj4_str towrite.to_file(os.path.join(_dir, cfg.BASENAMES['outlines'])) else: # Make a single directory and link the files log.debug('%s: number of divides: %d', gdir.rgi_id, 1) _dir = os.path.join(gdir.dir, 'divide_01') if not os.path.exists(_dir): os.makedirs(_dir) linkname = os.path.join(_dir, cfg.BASENAMES['outlines']) sourcename = gdir.get_filepath('outlines') for ending in ['.cpg', '.dbf', '.shp', '.shx', '.prj']: _s = sourcename.replace('.shp', ending) _l = linkname.replace('.shp', ending) if os.path.exists(_s): try: # we are on UNIX os.link(_s, _l) except AttributeError: # we are on windows copyfile(_s, _l)
def define_glacier_region(gdir, entity=None): """Very first task: define the glacier's local grid. Defines the local projection (Transverse Mercator), centered on the glacier. There is some options to set the resolution of the local grid. It can be adapted depending on the size of the glacier with:: dx (m) = d1 * AREA (km) + d2 ; clipped to dmax or be set to a fixed value. See ``params.cfg`` for setting these options. Default values of the adapted mode lead to a resolution of 50 m for Hintereisferner, which is approx. 8 km2 large. After defining the grid, the topography and the outlines of the glacier are transformed into the local projection. The default interpolation for the topography is `cubic`. Parameters ---------- gdir : :py:class:`oggm.GlacierDirectory` where to write the data entity : geopandas.GeoSeries the glacier geometry to process """ # Make a local glacier map proj_params = dict(name='tmerc', lat_0=0., lon_0=gdir.cenlon, k=0.9996, x_0=0, y_0=0, datum='WGS84') proj4_str = "+proj={name} +lat_0={lat_0} +lon_0={lon_0} +k={k} " \ "+x_0={x_0} +y_0={y_0} +datum={datum}".format(**proj_params) proj_in = pyproj.Proj("+init=EPSG:4326", preserve_units=True) proj_out = pyproj.Proj(proj4_str, preserve_units=True) project = partial(pyproj.transform, proj_in, proj_out) # transform geometry to map geometry = shapely.ops.transform(project, entity['geometry']) geometry = multi_to_poly(geometry, gdir=gdir) xx, yy = geometry.exterior.xy # Save transformed geometry to disk entity = entity.copy() entity['geometry'] = geometry # Avoid fiona bug: https://github.com/Toblerity/Fiona/issues/365 for k, s in entity.iteritems(): if type(s) in [np.int32, np.int64]: entity[k] = int(s) towrite = gpd.GeoDataFrame(entity).T towrite.crs = proj4_str # Delete the source before writing if 'DEM_SOURCE' in towrite: del towrite['DEM_SOURCE'] # Define glacier area to use area = entity['Area'] # Do we want to use the RGI area or ours? if not cfg.PARAMS['use_rgi_area']: area = geometry.area * 1e-6 entity['Area'] = area towrite['Area'] = area # Write shapefile gdir.write_shapefile(towrite, 'outlines') # Also transform the intersects if necessary gdf = cfg.PARAMS['intersects_gdf'] if len(gdf) > 0: gdf = gdf.loc[((gdf.RGIId_1 == gdir.rgi_id) | (gdf.RGIId_2 == gdir.rgi_id))] if len(gdf) > 0: gdf = salem.transform_geopandas(gdf, to_crs=proj_out) if hasattr(gdf.crs, 'srs'): # salem uses pyproj gdf.crs = gdf.crs.srs gdir.write_shapefile(gdf, 'intersects') else: # Sanity check if cfg.PARAMS['use_intersects']: raise InvalidParamsError('You seem to have forgotten to set the ' 'intersects file for this run. OGGM ' 'works better with such a file. If you ' 'know what your are doing, set ' "cfg.PARAMS['use_intersects'] = False to " "suppress this error.") # 6. choose a spatial resolution with respect to the glacier area dxmethod = cfg.PARAMS['grid_dx_method'] if dxmethod == 'linear': dx = np.rint(cfg.PARAMS['d1'] * area + cfg.PARAMS['d2']) elif dxmethod == 'square': dx = np.rint(cfg.PARAMS['d1'] * np.sqrt(area) + cfg.PARAMS['d2']) elif dxmethod == 'fixed': dx = np.rint(cfg.PARAMS['fixed_dx']) else: raise InvalidParamsError( 'grid_dx_method not supported: {}'.format(dxmethod)) # Additional trick for varying dx if dxmethod in ['linear', 'square']: dx = np.clip(dx, cfg.PARAMS['d2'], cfg.PARAMS['dmax']) log.debug('(%s) area %.2f km, dx=%.1f', gdir.rgi_id, area, dx) # Safety check border = cfg.PARAMS['border'] if border > 1000: raise InvalidParamsError("You have set a cfg.PARAMS['border'] value " "of {}. ".format(cfg.PARAMS['border']) + 'This a very large value, which is ' 'currently not supported in OGGM.') # For tidewater glaciers we force border to 10 if gdir.is_tidewater and cfg.PARAMS['clip_tidewater_border']: border = 10 # Corners, incl. a buffer of N pix ulx = np.min(xx) - border * dx lrx = np.max(xx) + border * dx uly = np.max(yy) + border * dx lry = np.min(yy) - border * dx # n pixels nx = np.int((lrx - ulx) / dx) ny = np.int((uly - lry) / dx) # Back to lon, lat for DEM download/preparation tmp_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), x0y0=(ulx, uly), dxdy=(dx, -dx), pixel_ref='corner') minlon, maxlon, minlat, maxlat = tmp_grid.extent_in_crs(crs=salem.wgs84) # Open DEM source = entity.DEM_SOURCE if hasattr(entity, 'DEM_SOURCE') else None dem_list, dem_source = get_topo_file((minlon, maxlon), (minlat, maxlat), rgi_region=gdir.rgi_region, rgi_subregion=gdir.rgi_subregion, source=source) log.debug('(%s) DEM source: %s', gdir.rgi_id, dem_source) log.debug('(%s) N DEM Files: %s', gdir.rgi_id, len(dem_list)) # A glacier area can cover more than one tile: if len(dem_list) == 1: dem_dss = [rasterio.open(dem_list[0])] # if one tile, just open it dem_data = rasterio.band(dem_dss[0], 1) if LooseVersion(rasterio.__version__) >= LooseVersion('1.0'): src_transform = dem_dss[0].transform else: src_transform = dem_dss[0].affine else: dem_dss = [rasterio.open(s) for s in dem_list] # list of rasters dem_data, src_transform = merge_tool(dem_dss) # merged rasters # Use Grid properties to create a transform (see rasterio cookbook) dst_transform = rasterio.transform.from_origin( ulx, uly, dx, dx # sign change (2nd dx) is done by rasterio.transform ) # Set up profile for writing output profile = dem_dss[0].profile profile.update({ 'crs': proj4_str, 'transform': dst_transform, 'width': nx, 'height': ny }) # Could be extended so that the cfg file takes all Resampling.* methods if cfg.PARAMS['topo_interp'] == 'bilinear': resampling = Resampling.bilinear elif cfg.PARAMS['topo_interp'] == 'cubic': resampling = Resampling.cubic else: raise InvalidParamsError('{} interpolation not understood'.format( cfg.PARAMS['topo_interp'])) dem_reproj = gdir.get_filepath('dem') profile.pop('blockxsize', None) profile.pop('blockysize', None) profile.pop('compress', None) nodata = dem_dss[0].meta.get('nodata', None) if source == 'TANDEM' and nodata is None: # badly tagged geotiffs, let's do it ourselves nodata = -32767 with rasterio.open(dem_reproj, 'w', **profile) as dest: dst_array = np.empty((ny, nx), dtype=dem_dss[0].dtypes[0]) reproject( # Source parameters source=dem_data, src_crs=dem_dss[0].crs, src_transform=src_transform, src_nodata=nodata, # Destination parameters destination=dst_array, dst_transform=dst_transform, dst_crs=proj4_str, dst_nodata=nodata, # Configuration resampling=resampling) dest.write(dst_array, 1) for dem_ds in dem_dss: dem_ds.close() # Glacier grid x0y0 = (ulx + dx / 2, uly - dx / 2) # To pixel center coordinates glacier_grid = salem.Grid(proj=proj_out, nxny=(nx, ny), dxdy=(dx, -dx), x0y0=x0y0) glacier_grid.to_json(gdir.get_filepath('glacier_grid')) # Write DEM source info gdir.add_to_diagnostics('dem_source', dem_source) source_txt = DEM_SOURCE_INFO.get(dem_source, dem_source) with open(gdir.get_filepath('dem_source'), 'w') as fw: fw.write(source_txt) fw.write('\n\n') fw.write('# Data files\n\n') for fname in dem_list: fw.write('{}\n'.format(os.path.basename(fname)))