def get_transform(xul, yul, dx, dy=None, rotation=0.): """Returns an affine.Affine instance that can be used to locate raster grids in space. See https://www.perrygeo.com/python-affine-transforms.html https://rasterio.readthedocs.io/en/stable/topics/migrating-to-v1.html Parameters ---------- xul : float x-coorindate of upper left corner of raster grid yul : float y-coorindate of upper left corner of raster grid dx : float cell spacing in the x-direction dy : float cell spacing in the y-direction rotation : rotation of the raster grid in degrees, clockwise Returns ------- affine.Affine instance """ if not rasterio: raise ModuleNotFoundError( 'This function requires rasterio. Please conda install rasterio.') if dy is None: dy = -dx return Affine(dx, 0., xul, 0., dy, yul) * \ Affine.rotation(rotation)
def defineSquare(self, x, y, alpha, length): transform0 = rasterio.open(self.imgdir + self.imgWithCoords(x, y), 'r').transform x_ras, y_ras = np.round((~transform0) * (x, y)) transform1 = transform0 * A.translation(x_ras, y_ras) * A.rotation(alpha) corners = [ transform1 * (0, 0), transform1 * (length, 0), transform1 * (length, length), transform1 * (0, length) ] return corners
def transform(self): """Rasterio-style affine transform object. https://www.perrygeo.com/python-affine-transforms.html """ if self.uniform: for param in ['dx', 'rotation', 'xul', 'dy', 'yul']: if self.__dict__[param] is None: print('This method requires a uniform grid and ' 'specification of xul, yul, dx, dy, and rotation.') return return Affine(self.dx, 0., self.xul, 0., -self.dy, self.yul) * Affine.rotation(self.rotation)
def test_get_transform(): dx = 5. height = 2 xll, yll = 0., 0. rotation = 30. xul = _xll_to_xul(xll, height * dx, rotation) yul = _yll_to_yul(yll, height * dx, rotation) transform = get_transform(xul=xul, yul=yul, dx=dx, dy=-dx, rotation=rotation) transform2 = Affine(dx, 0., xul, 0., -dx, yul) * \ Affine.rotation(rotation) assert transform == transform2
def newImage(self, x, y, alpha, length): square = self.defineSquare(x, y, alpha, length) imgs = self.imagesFromSquare(square) if len(imgs) < 1: return 0 datasets = [] road_datasets = [] for filename in imgs: datasets.append(rasterio.open(self.imgdir + filename, 'r')) road_datasets.append( rasterio.open(self.roaddir + self.roadName(filename), 'r')) if len(datasets) > 1: mergedImages, src_transform = rasterio.merge.merge(datasets) mergedRoads = rasterio.merge.merge(road_datasets)[0] else: mergedImages = datasets[0].read() mergedRoads = road_datasets[0].read() src_transform = datasets[0].transform #define destination transform from the upper left corner of the square translationVector = ~src_transform * square[0] dst_transform = src_transform * A.translation( *translationVector) * A.rotation(alpha) dest = np.zeros((mergedImages.shape[0], length, length), mergedImages.dtype) road_dest = np.zeros((length, length), mergedRoads.dtype) reproject(mergedImages, dest, src_transform=src_transform, src_crs=datasets[0].crs, dst_transform=dst_transform, dst_crs=datasets[0].crs, resampling=Resampling.nearest) reproject(mergedRoads, road_dest, src_transform=src_transform, src_crs=datasets[0].crs, dst_transform=dst_transform, dst_crs=datasets[0].crs, resampling=Resampling.nearest) return [dest, road_dest, dst_transform]
def export_array(modelgrid, filename, a, nodata=-9999, fieldname='value', **kwargs): """ Write a numpy array to Arc Ascii grid or shapefile with the model reference. Parameters ---------- filename : str Path of output file. Export format is determined by file extention. '.asc' Arc Ascii grid '.tif' GeoTIFF (requries rasterio package) '.shp' Shapefile a : 2D numpy.ndarray Array to export nodata : scalar Value to assign to np.nan entries (default -9999) fieldname : str Attribute field name for array values (shapefile export only). (default 'values') kwargs: keyword arguments to np.savetxt (ascii) rasterio.open (GeoTIFF) or flopy.export.shapefile_utils.write_grid_shapefile2 Notes ----- Rotated grids will be either be unrotated prior to export, using scipy.ndimage.rotate (Arc Ascii format) or rotation will be included in their transform property (GeoTiff format). In either case the pixels will be displayed in the (unrotated) projected geographic coordinate system, so the pixels will no longer align exactly with the model grid (as displayed from a shapefile, for example). A key difference between Arc Ascii and GeoTiff (besides disk usage) is that the unrotated Arc Ascii will have a different grid size, whereas the GeoTiff will have the same number of rows and pixels as the original. """ if filename.lower().endswith(".asc"): if len(np.unique(modelgrid.delr)) != len(np.unique(modelgrid.delc)) != 1 \ or modelgrid.delr[0] != modelgrid.delc[0]: raise ValueError('Arc ascii arrays require a uniform grid.') xoffset, yoffset = modelgrid.xoffset, modelgrid.yoffset cellsize = modelgrid.delr[0] # * self.length_multiplier fmt = kwargs.get('fmt', '%.18e') a = a.copy() a[np.isnan(a)] = nodata if modelgrid.angrot != 0: try: from scipy.ndimage import rotate a = rotate(a, modelgrid.angrot, cval=nodata) height_rot, width_rot = a.shape xmin, ymin, xmax, ymax = modelgrid.extent dx = (xmax - xmin) / width_rot dy = (ymax - ymin) / height_rot cellsize = np.max((dx, dy)) xoffset, yoffset = xmin, ymin except ImportError: print('scipy package required to export rotated grid.') filename = '.'.join( filename.split('.')[:-1]) + '.asc' # enforce .asc ending nrow, ncol = a.shape a[np.isnan(a)] = nodata txt = 'ncols {:d}\n'.format(ncol) txt += 'nrows {:d}\n'.format(nrow) txt += 'xllcorner {:f}\n'.format(xoffset) txt += 'yllcorner {:f}\n'.format(yoffset) txt += 'cellsize {}\n'.format(cellsize) # ensure that nodata fmt consistent w values txt += 'NODATA_value {}\n'.format(fmt) % (nodata) with open(filename, 'w') as output: output.write(txt) with open(filename, 'ab') as output: np.savetxt(output, a, **kwargs) print('wrote {}'.format(filename)) elif filename.lower().endswith(".tif"): if len(np.unique(modelgrid.delr)) != len(np.unique(modelgrid.delc)) != 1 \ or modelgrid.delr[0] != modelgrid.delc[0]: raise ValueError('GeoTIFF export require a uniform grid.') try: import rasterio from rasterio import Affine except ImportError: print('GeoTIFF export requires the rasterio package.') return dxdy = modelgrid.delc[0] # * self.length_multiplier trans = Affine.translation(modelgrid.xoffset, modelgrid.yoffset) * \ Affine.rotation(modelgrid.angrot) * \ Affine.scale(dxdy, -dxdy) # third dimension is the number of bands a = a.copy() if len(a.shape) == 2: a = np.reshape(a, (1, a.shape[0], a.shape[1])) if a.dtype.name == 'int64': a = a.astype('int32') dtype = rasterio.int32 elif a.dtype.name == 'int32': dtype = rasterio.int32 elif a.dtype.name == 'float64': dtype = rasterio.float64 elif a.dtype.name == 'float32': dtype = rasterio.float32 else: msg = 'ERROR: invalid dtype "{}"'.format(a.dtype.name) raise TypeError(msg) meta = {'count': a.shape[0], 'width': a.shape[2], 'height': a.shape[1], 'nodata': nodata, 'dtype': dtype, 'driver': 'GTiff', 'crs': modelgrid.proj4, 'transform': trans } meta.update(kwargs) with rasterio.open(filename, 'w', **meta) as dst: dst.write(a) print('wrote {}'.format(filename)) elif filename.lower().endswith(".shp"): from ..export.shapefile_utils import write_grid_shapefile2 epsg = kwargs.get('epsg', None) prj = kwargs.get('prj', None) if epsg is None and prj is None: epsg = modelgrid.epsg write_grid_shapefile2(filename, modelgrid, array_dict={fieldname: a}, nan_val=nodata, epsg=epsg, prj=prj)
def transform(self): """Rasterio Affine object (same as transform attribute of rasters). """ return Affine(self.delr[0], 0., self.xul, 0., -self.delc[0], self.yul) * \ Affine.rotation(angle=-self.angrot)
def get_nearest_point_on_grid(x, y, transform=None, xul=None, yul=None, dx=None, dy=None, rotation=0., offset='center', op=None): """ Parameters ---------- x : float x-coordinate of point y : float y-coordinate of point transform : Affine instance, optional Affine object instance describing grid xul : float x-coordinate of upper left corner of the grid yul : float y-coordinate of upper left corner of the grid dx : float grid spacing in the x-direction (along rows) dy : float grid spacing in the y-direction (along columns) rotation : float grid rotation about the upper left corner, in degrees clockwise from the x-axis offset : str, {'center', 'edge'} Whether the point on the grid represents a cell center or corner (edge). This argument is only used if xul, yul, dx, dy and rotation are supplied. If an Affine transform instance is supplied, it is assumed to already incorporate the offset. op : function, optional Function to convert fractional pixels to whole numbers (np.round, np.floor, np.ceiling). Defaults to np.round if offset == 'center'; otherwise defaults to np.floor. Returns ------- x_nearest, y_nearest : float Coordinates of nearest grid cell center. """ # get the closet (fractional) grid cell location # (in case the grid is rotated) if transform is None: transform = Affine(dx, 0., xul, 0., dy, yul) * \ Affine.rotation(rotation) if offset == 'center': transform *= Affine.translation(0.5, 0.5) x_raster, y_raster = ~transform * (x, y) if offset == 'center': op = np.round elif op is None: op = np.floor j = int(op(x_raster)) i = int(op(y_raster)) x_nearest, y_nearest = transform * (j, i) return x_nearest, y_nearest
def export_array(modelgrid, filename, a, nodata=-9999, fieldname='value', **kwargs): """Write a numpy array to Arc Ascii grid or shapefile with the model reference. Parameters ---------- filename : str Path of output file. Export format is determined by file extention. '.asc' Arc Ascii grid '.tif' GeoTIFF (requries rasterio package) '.shp' Shapefile a : 2D numpy.ndarray Array to export nodata : scalar Value to assign to np.nan entries (default -9999) fieldname : str Attribute field name for array values (shapefile export only). (default 'values') kwargs: keyword arguments to np.savetxt (ascii) rasterio.open (GeoTIFF) or flopy.export.shapefile_utils.write_grid_shapefile2 Notes ----- Rotated grids will be either be unrotated prior to export, using scipy.ndimage.rotate (Arc Ascii format) or rotation will be included in their transform property (GeoTiff format). In either case the pixels will be displayed in the (unrotated) projected geographic coordinate system, so the pixels will no longer align exactly with the model grid (as displayed from a shapefile, for example). A key difference between Arc Ascii and GeoTiff (besides disk usage) is that the unrotated Arc Ascii will have a different grid size, whereas the GeoTiff will have the same number of rows and pixels as the original. """ if filename.lower().endswith(".asc"): if len(np.unique(modelgrid.delr)) != len(np.unique(modelgrid.delc)) != 1 \ or modelgrid.delr[0] != modelgrid.delc[0]: raise ValueError('Arc ascii arrays require a uniform grid.') xoffset, yoffset = modelgrid.xoffset, modelgrid.yoffset cellsize = modelgrid.delr[0] # * self.length_multiplier fmt = kwargs.get('fmt', '%.18e') a = a.copy() a[np.isnan(a)] = nodata if modelgrid.angrot != 0: try: from scipy.ndimage import rotate a = rotate(a, modelgrid.angrot, cval=nodata) height_rot, width_rot = a.shape xmin, ymin, xmax, ymax = modelgrid.extent dx = (xmax - xmin) / width_rot dy = (ymax - ymin) / height_rot cellsize = np.max((dx, dy)) # cellsize = np.cos(np.radians(self.rotation)) * cellsize xoffset, yoffset = xmin, ymin except ImportError: print('scipy package required to export rotated grid.') pass filename = '.'.join( filename.split('.')[:-1]) + '.asc' # enforce .asc ending nrow, ncol = a.shape a[np.isnan(a)] = nodata txt = 'ncols {:d}\n'.format(ncol) txt += 'nrows {:d}\n'.format(nrow) txt += 'xllcorner {:f}\n'.format(xoffset) txt += 'yllcorner {:f}\n'.format(yoffset) txt += 'cellsize {}\n'.format(cellsize) # ensure that nodata fmt consistent w values txt += 'NODATA_value {}\n'.format(fmt) % (nodata) with open(filename, 'w') as output: output.write(txt) with open(filename, 'ab') as output: np.savetxt(output, a, **kwargs) print('wrote {}'.format(filename)) elif filename.lower().endswith(".tif"): if len(np.unique(modelgrid.delr)) != len(np.unique(modelgrid.delc)) != 1 \ or modelgrid.delr[0] != modelgrid.delc[0]: raise ValueError('GeoTIFF export require a uniform grid.') try: import rasterio from rasterio import Affine except: print('GeoTIFF export requires the rasterio package.') return dxdy = modelgrid.delc[0] # * self.length_multiplier trans = Affine.translation(modelgrid.xoffset, modelgrid.yoffset) * \ Affine.rotation(modelgrid.angrot) * \ Affine.scale(dxdy, -dxdy) # third dimension is the number of bands a = a.copy() if len(a.shape) == 2: a = np.reshape(a, (1, a.shape[0], a.shape[1])) if a.dtype.name == 'int64': a = a.astype('int32') dtype = rasterio.int32 elif a.dtype.name == 'int32': dtype = rasterio.int32 elif a.dtype.name == 'float64': dtype = rasterio.float64 elif a.dtype.name == 'float32': dtype = rasterio.float32 else: msg = 'ERROR: invalid dtype "{}"'.format(a.dtype.name) raise TypeError(msg) meta = {'count': a.shape[0], 'width': a.shape[2], 'height': a.shape[1], 'nodata': nodata, 'dtype': dtype, 'driver': 'GTiff', 'crs': modelgrid.proj4, 'transform': trans } meta.update(kwargs) with rasterio.open(filename, 'w', **meta) as dst: dst.write(a) print('wrote {}'.format(filename)) elif filename.lower().endswith(".shp"): from ..export.shapefile_utils import write_grid_shapefile2 epsg = kwargs.get('epsg', None) prj = kwargs.get('prj', None) if epsg is None and prj is None: epsg = modelgrid.epsg write_grid_shapefile2(filename, modelgrid, array_dict={fieldname: a}, nan_val=nodata, epsg=epsg, prj=prj)