def check_crs(crs): """ Checks a CRS instance Args: crs (``CRS`` | int | dict | str): The CRS instance. Returns: ``rasterio.crs.CRS`` """ if isinstance(crs, pyproj.crs.crs.CRS): dst_crs = CRS.from_proj4(crs.to_proj4()) elif isinstance(crs, CRS): dst_crs = crs elif isinstance(crs, int): dst_crs = CRS.from_epsg(crs) elif isinstance(crs, dict): dst_crs = CRS.from_dict(crs) elif isinstance(crs, str): if crs.startswith('+proj'): dst_crs = CRS.from_proj4(crs) else: dst_crs = CRS.from_string(crs) else: logger.exception(' The CRS was not understood.') raise TypeError return dst_crs
def make_geotiff(grid_x, grid_y, points, data_to_grid,name,epsg_data,no_data): #grid the data. The linear grid help id nan's. nan_grid = griddata(points, data_to_grid, (grid_x, grid_y), method='linear') data_grid = griddata(points, data_to_grid, (grid_x, grid_y), method='nearest') sum_data = np.sum([nan_grid*0,data_grid], axis=0) #set areas without data and with very large displacements to the no_data value a = np.isnan(sum_data)*no_data x_list = np.arange(0,sum_data.flatten().size,1) b1 = x_list[abs(sum_data.flatten()) > np.median(abs(data_to_grid))*5]; b2 = x_list[a.flatten()==no_data]; data_grid_flatten= data_grid.flatten() data_grid_flatten[b1]=no_data data_grid_flatten[b2]=no_data #reshape, as expected for geotiffs data_grid1= data_grid_flatten.reshape( data_grid.shape) data_grid1_ta=np.rot90(data_grid1) #create the affine transform aff=Affine.translation(min(x)-window_compare/2, max(y)+window_compare/2)*Affine.scale(window_compare,-window_compare) #coordinate system for geotiffs a,b=data_grid1.shape epsg_code = 'epsg:'+str(epsg_data) c=CRS.from_dict(init=epsg_code) d = {'driver' : 'GTiff','dtype' : 'float64','nodata': no_data, 'width': a+1, 'height' : b+1,'count':1,'crs':c,'transform':aff} with rio.open(name, 'w', **d) as outf: outf.write(data_grid1_ta, 1) return
def _reproject_polygon(self, dst_crs): """ Reproject polygon objects to destination projection. fiona transform geom object does not handle shapely objects but "GeoJSON-like" dictionaries instead. """ # get epsg src_crs = self.src_crs if isinstance(src_crs, int): # assume epsg number epsg = src_crs src_crs = CRS.from_epsg(epsg) elif isinstance(src_crs, dict): src_crs = CRS.from_dict(**src_crs) epsg = src_crs.to_epsg() elif isinstance(src_crs, CRS): epsg = src_crs.to_epsg() else: src_crs = CRS.from_string(src_crs) epsg = src_crs.to_epsg() if dst_crs.to_epsg() != epsg: if isinstance(self.polygon, MultiPolygon): coords = [ list(geom.exterior.coords) for geom in self.polygon.geoms ] type_str = 'MultiPolygon' transformed_poly = transform_geom(src_crs, dst_crs, { 'type': type_str, 'coordinates': coords }) # convert all polygon features back to shapely polygons polygons = [ Polygon(transformed_poly[i]["coordinates"][0]) for i in range(len(transformed_poly)) ] # join all shapely polygons to a single layer self.polygon = unary_union(MultiPolygon(polygons)) else: coords = [list(self.polygon.exterior.coords)] type_str = 'Polygon' transformed_poly = transform_geom(src_crs, dst_crs, { 'type': type_str, 'coordinates': coords }) self.polygon = Polygon(transformed_poly['coordinates'][0])
def test_areas_rasterio(self): """Test all areas have valid projections with rasterio.""" try: from rasterio.crs import CRS except ImportError: return unittest.skip("Missing rasterio dependency") if not hasattr(CRS, 'from_dict'): return unittest.skip("RasterIO 1.0+ required") from pyresample import parse_area_file from satpy.resample import get_area_file all_areas = parse_area_file(get_area_file()) for area_obj in all_areas: if getattr(area_obj, 'optimize_projection', False): # the PROJ.4 is known to not be valid on this DynamicAreaDef continue proj_dict = area_obj.proj_dict _ = CRS.from_dict(proj_dict)
def get_profile(lon, lat, crs, size=SIZE): x, y = transform(Proj(init='epsg:4326'), Proj(init=crs), lon, lat) x, y = int(round(x)), int(round(y)) xmin = x - HALF_SIZE ymin = y - HALF_SIZE return { 'compress': 'lzw', 'count': len(INPUT_BANDS), 'crs': CRS.from_dict(init=crs), 'driver': 'GTiff', 'dtype': 'uint16', 'height': SIZE, 'interleave': 'pixel', 'nodata': None, 'tiled': False, 'transform': Affine(RES, 0, xmin, 0, -RES, ymin), 'width': SIZE }
def check_crs(crs): """ Checks a CRS instance Args: crs (``CRS`` | int | dict | str): The CRS instance. Returns: ``rasterio.crs.CRS`` """ import warnings with rio.Env(): with warnings.catch_warnings(): warnings.simplefilter('ignore', UserWarning) if isinstance(crs, pyproj.crs.crs.CRS): dst_crs = CRS.from_proj4(crs.to_proj4()) elif isinstance(crs, CRS): dst_crs = crs elif isinstance(crs, int): dst_crs = CRS.from_epsg(crs) elif isinstance(crs, dict): dst_crs = CRS.from_dict(crs) elif isinstance(crs, str): if crs.startswith('+proj'): dst_crs = CRS.from_proj4(crs) else: dst_crs = CRS.from_string(crs.replace('+init=', '')) else: logger.exception(' The CRS was not understood.') raise TypeError return dst_crs
def test_areas_rasterio(self): """Test all areas have valid projections with rasterio.""" try: from rasterio.crs import CRS except ImportError: return unittest.skip("Missing rasterio dependency") if not hasattr(CRS, 'from_dict'): return unittest.skip("RasterIO 1.0+ required") import numpy as np import xarray as xr from pyresample import parse_area_file from pyresample.geometry import SwathDefinition from satpy.resample import get_area_file lons = np.array([[0, 0.1, 0.2], [0.05, 0.15, 0.25]]) lats = np.array([[0, 0.1, 0.2], [0.05, 0.15, 0.25]]) lons = xr.DataArray(lons) lats = xr.DataArray(lats) swath_def = SwathDefinition(lons, lats) all_areas = parse_area_file(get_area_file()) for area_obj in all_areas: if hasattr(area_obj, 'freeze'): try: area_obj = area_obj.freeze(lonslats=swath_def) except RuntimeError: # we didn't provide enough info to freeze, hard to guess # in a generic test so just skip this area continue proj_dict = area_obj.proj_dict if proj_dict.get('proj') in ('ob_tran', 'nsper') and \ 'wktext' not in proj_dict: # FIXME: rasterio doesn't understand ob_tran unless +wktext # See: https://github.com/pyproj4/pyproj/issues/357 # pyproj 2.0+ seems to drop wktext from PROJ dict continue _ = CRS.from_dict(proj_dict)
def test_meta_of_rasterio(self): expected_general = { 'driver': 'GTiff', 'dtype': 'uint16', 'nodata': None, 'width': 9, 'height': 8, 'count': 7, 'crs': CRS.from_dict(init='epsg:32618'), } self.assertEqual(expected_general['driver'], self.landsat5.meta['driver']) self.assertEqual(expected_general['dtype'], self.landsat5.meta['dtype']) self.assertEqual(expected_general['nodata'], self.landsat5.meta['nodata']) self.assertEqual(expected_general['width'], self.landsat5.meta['width']) self.assertEqual(expected_general["height"], self.landsat5.meta["height"]) self.assertEqual(expected_general["count"], self.landsat5.meta["count"]) self.assertEqual(expected_general["crs"], self.landsat5.meta["crs"])
def main(): #-- Read the system arguments listed after the program long_options = ['DIR=', 'FILTER=', 'DX=', 'DY='] optlist, arglist = getopt.getopt(sys.argv[1:], 'D:F:X:Y:', long_options) #-- Set default settings ddir = os.path.join(os.path.expanduser('~'),'GL_learning_data',\ 'geocoded_v1','stitched.dir',\ 'atrous_32init_drop0.2_customLossR727.dir','shapefiles.dir') FILTER = 6000 dx = 100. dy = 100. for opt, arg in optlist: if opt in ("-D", "--DIR"): ddir = os.path.expanduser(arg) elif opt in ("-F", "--FILTER"): if arg not in ['NONE', 'none', 'None', 'N', 'n', 0]: FILTER = float(arg) elif opt in ('-X', '--DX'): dx = float(arg) elif opt in ('-Y', '--DY'): dy = float(arg) flt_str = '_%.1fkm' % (FILTER / 1000) #-- Get list of files fileList = os.listdir(ddir) pred_list = [ f for f in fileList if (f.endswith('%s.shp' % flt_str) and ('ERR' not in f)) ] print(pred_list) print(ddir) #-- read one error scene to get projection gdf = gpd.read_file(os.path.join(ddir, pred_list[0])) crs_wkt = CRS.from_dict(gdf.crs).to_wkt() #-- go through shapefiles and shift them by half a pixel for f in pred_list: gdf = gpd.read_file(os.path.join(ddir, f)) for g in range(len(gdf['geometry'])): #-- get coordinates x, y = gdf['geometry'][g].coords.xy #-- convert to numpy array x = np.array(x) y = np.array(y) #-- add offset x -= dx / 2 y += dy / 2 #-- make new linestring ll = LineString(zip(x, y)) #-- replace original geometry gdf['geometry'][g] = ll try: #-- save update geodataframe to file gdf.to_file(os.path.join(ddir, f), driver='ESRI Shapefile', crs_wkt=crs_wkt) except: print('empty file.') print(f) pass
# ROI information dir_location = os.path.join(dir_data, location) if not os.path.exists(dir_location): os.mkdir(dir_location) x_min, y_min, x_max, y_max = df_communities.loc[df_communities["NAAM"] == location, "geometry"].to_numpy()[0].bounds x_min, y_min = np.floor([x_min, y_min]) x_max, y_max = np.ceil([x_max, y_max]) x_max += (s1_resolution - ((x_max - x_min) % s1_resolution)) y_max += (s1_resolution - ((y_max - y_min) % s1_resolution)) roi_extent = (x_min, y_min, x_max, y_max) roi_extent_wgs = transformer_utm_wgs.transform(x_min, y_min) + transformer_utm_wgs.transform(x_max, y_max) roi_shape = (int((x_max - x_min) / s1_resolution), int((y_max - y_min) / s1_resolution)) # width, height roi_transform = rasterio.transform.from_bounds(*roi_extent, *roi_shape) roi_metadata = { "crs": CRS.from_dict(init=utm), "transform": roi_transform, "width": roi_shape[0], "height": roi_shape[1] } pixel_resolution_x = roi_transform[0] pixel_resolution_y = -roi_transform[4] # Tree & PW presence data print("{} - Gathering LC data...".format(dt.now())) trees_filename = os.path.join(dir_location, "Trees.tif") if not os.path.exists(trees_filename): trees = caf.mosaic_tiles(dir_lc, *roi_extent, roi_transform, *roi_shape, utm, "int8", nd_pres, query_string="Trees", kbl_dir=dir_kbl, kbl_scale=0, resampling="max", fill_nodata=False, mosaic_filename=trees_filename) else:
def _parse_parameter_file(self): self.num_particles = {} with rasterio.open(self.parameter_filename, "r") as f: for key in f.meta.keys(): v = f.meta[key] self.parameters[key] = v self.parameters["res"] = f.res self.parameters["profile"] = f.profile self.current_time = 0 # overwrite crs if one is provided by user if not (self.crs is None): # make sure user provided CRS is valid CRS object if not isinstance(self.crs, CRS): if isinstance(self.crs, int): # assume epsg number self.crs = CRS.from_epsg(self.crs) elif isinstance(self.crs, dict): self.crs = CRS.from_dict(**self.crs) else: self.crs = CRS.from_string(self.crs) # get reprojected transform left_edge = self.parameters["transform"] * (0, 0) right_edge = self.parameters["transform"] * ( self.parameters["width"], self.parameters["height"]) transform, width, height = warp.calculate_default_transform( self.parameters["crs"], self.crs, self.parameters["width"], self.parameters["height"], left=left_edge[0], bottom=left_edge[1], right=right_edge[0], top=right_edge[1], # resolution=self.parameters["transform"][0] dst_width=self.parameters["width"], dst_height=self.parameters["height"] ) # current solution can create rectangular pixels # xs, ys = warp.transform( # self.parameters["crs"], # dst_crs, # [left_edge[0], right_edge[0]], # [left_edge[1], right_edge[1]], # zs=None # ) # update parameters self.parameters["res"] = (transform[0], -transform[4]) self.parameters["crs"] = self.crs self.parameters["transform"] = transform self.parameters["width"] = width self.parameters["height"] = height else: # if no crs has be provided replace None with base image CRS self.crs = self.parameters["crs"] # get units and conversion factor to metres self.parameters["units"] = self.parameters["crs"].linear_units # for non-projected crs this is unknown if self.parameters["units"] == 'unknown': mylog.warning(f"Dataset CRS {self.parameters['crs']} " "units are 'unknown'. Using meters.") self.parameters["units"] = 'm' # just a place holder else: mylog.info(f"Dataset CRS {self.parameters['crs']} " f"units are '{self.parameters['units']}'. ") # set domain width = self.parameters["width"] height = self.parameters["height"] transform = self.parameters["transform"] self.dimensionality = 3 self.domain_dimensions = np.array([width, height, 1], dtype=np.int32) rast_left = np.concatenate([transform * (0, 0), [0]]) rast_right = np.concatenate([transform * (width, height), [1]]) # save dimensions that need to be flipped self._flip_axes = np.where(rast_left > rast_right)[0] self.domain_left_edge = self.arr( np.min([rast_left, rast_right], axis=0), self.parameters["units"]) self.domain_right_edge = self.arr( np.max([rast_left, rast_right], axis=0), self.parameters["units"]) self.resolution = self.arr(self.parameters["res"], self.parameters["units"])
def read_raster(file_name, band=[1], src_crs=None, window=False, geometry=False, dst_crs=False, transform=None, width=None, height=None, resampling=Resampling.nearest): """ Read raster of bands and set 0 values to the masked ones. Each band is an event. Select region using window or geometry. Reproject input by proving dst_crs and/or (transform, width, height). Returns matrix in 2d: band x coordinates in 1d (evtl. reshape to band x height x width) Parameters: file_name (str): name of the file band (list(int), optional): band number to read. Default: 1 window (rasterio.windows.Window, optional): window to read geometry (shapely.geometry, optional): consider pixels only in shape dst_crs (crs, optional): reproject to given crs transform (rasterio.Affine): affine transformation to apply wdith (float): number of lons for transform height (float): number of lats for transform resampling (rasterio.warp,.Resampling optional): resampling function used for reprojection to dst_crs Returns: dict (meta), np.array (band x coordinates_in_1d) """ LOGGER.info('Reading %s', file_name) if os.path.splitext(file_name)[1] == '.gz': file_name = '/vsigzip/' + file_name with rasterio.Env(): with rasterio.open(file_name, 'r') as src: if src_crs is None: src_meta = CRS.from_dict(DEF_CRS) if not src.crs else src.crs else: src_meta = src_crs if dst_crs or transform: LOGGER.debug('Reprojecting ...') if not dst_crs: dst_crs = src_meta if not transform: transform, width, height = calculate_default_transform(\ src_meta, dst_crs, src.width, src.height, *src.bounds) dst_meta = src.meta.copy() dst_meta.update({'crs': dst_crs, 'transform': transform, 'width': width, 'height': height }) kwargs = {} if src.meta['nodata']: kwargs['src_nodata'] = src.meta['nodata'] kwargs['dst_nodata'] = src.meta['nodata'] intensity = np.zeros((len(band), height, width)) for idx_band, i_band in enumerate(band): reproject(source=src.read(i_band), destination=intensity[idx_band, :], src_transform=src.transform, src_crs=src_meta, dst_transform=transform, dst_crs=dst_crs, resampling=resampling, **kwargs) if dst_meta['nodata'] and np.isnan(dst_meta['nodata']): intensity[idx_band, :][np.isnan(intensity[idx_band, :])] = 0 else: intensity[idx_band, :][intensity[idx_band, :] == dst_meta['nodata']] = 0 meta = dst_meta return meta, intensity.reshape((len(band), meta['height']*meta['width'])) meta = src.meta.copy() if geometry: inten, mask_trans = mask(src, geometry, crop=True, indexes=band) if meta['nodata'] and np.isnan(meta['nodata']): inten[np.isnan(inten)] = 0 else: inten[inten == meta['nodata']] = 0 meta.update({"height": inten.shape[1], "width": inten.shape[2], "transform": mask_trans}) else: masked_array = src.read(band, window=window, masked=True) inten = masked_array.data inten[masked_array.mask] = 0 if window: meta.update({"height": window.height, \ "width": window.width, \ "transform": rasterio.windows.transform(window, src.transform)}) if not meta['crs']: meta['crs'] = CRS.from_dict(DEF_CRS) intensity = inten[range(len(band)), :] return meta, intensity.reshape((len(band), meta['height']*meta['width']))
def test_issue1620(): """Different forms of EPSG:3857 are equal""" assert CRS.from_wkt( 'PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]' ) == CRS.from_dict(init='epsg:3857')
def test_issue1620(): """Different forms of EPSG:3857 are equal""" assert CRS.from_wkt('PROJCS["WGS 84 / Pseudo-Mercator",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]],PROJECTION["Mercator_1SP"],PARAMETER["central_meridian",0],PARAMETER["scale_factor",1],PARAMETER["false_easting",0],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["X",EAST],AXIS["Y",NORTH],EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],AUTHORITY["EPSG","3857"]]') == CRS.from_dict(init='epsg:3857')
len(col_dan_1km_ind), len(row_dan_1km_ind)) kwargs_1km_sub = { 'driver': 'GTiff', 'dtype': 'float32', 'nodata': 0.0, 'width': len(lon_world_ease_1km), 'height': len(lat_world_ease_1km), 'count': 1, 'crs': CRS.from_dict(init='epsg:6933'), 'transform': Affine(1000.89502334956, 0.0, -17367530.44516138, 0.0, -1000.89502334956, 7314540.79258289) } smap_sm_dan_1km_output = sub_n_reproj(smap_sm_dan_1km_sub, kwargs_1km_sub, sub_window_dan_1km, output_crs) masked_ds_dan_1km, mask_transform_ds_dan_1km = mask( dataset=smap_sm_dan_1km_output, shapes=crop_shape, crop=True) masked_ds_dan_1km[np.where(masked_ds_dan_1km == 0)] = np.nan sub_window_dan_9km = Window(col_dan_9km_ind[0], row_dan_9km_ind[0], len(col_dan_9km_ind), len(row_dan_9km_ind)) kwargs_9km_sub = { 'driver':
import re import torch from torch.autograd import Variable import torch.backends.cudnn as cudnn import torch.nn as nn from util.visualizer import Visualizer import networks.networks as networks from .mtl_test import MTL_Test as GenericTestModel from rasterio.crs import CRS from rasterio.transform import Affine GEN_META = {'driver': 'GTiff', 'dtype': 'float32', 'nodata': None, 'width': 3816, 'height': 2550, 'count': 1, 'transform': Affine(1.0, 0.0, 0.0, 0.0, 1.0, 0.0), 'crs': CRS.from_dict(init='epsg:26915')} class TestModel(GenericTestModel): def initialize(self, opt): GenericTestModel.initialize(self, opt) self.get_color_palette() def name(self): return 'Raster Test Model' def get_color_palette(self): if self.opt.dataset_name == 'dfc': self.opt.color_palette = [ [0, 0, 0], [0, 205, 0], [127, 255, 0], [46, 139, 87], [0, 139, 0], [0, 70, 0], [160, 82, 45], [0, 255, 255], [255, 255, 255], [216, 191, 216], [255, 0, 0], [170, 160, 150], [128, 128, 128], [160, 0, 0], [80, 0, 0], [232, 161, 24], [255, 255, 0], [238, 154, 0], [255, 0, 255], [0, 0, 255], [176, 196, 222] ]
from util.visualizer import Visualizer import networks.networks as networks from .mtl_test import MTL_Test as GenericTestModel from rasterio.crs import CRS from rasterio.transform import Affine OUT_META_SEM = [{ 'driver': 'GTiff', 'dtype': 'uint8', 'nodata': None, 'width': 1192, 'height': 1202, 'count': 1, 'crs': CRS.from_dict(init='epsg:26915'), 'transform': Affine(0.5, 0.0, 271460.0, 0.0, -0.5, 3290290.0) }, { 'driver': 'GTiff', 'dtype': 'uint8', 'nodata': None, 'width': 1192, 'height': 1202, 'count': 1, 'crs': CRS.from_dict(init='epsg:26915'), 'transform': Affine(0.5, 0.0, 271460.0, 0.0, -0.5, 3290891.0) }, { 'driver': 'GTiff', 'dtype': 'uint8', 'nodata': None, 'width': 1192,
def warp(filename, resampling='nearest', bounds=None, crs=None, res=None, nodata=0, warp_mem_limit=512, num_threads=1, tap=False): """ Warps an image to a VRT object Args: filename (str): The input file name. resampling (Optional[str]): The resampling method. Choices are ['average', 'bilinear', 'cubic', 'cubic_spline', 'gauss', 'lanczos', 'max', 'med', 'min', 'mode', 'nearest']. bounds (Optional[tuple]): The extent bounds to warp to. crs (Optional[object]): The CRS to warp to. res (Optional[tuple]): The cell resolution to warp to. nodata (Optional[int or float]): The 'no data' value. warp_mem_limit (Optional[int]): The memory limit (in MB) for the ``rasterio.vrt.WarpedVRT`` function. num_threads (Optional[int]): The number of warp worker threads. tap (Optional[bool]): Whether to target align pixels. Returns: ``rasterio.vrt.WarpedVRT`` """ with rio.open(filename) as src: if res: dst_res = res else: dst_res = src.res if crs: if isinstance(crs, CRS): dst_crs = crs elif isinstance(crs, int): dst_crs = CRS.from_epsg(crs) elif isinstance(crs, dict): dst_crs = CRS.from_dict(crs) elif isinstance(crs, str): if crs.startswith('+proj'): dst_crs = CRS.from_proj4(crs) else: dst_crs = CRS.from_string(crs) else: logger.exception(' The CRS was not understood.') else: dst_crs = src.crs dst_transform, dst_width, dst_height = calculate_default_transform( src.crs, dst_crs, src.width, src.height, left=src.bounds.left, bottom=src.bounds.bottom, right=src.bounds.right, top=src.bounds.top, resolution=dst_res) # Check if the data need to be subset if bounds and (bounds != src.bounds): if isinstance(bounds, str): if bounds.startswith('BoundingBox'): bounds_str = bounds.replace('BoundingBox(', '').split(',') for str_ in bounds_str: if str_.strip().startswith('left='): left_coord = float( str_.strip().split('=')[1].replace(')', '')) elif str_.strip().startswith('bottom='): bottom_coord = float( str_.strip().split('=')[1].replace(')', '')) elif str_.strip().startswith('right='): right_coord = float( str_.strip().split('=')[1].replace(')', '')) elif str_.strip().startswith('top='): top_coord = float( str_.strip().split('=')[1].replace(')', '')) bounds = BoundingBox(left=left_coord, bottom=bottom_coord, right=right_coord, top=top_coord) else: logger.exception(' The bounds were not accepted.') left, bottom, right, top = bounds # Keep the CRS but subset the data dst_transform, dst_width, dst_height = calculate_default_transform( dst_crs, dst_crs, dst_width, dst_height, left=left, bottom=bottom, right=right, top=top, resolution=dst_res) dst_width = int((right - left) / dst_res[0]) dst_height = int((top - bottom) / dst_res[1]) else: bounds = src.bounds # Do not warp if all the key metadata match the reference information if (src.bounds == bounds) and (src.res == dst_res) and ( src.crs == dst_crs) and (src.width == dst_width) and (src.height == dst_height): output = filename else: if tap: # Align the cells dst_transform, dst_width, dst_height = aligned_target( dst_transform, dst_width, dst_height, dst_res) vrt_options = { 'resampling': getattr(Resampling, resampling), 'crs': dst_crs, 'transform': dst_transform, 'height': dst_height, 'width': dst_width, 'nodata': nodata, 'warp_mem_limit': warp_mem_limit, 'warp_extras': { 'multi': True, 'warp_option': 'NUM_THREADS={:d}'.format(num_threads) } } with WarpedVRT(src, **vrt_options) as vrt: output = vrt return output
def write_raster( self, output_filename: PathLike, variable: str, time: datetime, fill_value=LEAFLET_NODATA_VALUE, driver: str = 'GTiff', crop: bool = True, ): """ Writes interpolated raster of given variable to output path. :param output_filename: path of raster file to create :param variable: name of variable :param time: time from which to retrieve data :param fill_value: desired fill value of output :param driver: strings of valid GDAL driver (currently one of 'GTiff', 'GPKG', or 'AAIGrid') :param crop: whether to crop to study area extent """ if not isinstance(output_filename, Path): output_filename = Path(output_filename) output_data = self.data(variable, time, crop).values if output_data is not None: if crop: transform = self.study_area_transform else: transform = self.global_grid_transform gdal_args = { 'transform': transform, 'height': output_data.shape[0], 'width': output_data.shape[1], 'count': 1, 'dtype': rasterio.float32, 'crs': CRS.from_dict(OUTPUT_CRS), 'nodata': numpy.array([fill_value]).astype(output_data.dtype).item(), } if driver == 'AAIGrid': file_extension = 'asc' gdal_args.update({'FORCE_CELLSIZE': 'YES'}) elif driver == 'GPKG': file_extension = 'gpkg' else: file_extension = 'tiff' gdal_args.update(TIFF_CREATION_OPTIONS) output_filename = f'{output_filename.stem}.{file_extension}' LOGGER.info(f'Writing {output_filename}') with rasterio.open(output_filename, 'w', driver, **gdal_args) as output_raster: output_raster.write(output_data, 1) if driver == 'GTiff': output_raster.build_overviews( PyOFS.overview_levels(output_data.shape), Resampling['average']) output_raster.update_tags(ns='rio_overview', resampling='average')
def write_rasters( self, output_dir: PathLike, variables: list, time: datetime, filename_prefix: str = None, filename_suffix: str = None, fill_value=LEAFLET_NODATA_VALUE, driver: str = 'GTiff', crop: bool = True, ): """ Write averaged raster data of given variables to given output directory. :param output_dir: path to directory :param variables: variable names to use :param time: time from which to retrieve data :param filename_prefix: prefix for filenames :param filename_suffix: suffix for filenames :param fill_value: desired fill value of output :param driver: strings of valid GDAL driver (currently one of 'GTiff', 'GPKG', or 'AAIGrid') :param crop: whether to crop to study area extent """ if not isinstance(output_dir, Path): output_dir = Path(output_dir) if variables is None: variables = DATA_VARIABLES[self.source] if filename_prefix is None: filename_prefix = 'rtofs' filename_suffix = f'_{filename_suffix}' if filename_suffix is not None else '' if self.time_interval == 'daily': time = time.replace(hour=0, minute=0, second=0, microsecond=0) time_delta = int((time - self.model_time) / timedelta(days=1)) direction = 'forecast' if time_delta >= 0 else 'nowcast' time_delta_string = f'{direction[0]}{abs(time_delta) + 1 if direction == "forecast" else abs(time_delta):03}' variable_means = {} for variable in variables: if variable not in ['dir', 'mag']: try: variable_means[variable] = self.data(variable, time, crop) except KeyError: LOGGER.warning( f'variable "{variable}" not found in RTOFS dataset') except Exception as error: LOGGER.warning(error) variable_means = { variable: variable_mean.values for variable, variable_mean in variable_means.items() if variable_mean is not None } if 'dir' in variables or 'mag' in variables: u_name = 'ssu' v_name = 'ssv' if u_name not in variable_means: u_data = self.data(u_name, time, crop) u_data = u_data.values if u_data is not None else None else: u_data = variable_means[u_name] if v_name not in variable_means: v_data = self.data(v_name, time, crop) v_data = v_data.values if v_data is not None else None else: v_data = variable_means[v_name] if 'anim' in filename_suffix: variable_means['dir'] = u_data variable_means['mag'] = v_data else: # calculate direction and magnitude of vector in degrees (0-360) and in metres per second variable_means['dir'] = (numpy.arctan2(u_data, v_data) + numpy.pi) * (180 / numpy.pi) variable_means['mag'] = numpy.sqrt(u_data**2 + v_data**2) # write interpolated grids to raster files for variable, variable_mean in variable_means.items(): if variable_mean is not None and variable_mean.size > 0: if crop: transform = self.study_area_transform else: transform = self.global_grid_transform if fill_value is not None: variable_mean[numpy.isnan(variable_mean)] = fill_value gdal_args = { 'transform': transform, 'height': variable_mean.shape[0], 'width': variable_mean.shape[1], 'count': 1, 'dtype': rasterio.float32, 'crs': CRS.from_dict(OUTPUT_CRS), 'nodata': numpy.array([fill_value ]).astype(variable_mean.dtype).item(), } if driver == 'AAIGrid': file_extension = 'asc' gdal_args.update({'FORCE_CELLSIZE': 'YES'}) elif driver == 'GPKG': file_extension = 'gpkg' else: file_extension = 'tiff' gdal_args.update(TIFF_CREATION_OPTIONS) output_filename = f'{filename_prefix}_{variable}_{self.model_time:%Y%m%d}_{time_delta_string}{filename_suffix}.{file_extension}' output_filename = output_dir / output_filename LOGGER.info(f'Writing {output_filename}') with rasterio.open(output_filename, 'w', driver, **gdal_args) as output_raster: output_raster.write(variable_mean, 1) if driver == 'GTiff': output_raster.build_overviews( PyOFS.overview_levels(variable_mean.shape), Resampling['average']) output_raster.update_tags(ns='rio_overview', resampling='average')
def test_crs_from_json_dict(): aeqd_crs = CRS(proj="aeqd", lon_0=-80, lat_0=40.5) assert CRS.from_dict(aeqd_crs.to_dict(projjson=True)) == aeqd_crs
def __init__(self, filename, ds=None, field_parameters=None, crs=None): validate_object(ds, Dataset) validate_object(field_parameters, dict) self.src_crs = crs if isinstance(filename, str): self.filename = filename # read shapefile with fiona with fiona.open(filename, "r") as shapefile: shapes_from_file = [ feature["geometry"] for feature in shapefile ] self.src_crs = CRS.from_dict(**shapefile.crs) # shapefile crs # save number of polygons self._number_features = len(shapes_from_file) # reproject to datasets crs for i in range(self._number_features): shapes_from_file[i] = transform_geom( f'EPSG:{self.src_crs.to_epsg()}', f'EPSG:{ds.parameters["crs"].to_epsg()}', shapes_from_file[i]) # convert all polygon features in shapefile to list of shapely polygons polygons = [ Polygon(shapes_from_file[i]["coordinates"][0]) for i in range(self._number_features) ] # fix invalid MultiPolygons m = MultiPolygon(polygons) # join all shapely polygons to a single layer self.polygon = unary_union(m) elif isinstance(filename, Polygon): # only one polygon self._number_features = 1 self.polygon = filename if not (self.src_crs is None): self._reproject_polygon(ds.parameters['crs']) elif isinstance(filename, MultiPolygon): # only one polygon self._number_features = len(filename.geoms) self.polygon = unary_union(filename) if not (self.src_crs is None): self._reproject_polygon(ds.parameters['crs']) elif isinstance(filename, list): # assume list of shapely polygons self._number_features = len(filename) # fix invalid MultiPolygons m = MultiPolygon(filename) # join all shapely polygons to a single layer self.polygon = unary_union(m) if not (self.src_crs is None): self._reproject_polygon(ds.parameters['crs']) mylog.info( f"Number of features in poly object: {self._number_features}") # define coordinates of center self.center = [ self.polygon.centroid.coords.xy[0][0], self.polygon.centroid.coords.xy[1][0], ] data_source = None super().__init__(self.center, ds, field_parameters, data_source)
def prepare_points(self, data, aoi, frac=1.0, all_touched=False, id_column='id', mask=None, n_jobs=8, verbose=0): if isinstance(aoi, gpd.GeoDataFrame): df = aoi else: if isinstance(aoi, str): if not os.path.isfile(aoi): logger.exception(' The AOI file does not exist.') df = gpd.read_file(aoi) else: logger.exception( ' The AOI must be a vector file or a GeoDataFrame.') # Re-project the data to match the image CRS if isinstance(df.crs, str): if df.crs.lower().startswith('+proj'): if data.crs != df.crs: df = df.to_crs(data.crs) elif isinstance(df.crs, int): if data.crs != CRS.from_epsg(df.crs).to_proj4(): df = df.to_crs(data.crs) else: if data.crs != CRS.from_dict(df.crs).to_proj4(): df = df.to_crs(data.crs) if verbose > 0: logger.info(' Checking geometry validity ...') # Ensure all geometry is valid df = df[df['geometry'].apply(lambda x_: x_ is not None)] if verbose > 0: logger.info(' Checking geometry extent ...') # Remove data outside of the image bounds if type(df.iloc[0].geometry) == Polygon: df = gpd.overlay(df, gpd.GeoDataFrame(data=[0], geometry=[data.gw.meta.geometry], crs=df.crs), how='intersection') else: # Clip points to the image bounds df = df[df.geometry.intersects(data.gw.unary_union)] if isinstance(mask, Polygon) or isinstance(mask, gpd.GeoDataFrame): if isinstance(mask, gpd.GeoDataFrame): if CRS.from_dict(mask.crs).to_proj4() != CRS.from_dict( df.crs).to_proj4(): mask = mask.to_crs(df.crs) if verbose > 0: logger.info(' Clipping geometry ...') df = df[df.within(mask)] if df.empty: logger.exception( ' No geometry intersects the user-provided mask.') # Subset the DataArray # minx, miny, maxx, maxy = df.total_bounds # # obj_subset = self._obj.gw.subset(left=float(minx)-self._obj.res[0], # top=float(maxy)+self._obj.res[0], # right=float(maxx)+self._obj.res[0], # bottom=float(miny)-self._obj.res[0]) # Convert polygons to points if type(df.iloc[0].geometry) == Polygon: if verbose > 0: logger.info(' Converting polygons to points ...') df = self.polygons_to_points(data, df, frac=frac, all_touched=all_touched, id_column=id_column, n_jobs=n_jobs) # Ensure a unique index df.index = list(range(0, df.shape[0])) return df
# Subplot maps # Load in watershed shapefile boundaries shapefile_dan = fiona.open(path_shp_dan + '/' + shp_dan_file, 'r') crop_shape_dan = [feature["geometry"] for feature in shapefile_dan] shp_dan_extent = list(shapefile_dan.bounds) output_crs = 'EPSG:4326' # Subset and reproject the amsr2 SM data at watershed # 1 km masked_ds_dan_1km_all = [] for n in range(amsr2_1km_data_stack.shape[0]): sub_window_dan_1km = Window(col_dan_1km_ind[0], row_dan_1km_ind[0], len(col_dan_1km_ind), len(row_dan_1km_ind)) kwargs_1km_sub = {'driver': 'GTiff', 'dtype': 'float32', 'nodata': 0.0, 'width': len(lon_world_ease_1km), 'height': len(lat_world_ease_1km), 'count': 1, 'crs': CRS.from_dict(init='epsg:6933'), 'transform': Affine(1000.89502334956, 0.0, -17367530.44516138, 0.0, -1000.89502334956, 7314540.79258289)} amsr2_sm_dan_1km_output = sub_n_reproj(amsr2_1km_data_stack[n, :, :], kwargs_1km_sub, sub_window_dan_1km, output_crs) masked_ds_dan_1km, mask_transform_ds_dan_1km = mask(dataset=amsr2_sm_dan_1km_output, shapes=crop_shape_dan, crop=True) masked_ds_dan_1km[np.where(masked_ds_dan_1km == 0)] = np.nan masked_ds_dan_1km = masked_ds_dan_1km.squeeze() masked_ds_dan_1km_all.append(masked_ds_dan_1km) masked_ds_dan_1km_all = np.asarray(masked_ds_dan_1km_all) # 9 km masked_ds_dan_9km_all = [] for n in range(amsr2_9km_data_stack.shape[0]):
def test_equality_from_dict(epsg_code): assert CRS.from_dict(init='epsg:{}'.format(epsg_code)) == CRS.from_dict(init='epsg:{}'.format(epsg_code))
def clip(self, data, df, query=None, mask_data=False, expand_by=0): """ Clips a DataArray by vector polygon geometry Args: data (DataArray): The ``xarray.DataArray`` to subset. df (GeoDataFrame or str): The ``geopandas.GeoDataFrame`` or filename to clip to. query (Optional[str]): A query to apply to ``df``. mask_data (Optional[bool]): Whether to mask values outside of the ``df`` geometry envelope. expand_by (Optional[int]): Expand the clip array bounds by ``expand_by`` pixels on each side. Returns: ``xarray.DataArray`` Examples: >>> import geowombat as gw >>> >>> with gw.open('image.tif') as ds: >>> ds = gw.clip(ds, df, query="Id == 1") >>> >>> # or >>> >>> with gw.open('image.tif') as ds: >>> ds = ds.gw.clip(df, query="Id == 1") """ if isinstance(df, str) and os.path.isfile(df): df = gpd.read_file(df) if query: df = df.query(query) try: if data.crs.strip() != CRS.from_dict(df.crs).to_proj4().strip(): # Re-project the DataFrame to match the image CRS df = df.to_crs(data.crs) except: if data.crs.strip() != CRS.from_proj4(df.crs).to_proj4().strip(): df = df.to_crs(data.crs) row_chunks = data.gw.row_chunks col_chunks = data.gw.col_chunks left, bottom, right, top = df.total_bounds # Align the geometry array grid align_transform, align_width, align_height = align_bounds( left, bottom, right, top, data.res) # Get the new bounds new_left, new_bottom, new_right, new_top = array_bounds( align_height, align_width, align_transform) if expand_by > 0: new_left -= data.gw.cellx * expand_by new_bottom -= data.gw.celly * expand_by new_right += data.gw.cellx * expand_by new_top += data.gw.celly * expand_by # Subset the array data = self.subset(data, left=new_left, bottom=new_bottom, right=new_right, top=new_top) if mask_data: # Rasterize the geometry and store as a DataArray mask = xr.DataArray(data=da.from_array( features.rasterize(list(df.geometry.values), out_shape=(align_height, align_width), transform=align_transform, fill=0, out=None, all_touched=True, default_value=1, dtype='int32'), chunks=(row_chunks, col_chunks)), dims=['y', 'x'], coords={ 'y': data.y.values, 'x': data.x.values }) # Return the clipped array return data.where(mask == 1) else: return data
def test_equality_from_dict(epsg_code): assert CRS.from_dict(init='epsg:{}'.format(epsg_code)) == CRS.from_dict( init='epsg:{}'.format(epsg_code))
def mask(data, df, query=None, keep='in'): """ Masks a DataArray by vector polygon geometry Args: data (DataArray): The ``xarray.DataArray`` to mask. df (GeoDataFrame or str): The ``geopandas.GeoDataFrame`` or filename to use for masking. query (Optional[str]): A query to apply to ``df``. keep (Optional[str]): If ``keep`` = 'in', mask values outside of the geometry (keep inside). Otherwise, if ``keep`` = 'out', mask values inside (keep outside). Returns: ``xarray.DataArray`` Examples: >>> import geowombat as gw >>> >>> with gw.open('image.tif') as ds: >>> ds = ds.gw.mask(df) """ if isinstance(df, str) and os.path.isfile(df): df = gpd.read_file(df) if query: df = df.query(query) try: if data.crs.strip() != CRS.from_dict(df.crs).to_proj4().strip(): # Re-project the DataFrame to match the image CRS df = df.to_crs(data.crs) except: if data.crs.strip() != CRS.from_proj4(df.crs).to_proj4().strip(): df = df.to_crs(data.crs) # Rasterize the geometry and store as a DataArray mask = xr.DataArray(data=da.from_array( features.rasterize(list(df.geometry.values), out_shape=(data.gw.nrows, data.gw.ncols), transform=data.transform, fill=0, out=None, all_touched=True, default_value=1, dtype='int32'), chunks=(data.gw.row_chunks, data.gw.col_chunks)), dims=['y', 'x'], coords={ 'y': data.y.values, 'x': data.x.values }) # Return the masked array if keep == 'out': return data.where(mask != 1) else: return data.where(mask == 1)
def write_rasters( self, output_dir: PathLike, variables: Collection[str] = ('sst', 'sses'), filename_prefix: str = 'abi', fill_value: float = LEAFLET_NODATA_VALUE, driver: str = 'GTiff', correct_sses: bool = False, ): """ Write ABI rasters to file using data from given variables. :param output_dir: path to output directory :param variables: variable names to write :param filename_prefix: prefix for output filenames :param fill_value: desired fill value of output :param driver: strings of valid GDAL driver (currently one of 'GTiff', 'GPKG', or 'AAIGrid') :param correct_sses: whether to subtract SSES bias from SST """ if not isinstance(output_dir, Path): output_dir = Path(output_dir) for variable in variables: input_data = self.data(variable, correct_sses) if variable == 'sses': fill_value = 0 if input_data is not None and not numpy.isnan(input_data).all(): if fill_value is not None: input_data[numpy.isnan(input_data)] = fill_value gdal_args = { 'height': input_data.shape[0], 'width': input_data.shape[1], 'count': 1, 'dtype': rasterio.float32, 'crs': CRS.from_dict(OUTPUT_CRS), 'transform': ABIDataset.study_area_transform, 'nodata': fill_value, } if driver == 'AAIGrid': file_extension = 'asc' gdal_args.update({'FORCE_CELLSIZE': 'YES'}) elif driver == 'GPKG': file_extension = 'gpkg' else: file_extension = 'tiff' gdal_args.update(TIFF_CREATION_OPTIONS) output_filename = output_dir / f'{filename_prefix}_{variable}.{file_extension}' # use rasterio to write to raster with GDAL args LOGGER.info(f'Writing to {output_filename}') with rasterio.open(output_filename, 'w', driver, **gdal_args) as output_raster: output_raster.write(input_data, 1) if driver == 'GTiff': output_raster.build_overviews( PyOFS.overview_levels(input_data.shape), Resampling['average']) output_raster.update_tags(ns='rio_overview', resampling='average')
def warp_to_transform(src_filename, dst_transform, dst_width, dst_height, dst_crs_init, src_band=1, return_nodata=False, resampling="nearest"): """ Warp content of src_filename to match transform Inputs src_filename: str path to source file dst_transform: rasterio transform destination transform dst_width: int destination width (no. of columns) dst_height: int destination height (no. of rows) dst_crs_init: str destination CRS (format "EPSG:code") src_band: int index of band to warp, starting from 1 (default = 1) return_nodata: bool whether to return no data value (default = False) resampling: str resampling mode, one of ["nearest", "average", "cubic", "bilinear", "min", "max"] Outputs dst_array: nd array warped array nd_value: float no data value warped array (onli if return_nodata = True) """ if resampling == "nearest": resampling = Resampling.nearest elif resampling == "average": resampling = Resampling.average elif resampling == "cubic": resampling = Resampling.cubic elif resampling == "bilinear": resampling = Resampling.bilinear elif resampling == "min": resampling = Resampling.min elif resampling == "max": resampling = Resampling.max else: print( "Warning: Resampling method not recognized, switching to default 'nearest'" ) resampling = Resampling.nearest if type(src_band) is list: num_bands = len(src_band) else: num_bands = 1 with rasterio.open(src_filename) as src: if src.nodata is None: nd_value = 0 else: nd_value = src.nodata if num_bands == 1: dst_array = np.ones( (dst_height, dst_width), dtype=src.meta["dtype"]) * nd_value else: dst_array = np.ones((num_bands, dst_height, dst_width), dtype=src.meta["dtype"]) * nd_value warp.reproject(rasterio.band(src, src_band), destination=dst_array, dst_transform=dst_transform, dst_crs=CRS.from_dict(init=dst_crs_init), dst_resolution=dst_transform[0], resampling=resampling) if return_nodata: return dst_array, nd_value return dst_array
def prepare_points(self, data, aoi, frac=1.0, min_frac_area=None, all_touched=False, id_column='id', mask=None, n_jobs=8, verbose=0, **kwargs): if isinstance(aoi, gpd.GeoDataFrame): df = aoi else: if isinstance(aoi, str): if not os.path.isfile(aoi): logger.exception(' The AOI file does not exist.') raise OSError df = gpd.read_file(aoi) else: logger.exception( ' The AOI must be a vector file or a GeoDataFrame.') raise TypeError if id_column not in df.columns.tolist(): df.loc[:, id_column] = df.index.values df_crs = check_crs(df.crs).to_proj4() data_crs = check_crs(data.crs).to_proj4() # Re-project the data to match the image CRS if data_crs != df_crs: df = df.to_crs(data_crs) if verbose > 0: logger.info(' Checking geometry validity ...') # Ensure all geometry is valid df = df[df['geometry'].apply(lambda x_: x_ is not None)] if verbose > 0: logger.info(' Checking geometry extent ...') # Remove data outside of the image bounds if (type(df.iloc[0].geometry) == Polygon) or (type(df.iloc[0].geometry) == MultiPolygon): df = gpd.overlay(df, gpd.GeoDataFrame(data=[0], geometry=[data.gw.geometry], crs=df_crs), how='intersection').drop(columns=[0]) else: # Clip points to the image bounds df = df[df.geometry.intersects(data.gw.geometry)] if isinstance(mask, Polygon) or isinstance( mask, MultiPolygon) or isinstance(mask, gpd.GeoDataFrame): if isinstance(mask, gpd.GeoDataFrame): if CRS.from_dict(mask.crs).to_proj4() != df_crs: mask = mask.to_crs(df_crs) if verbose > 0: logger.info(' Clipping geometry ...') df = df[df.within(mask)] if df.empty: logger.exception( ' No geometry intersects the user-provided mask.') raise LookupError if not df.empty: # Convert polygons to points if (type(df.iloc[0].geometry) == Polygon) or (type( df.iloc[0].geometry) == MultiPolygon): if verbose > 0: logger.info(' Converting polygons to points ...') df = self.polygons_to_points(data, df, frac=frac, min_frac_area=min_frac_area, all_touched=all_touched, id_column=id_column, n_jobs=n_jobs, **kwargs) if not df.empty: # Ensure a unique index df.index = list(range(0, df.shape[0])) return df
class Terrain(object): latlon_crs = CRS.from_dict(init='epsg:4326') def __init__(self, latlon_bounds, fpath='terrain.tif'): """Create container for manipulating GeoTIFF data in the specified region Usage ===== latlon_bounds : list or tuple Latitude/longitude corresponding to west, south, east, and north bounds, used to define the source transformation. fpath : str, optional Where to save downloaded GeoTIFF (*.tif) data. """ self.bounds = list(latlon_bounds) self._get_utm_crs() # from bounds self.tiffdata = fpath self.have_terrain = False if not hasattr(self, 'have_metadata'): # set attribute if it hasn't been set already self.have_metadata = False def _get_utm_crs(self, datum='WGS84', ellps='WGS84'): """Get coordinate system from zone number associated with the longitude of the northwest corner Parameters ========== datum : str, optional Origin of destination coordinate system, used to describe PROJ.4 string; default is WGS84. ellps : str, optional Ellipsoid defining the shape of the earth in the destination coordinate system, used to describe PROJ.4 string; default is WGS84. """ #west, south, east, north = self.bounds self.zone_number = int((self.bounds[0] + 180) / 6) + 1 proj = '+proj=utm +zone={:d} '.format(self.zone_number) \ + '+datum={:s} +units=m +no_defs '.format(datum) \ + '+ellps={:s} +towgs84=0,0,0'.format(ellps) self.utm_crs = CRS.from_proj4(proj) def _get_bounds_from_metadata(self): """This is a stub""" assert self.have_metadata raise NotImplementedError() def to_terrain(self, dx, dy=None, resampling=warp.Resampling.bilinear): """Load geospatial raster data and reproject onto specified grid Usage ===== dx,dy : float Grid spacings [m]. If dy is not specified, then uniform spacing is assumed. resampling : warp.Resampling value, optional See `list(warp.Resampling)`. """ if dy is None: dy = dx # load raster if not os.path.isfile(self.tiffdata): raise FileNotFoundError('Need to download()') dem_raster = rasterio.open(self.tiffdata) # get source coordinate reference system, transform west, south, east, north = self.bounds src_height, src_width = dem_raster.shape src_crs = dem_raster.crs src_transform = transform.from_bounds(*self.bounds, src_width, src_height) src = dem_raster.read(1) # calculate destination coordinate reference system, transform dst_crs = self.utm_crs print('Projecting from', src_crs, 'to', dst_crs) # - get origin (the _upper_ left corner) from bounds orix, oriy = self.to_xy(north, west) origin = (orix, oriy) self.origin = origin dst_transform = transform.from_origin(*origin, dx, dy) # - get extents from lower right corner SE_x, SE_y = self.to_xy(south, east) Lx = SE_x - orix Ly = oriy - SE_y Nx = int(Lx / dx) Ny = int(Ly / dy) # reproject to uniform grid in the UTM CRS dem_array = np.empty((Ny, Nx)) warp.reproject(src, dem_array, src_transform=src_transform, src_crs=src_crs, dst_transform=dst_transform, dst_crs=dst_crs, resampling=resampling) utmx = orix + np.arange(0, Nx * dx, dx) utmy = oriy + np.arange((-Ny + 1) * dy, dy, dy) self.x, self.y = np.meshgrid(utmx, utmy, indexing='ij') self.z = np.flipud(dem_array).T self.zfun = RectBivariateSpline(utmx, utmy, self.z) self.have_terrain = True return self.x, self.y, self.z def to_latlon(self, x, y): """Transform uniform grid to lat/lon space""" if not hasattr(x, '__iter__'): assert ~hasattr(x, '__iter__') x = [x] y = [y] xlon, xlat = warp.transform(self.utm_crs, self.latlon_crs, x, y) try: shape = x.shape except AttributeError: xlat = xlat[0] xlon = xlon[0] else: xlat = np.reshape(xlat, shape) xlon = np.reshape(xlon, shape) return xlat, xlon def to_xy(self, lat, lon, xref=None, yref=None): """Transform lat/lon to UTM space""" if not hasattr(lat, '__iter__'): assert ~hasattr(lat, '__iter__') lat = [lat] lon = [lon] x, y = warp.transform(self.latlon_crs, self.utm_crs, lon, lat) try: shape = lon.shape except AttributeError: x = x[0] y = y[0] else: x = np.reshape(x, shape) y = np.reshape(y, shape) if xref is not None: x -= xref if yref is not None: y -= yref return x, y def xtransect(self, xy=None, latlon=None, wdir=270.0, xrange=(None, None)): """Get terrain transect along x for a slice aligned with the specified wind direction and going through a specified reference point (defined by xy or latlon) Usage ===== xy : list or tuple Reference location in the UTM coordinate reference system [m] latlon : list-like Reference location in latitude and longitude [deg] wdir : float Wind direction with which the slice is aligned [deg] xrange : list or tuple, optional Range of x values over which slice (or None to use min/max) """ assert self.have_terrain, 'Need to call to_terrain()' assert ((xy is not None) ^ (latlon is not None)), 'Specify xy or latlon' if xy: refloc = xy elif latlon: x, y = self.to_xy(*latlon) refloc = (x, y) ang = 270 - wdir print('Slice through', refloc, 'at', ang, 'deg') ang *= np.pi / 180. # direction specific code imin = 0 if (xrange[0] is None) else np.where( self.x <= xrange[0])[0][-1] imax = None if (xrange[1] is None) else np.where( self.x > xrange[1])[0][0] x = self.x[imin:imax, 0] y = np.tan(ang) * (x - refloc[0]) + refloc[1] z = self.zfun(x, y, grid=False) return x - refloc[0], z def ytransect(self, xy=None, latlon=None, wdir=180.0, yrange=(None, None)): """Get terrain transect along x for a slice aligned with the specified wind direction and going through a specified reference point (defined by xy or latlon) Usage ===== xy : list or tuple Reference location in the UTM coordinate reference system [m] latlon : list-like Reference location in latitude and longitude [deg] wdir : float Wind direction with which the slice is aligned [deg] xrange : list or tuple, optional Range of x values over which slice (or None to use min/max) """ assert self.have_terrain, 'Need to call to_terrain()' assert ((xy is not None) ^ (latlon is not None)), 'Specify xy or latlon' if xy: refloc = xy elif latlon: x, y = self.to_xy(*latlon) refloc = (x, y) ang = 180 - wdir print('Slice through', refloc, 'at', ang, 'deg') ang *= np.pi / 180. # direction specific code jmin = 0 if (yrange[0] is None) else np.where( self.y <= yrange[0])[1][-1] jmax = None if (yrange[1] is None) else np.where( self.y > yrange[1])[1][0] y = self.y[0, jmin:jmax] x = refloc[0] - np.tan(ang) * (y - refloc[1]) z = self.zfun(x, y, grid=False) return y - refloc[1], z