def test_rasterize_supported_dtype(basic_geometry): """ Supported data types should return valid results """ with Env(): supported_types = ( ('int16', -32768), ('int32', -2147483648), ('uint8', 255), ('uint16', 65535), ('uint32', 4294967295), ('float32', 1.434532), ('float64', -98332.133422114) ) for dtype, default_value in supported_types: truth = np.zeros(DEFAULT_SHAPE, dtype=dtype) truth[2:4, 2:4] = default_value result = rasterize( [basic_geometry], out_shape=DEFAULT_SHAPE, default_value=default_value, dtype=dtype ) assert np.array_equal(result, truth) assert np.dtype(result.dtype) == np.dtype(truth.dtype) result = rasterize( [(basic_geometry, default_value)], out_shape=DEFAULT_SHAPE ) if np.dtype(dtype).kind == 'f': assert np.allclose(result, truth) else: assert np.array_equal(result, truth)
def test_rasterize_missing_shapes(): """Shapes are required for this operation.""" with rasterio.Env(): with pytest.raises(ValueError) as ex: rasterize([], out_shape=DEFAULT_SHAPE) assert 'No valid geometry objects' in str(ex.value)
def test_rasterize_unsupported_dtype(basic_geometry): """ Unsupported types should all raise exceptions """ with Env(): unsupported_types = ( ('int8', -127), ('int64', 20439845334323), ('float16', -9343.232) ) for dtype, default_value in unsupported_types: with pytest.raises(ValueError): rasterize( [basic_geometry], out_shape=DEFAULT_SHAPE, default_value=default_value, dtype=dtype ) with pytest.raises(ValueError): rasterize( [(basic_geometry, default_value)], out_shape=DEFAULT_SHAPE, dtype=dtype )
def test_rasterize_fill_value_dtype_mismatch(basic_geometry): """A fill value that doesn't match dtype should fail.""" with pytest.raises(ValueError): rasterize( [basic_geometry], out_shape=DEFAULT_SHAPE, fill=1000000, default_value=2, dtype=np.uint8 )
def test_rasterize_invalid_shapes(): """Invalid shapes should raise an exception rather than be skipped.""" with rasterio.Env(): with pytest.raises(ValueError) as ex: rasterize([{'foo': 'bar'}], out_shape=DEFAULT_SHAPE) assert 'Invalid geometry object' in str(ex.value)
def test_rasterize_invalid_fill_value(basic_geometry): """A fill value that requires an int64 should raise an exception.""" with pytest.raises(ValueError): rasterize( [basic_geometry], out_shape=DEFAULT_SHAPE, fill=1000000000000, default_value=2 )
def test_rasterize_invalid_out_dtype(basic_geometry): """ A non-supported data type for out should raise an exception """ out = np.zeros(DEFAULT_SHAPE, dtype=np.int64) with Env(): with pytest.raises(ValueError): rasterize([basic_geometry], out=out)
def test_rasterize_shapes_out_dtype_mismatch(basic_geometry): """ Shape values must be able to fit in data type for out """ out = np.zeros(DEFAULT_SHAPE, dtype=np.uint8) with Env(): with pytest.raises(ValueError): rasterize([(basic_geometry, 10000000)], out=out)
def test_rasterize_invalid_value(basic_geometry): """A shape value that requires an int64 should raise an exception.""" with pytest.raises(ValueError) as ex: rasterize( [(basic_geometry, 1000000000000)], out_shape=DEFAULT_SHAPE ) assert 'dtype must be one of' in str(ex.value)
def test_rasterize_invalid_out_shape(basic_geometry): """output array shape must be 2D.""" with pytest.raises(ValueError) as ex: rasterize([basic_geometry], out_shape=(1, 10, 10)) assert 'Invalid out_shape' in str(ex.value) with pytest.raises(ValueError) as ex: rasterize([basic_geometry], out_shape=(10,)) assert 'Invalid out_shape' in str(ex.value)
def test_rasterize_invalid_default_value(basic_geometry): """ A default value that requires an int64 should raise an exception """ with Env(): with pytest.raises(ValueError): rasterize( [basic_geometry], out_shape=DEFAULT_SHAPE, default_value=1000000000000 )
def test_rasterize(basic_geometry, basic_image_2x2): """Rasterize operation should succeed for both an out_shape and out.""" assert np.array_equal( basic_image_2x2, rasterize([basic_geometry], out_shape=DEFAULT_SHAPE) ) out = np.zeros(DEFAULT_SHAPE) rasterize([basic_geometry], out=out) assert np.array_equal(basic_image_2x2, out)
def set_roi(self, shape=None, geometry=None, crs=wgs84, grid=None, corners=None, noerase=False): """Set a region of interest for the dataset. If set succesfully, a ROI is simply a mask of the same size as the dataset's grid, obtained with the .roi attribute. I haven't decided yet if the data should be masekd out when a ROI has been set. Parameters ---------- shape: path to a shapefile geometry: a shapely geometry crs: the crs of the geometry grid: a Grid object corners: a ((x0, y0), (x1, y1)) tuple of the corners of the square to subset the dataset to. The coordinates are not expressed in wgs84, set the crs keyword noerase: set to true in order to add the new ROI to the previous one """ # The rois are always defined on the original grids, but of course # we take that into account when a subset is set (see roi # decorator below) ogrid = self._ogrid # Initial mask if noerase and (self.roi is not None): mask = self.roi else: mask = np.zeros((ogrid.ny, ogrid.nx), dtype=np.int16) # Several cases if shape is not None: gdf = sio.read_shapefile(shape) gis.transform_geopandas(gdf, to_crs=ogrid.corner_grid, inplace=True) with rasterio.Env(): mask = features.rasterize(gdf.geometry, out=mask) if geometry is not None: geom = gis.transform_geometry(geometry, crs=crs, to_crs=ogrid.corner_grid) with rasterio.Env(): mask = features.rasterize(np.atleast_1d(geom), out=mask) if grid is not None: _tmp = np.ones((grid.ny, grid.nx), dtype=np.int16) mask = ogrid.map_gridded_data(_tmp, grid, out=mask).filled(0) if corners is not None: cgrid = self._ogrid.center_grid xy0, xy1 = corners x0, y0 = cgrid.transform(*xy0, crs=crs, nearest=True) x1, y1 = cgrid.transform(*xy1, crs=crs, nearest=True) mask[np.min([y0, y1]):np.max([y0, y1])+1, np.min([x0, x1]):np.max([x0, x1])+1] = 1 self.roi = mask
def test_rasterize(basic_geometry, basic_image_2x2): """ Rasterize operation should succeed for both an out_shape and out """ with rasterio.drivers(): assert numpy.array_equal( basic_image_2x2, rasterize([basic_geometry], out_shape=DEFAULT_SHAPE) ) out = numpy.zeros(DEFAULT_SHAPE) rasterize([basic_geometry], out=out) assert numpy.array_equal(basic_image_2x2, out)
def gdf_to_rst(gdf, trs, w, h, path_to_desc): ''' Convert a view of a gdf to a color-coded numpy array. ''' unitcolor = generate_unitcolor_lookup(path_to_desc) rz = rasterize([(x.geometry, unitcolor.R[gdf.mapunit[i]]) for i, x in gdf.iterrows()], out_shape=(w, h), transform=trs) gz = rasterize([(x.geometry, unitcolor.G[gdf.mapunit[i]]) for i, x in gdf.iterrows()], out_shape=(w, h), transform=trs) bz = rasterize([(x.geometry, unitcolor.B[gdf.mapunit[i]]) for i, x in gdf.iterrows()], out_shape=(w, h), transform=trs) return np.dstack((rz, gz, bz))
def test_rasterize_geometries(): """ Make sure that geometries are correctly rasterized according to parameters """ rows = cols = 10 transform = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0) geometry = { 'type': 'Polygon', 'coordinates': [[(2, 2), (2, 4.25), (4.25, 4.25), (4.25, 2), (2, 2)]] } with rasterio.drivers(): # we expect a subset of the pixels using default mode result = rasterize([geometry], out_shape=(rows, cols)) truth = numpy.zeros((rows, cols)) truth[2:4, 2:4] = 1 assert numpy.array_equal(result, truth) out = numpy.zeros((rows, cols)) result = rasterize([geometry], out=out, default_value=1) assert numpy.array_equal(out, truth) # we expect all touched pixels result = rasterize( [geometry], out_shape=(rows, cols), all_touched=True ) truth = numpy.zeros((rows, cols)) truth[2:5, 2:5] = 1 assert numpy.array_equal(result, truth) # we expect the pixel value to match the one we pass in value = 5 result = rasterize([(geometry, value)], out_shape=(rows, cols)) truth = numpy.zeros((rows, cols)) truth[2:4, 2:4] = value assert numpy.array_equal(result, truth) # Check the fill and default transform. # we expect the pixel value to match the one we pass in value = 5 result = rasterize( [(geometry, value)], out_shape=(rows, cols), fill=1 ) truth = numpy.ones((rows, cols)) truth[2:4, 2:4] = value assert numpy.array_equal(result, truth)
def get_costRaster(self,mbb,transform,psize,crs): x_min = mbb[0] y_min = mbb[1] x_max = mbb[2] y_max = mbb[3] x_res = int((x_max - x_min) / psize) y_res = int((y_max - y_min) / psize) features = self.get_projected_gjson(crs)['features'] feats = [g['geometry'] for g in features] raster = rasterize( feats, out_shape=(y_res, x_res), transform=transform, dtype='float32', all_touched=True) if self.interpretation == 'boundary': #1000 is just an arbitrarily large number, could try np.inf raster = np.where(raster==0,1000.0,self.cost) print 'at boundary', raster elif self.interpretation == 'crossing': raster = raster*(self.cost/psize) print 'at all else', raster return raster
def main(infile, outfile, bbox, res): """ Rasterize vector with constraints. """ x_min, y_min, x_max, y_max = bbox width = int((x_max - x_min) / res) if width % 2 is not 0: width += 1 res = (x_max - x_min) / width height = int((y_max - y_min) / res) with fio.open(infile) as src: aff = affine.Affine(res, 0.0, x_min, 0.0, -res, y_max) meta = { 'driver': 'GTiff', 'count': 1, 'crs': src.crs, 'affine': aff, 'transform': rio.guard_transform(aff), 'COMPRESS': 'DEFLATE', 'INTERLEAVE': 'BAND', 'BIGTIFF': 'YES', 'TILED': 'NO', 'PREDICTOR': '2', 'dtype': rio.ubyte, 'nodata': None, 'width': width, 'height': height } print(width) print(height) exit() with fio.open(infile) as src, rio.open(outfile, 'w', **meta) as dst: length = len([i for i in dst.block_windows()]) with click.progressbar(dst.block_windows(), length=length) as block_windows: for _, window in block_windows: aff = dst.window_transform(window) ((row_min, row_max), (col_min, col_max)) = window x_min, y_min = aff * (col_min, row_max) x_max, y_max = aff * (col_max, row_min) data = np.zeros((row_max - row_min, col_max - col_min)) try: data = rasterize( shapes=(feat['geometry'] for feat in src.filter(bbox=(x_min, y_min, x_max, y_max))), fill=0, out=data, transform=aff, all_touched=True, default_value=1, dtype=dst.meta['dtype'] ) except ValueError: pass dst.write(data, window=window)
def test_rasterize_all_touched(basic_geometry, basic_image): assert np.array_equal( basic_image, rasterize( [basic_geometry], out_shape=DEFAULT_SHAPE, all_touched=True ) )
def test_rasterize_skip_invalid_geom(geojson_polygon, basic_image_2x2): """Rasterize operation should succeed for at least one valid geometry and should skip any invalid or empty geometries with an error.""" with pytest.warns(UserWarning, match="Invalid or empty shape"): out = rasterize([geojson_polygon, {'type': 'Polygon', 'coordinates': []}], out_shape=DEFAULT_SHAPE) assert np.array_equal(out, basic_image_2x2)
def handle(self, *args, **options): js = Jurisdiction.objects.all() for j in js: image = str(settings.BASE_DIR.path('config/static/jurisdictions/%s.png' % j.id)) image_aux = image + '.aux.xml' if not os.path.exists(image): obj = { 'type': 'Feature', 'geometry': json.loads(j.geometry.geojson) } rasterize(obj, image, fill=0, default_value=180, res=(0.001,), driver='PNG') # Remove all .aux.xml files os.unlink(image_aux)
def test_rasterize_geometries_symmetric(): """Make sure that rasterize is symmetric with shapes.""" transform = (1.0, 0.0, 0.0, 0.0, -1.0, 0.0) truth = np.zeros(DEFAULT_SHAPE, dtype=rasterio.ubyte) truth[2:5, 2:5] = 1 s = shapes(truth, transform=transform) result = rasterize(s, out_shape=DEFAULT_SHAPE, transform=transform) assert np.array_equal(result, truth)
def _rasterize_id( df, value, out_shape, out_transform, background_value=0 ): from rasterio.features import rasterize geom = df.geometry out = rasterize( ( ( g, value ) for g in geom ), out_shape=out_shape, transform=out_transform, fill=background_value ) return out
def test_rasterize_point(geojson_point): expected = np.zeros(shape=DEFAULT_SHAPE, dtype='uint8') expected[2, 2] = 1 assert np.array_equal( rasterize([geojson_point], out_shape=DEFAULT_SHAPE), expected )
def main(no_data_val=-9999, fname_out='county_level_pop_out.tif', year=2017, grwth_rate_fct=0.0): ''' ''' # Get raster reference project from configuration file (ref_proj) with self.input()['ref_proj'].open('r') as fid: ref_proj = json.load(fid) # Read the shapefile with total population data shp_pop_obj = self.input()['geoshape_file'] pop_data = gpd.read_file(shp_pop_obj.path) # ------------------------------------- # Calculate Population from 2008 Census # data and growth rate # Apply growth rate adjustment # ------------------------------------- nstep = self.year - 2008 pop_data['GROWTHRATE'] = pop_data['GROWTHRATE'] * (1 + self.grwth_rate_fct) pop_data['Population'] = pop_data['SS2008'] * np.exp(pop_data['GROWTHRATE'] * nstep) pop_data['Population'] = pop_data['Population'].apply(lambda x: np.round(x)) pop_data['Population'] = pop_data['Population'].astype('int32') # ----------------------------------------------------------------------- # Rasterize Total population along with age and gender ratios # Total population and age & gender ratios should go into one raster file # ----------------------------------------------------------------------- (ncols, nrows) = ref_proj['ncolsrows'] rast_meta = { 'driver': 'GTiff', 'height': nrows, 'width': ncols, 'count': 1, 'dtype': np.int32, 'crs': ref_proj['srs'], 'transform': ref_proj['pixel'], 'nodata': -9999.0, } # Open raster file for writing with self.output().open("wb") as fout: with rasterio.open(fout.name, 'w', **rast_meta) as out_raster: out_array = out_raster.read(1) # Rasterize geopandas geometries with population values shapes = ( (geom, pop_values) for geom, pop_values in zip(pop_data['geometry'], pop_data['Population']) ) burned_data = features.rasterize( shapes=shapes, fill=0, out=out_array, transform=out_raster.transform ) out_raster.write_band(1, burned_data) # Tag raster bands band_tags = {'band_1': 'Total_Population'} out_raster.update_tags(**band_tags)
def test_rasterize_geomcollection(geojson_geomcollection): expected = np.zeros(shape=DEFAULT_SHAPE, dtype='uint8') expected[0:1, 0:1] = 1 expected[2:4, 2:4] = 1 assert np.array_equal( rasterize([geojson_geomcollection], out_shape=DEFAULT_SHAPE), expected )
def test_rasterize_geo_interface_2(geojson_polygon): """Objects that implement the geo interface should rasterize properly""" class GeoObj: @property def __geo_interface__(self): return geojson_polygon assert rasterize([GeoObj()], out_shape=DEFAULT_SHAPE).sum() == 4
def test_rasterize_mismatched_dtype(basic_geometry): """Mismatched values and dtypes should raise exceptions.""" mismatched_types = (('uint8', 3.2423), ('uint8', -2147483648)) for dtype, default_value in mismatched_types: with pytest.raises(ValueError): rasterize( [basic_geometry], out_shape=DEFAULT_SHAPE, default_value=default_value, dtype=dtype ) with pytest.raises(ValueError): rasterize( [(basic_geometry, default_value)], out_shape=DEFAULT_SHAPE, dtype=dtype )
def rasterize_geom(geom, like, all_touched=False): geoms = [(geom, 1)] rv_array = features.rasterize( geoms, out_shape=like.shape, transform=like.affine, fill=0, all_touched=all_touched) return rv_array
def rasterize_geom(geom, shape, affine, all_touched=False, scale=None): geoms = [(geom, 1)] rv_array = features.rasterize( geoms, out_shape=shape, transform=affine, fill=0, all_touched=all_touched) return rv_array
print(out_meta) out_meta.update(dtype=rasterio.int16, driver='GTiff') mask = features.geometry_mask( [feature["feature"]["geometry"] for feature in list_of_dicts], src_rst.shape, transform=src_rst.transform, all_touched=True, invert=True) new_dsm = np.copy(np.squeeze( dsm)) #Forstaer ikke hvorfor, men dsm'en har en ekstra dimension, #som jeg fjerner med squeeze, saa den passer med result dsm'en with rasterio.Env(): result = features.rasterize( ((feature['feature']['geometry'], np.int(feature['feature']['properties']['hoejde']) * 1000) for feature in list_of_dicts), out_shape=src_rst.shape, transform=src_rst.transform, all_touched=True) new_dsm[mask] = result[mask] with Session(gisdb=grassdb, location="test", create_opts=vrt): import grass.script.array as garray r_viewshed = Module('r.viewshed') r_out_gdal = Module('r.out.gdal') from_np_raster = garray.array() from_np_raster[...] = new_dsm from_np_raster.write('ny_rast', overwrite=True) print(from_np_raster) gcore.run_command('r.viewshed', overwrite=True,
def rasterize(ctx, files, output, driver, like, bounds, dimensions, res, src_crs, all_touched, default_value, fill, property, creation_options): """Rasterize GeoJSON into a new or existing raster. If the output raster exists, rio-rasterize will rasterize feature values into all bands of that raster. The GeoJSON is assumed to be in the same coordinate reference system as the output unless --src-crs is provided. --default_value or property values when using --property must be using a data type valid for the data type of that raster. If a template raster is provided using the --like option, the affine transform and data type from that raster will be used to create the output. Only a single band will be output. The GeoJSON is assumed to be in the same coordinate reference system unless --src-crs is provided. --default_value or property values when using --property must be using a data type valid for the data type of that raster. --driver, --bounds, --dimensions, and --res are ignored when output exists or --like raster is provided If the output does not exist and --like raster is not provided, the input GeoJSON will be used to determine the bounds of the output unless provided using --bounds. --dimensions or --res are required in this case. If --res is provided, the bottom and right coordinates of bounds are ignored. Note: The GeoJSON is not projected to match the coordinate reference system of the output or --like rasters at this time. This functionality may be added in the future. """ from rasterio._base import is_geographic_crs, is_same_crs from rasterio.features import rasterize from rasterio.features import bounds as calculate_bounds verbosity = (ctx.obj and ctx.obj.get('verbosity')) or 1 output, files = resolve_inout(files=files, output=output) has_src_crs = src_crs is not None src_crs = src_crs or 'EPSG:4326' # If values are actually meant to be integers, we need to cast them # as such or rasterize creates floating point outputs if default_value == int(default_value): default_value = int(default_value) if fill == int(fill): fill = int(fill) with rasterio.drivers(CPL_DEBUG=verbosity > 2): def feature_value(feature): if property and 'properties' in feature: return feature['properties'].get(property, default_value) return default_value with click.open_file(files.pop(0) if files else '-') as gj_f: geojson = json.loads(gj_f.read()) if 'features' in geojson: geometries = [] for f in geojson['features']: geometries.append((f['geometry'], feature_value(f))) elif 'geometry' in geojson: geometries = ((geojson['geometry'], feature_value(geojson)), ) else: raise click.BadParameter('Invalid GeoJSON', param=input, param_hint='input') geojson_bounds = geojson.get('bbox', calculate_bounds(geojson)) if os.path.exists(output): with rasterio.open(output, 'r+') as out: if has_src_crs and not is_same_crs(src_crs, out.crs): raise click.BadParameter( 'GeoJSON does not match crs of ' 'existing output raster', param='input', param_hint='input') if _disjoint_bounds(geojson_bounds, out.bounds): click.echo( "GeoJSON outside bounds of existing output " "raster. Are they in different coordinate " "reference systems?", err=True) meta = out.meta.copy() result = rasterize(geometries, out_shape=(meta['height'], meta['width']), transform=meta.get('affine', meta['transform']), all_touched=all_touched, dtype=meta.get('dtype', None), default_value=default_value, fill=fill) for bidx in range(1, meta['count'] + 1): data = out.read_band(bidx, masked=True) # Burn in any non-fill pixels, and update mask accordingly ne = result != fill data[ne] = result[ne] data.mask[ne] = False out.write_band(bidx, data) else: if like is not None: template_ds = rasterio.open(like) if has_src_crs and not is_same_crs(src_crs, template_ds.crs): raise click.BadParameter( 'GeoJSON does not match crs of ' '--like raster', param='input', param_hint='input') if _disjoint_bounds(geojson_bounds, template_ds.bounds): click.echo( "GeoJSON outside bounds of --like raster. " "Are they in different coordinate reference " "systems?", err=True) kwargs = template_ds.meta.copy() kwargs['count'] = 1 template_ds.close() else: bounds = bounds or geojson_bounds if is_geographic_crs(src_crs): if (bounds[0] < -180 or bounds[2] > 180 or bounds[1] < -80 or bounds[3] > 80): raise click.BadParameter( "Bounds are beyond the valid extent for " "EPSG:4326.", ctx, param=bounds, param_hint='--bounds') if dimensions: width, height = dimensions res = ((bounds[2] - bounds[0]) / float(width), (bounds[3] - bounds[1]) / float(height)) else: if not res: raise click.BadParameter( 'pixel dimensions are required', ctx, param=res, param_hint='--res') elif len(res) == 1: res = (res[0], res[0]) width = max( int(ceil((bounds[2] - bounds[0]) / float(res[0]))), 1) height = max( int(ceil((bounds[3] - bounds[1]) / float(res[1]))), 1) src_crs = src_crs.upper() if not src_crs.count('EPSG:'): raise click.BadParameter( 'invalid CRS. Must be an EPSG code.', ctx, param=src_crs, param_hint='--src_crs') kwargs = { 'count': 1, 'crs': src_crs, 'width': width, 'height': height, 'transform': Affine(res[0], 0, bounds[0], 0, -res[1], bounds[3]), 'driver': driver } kwargs.update(**creation_options) result = rasterize(geometries, out_shape=(kwargs['height'], kwargs['width']), transform=kwargs.get('affine', kwargs['transform']), all_touched=all_touched, dtype=kwargs.get('dtype', None), default_value=default_value, fill=fill) if 'dtype' not in kwargs: kwargs['dtype'] = result.dtype kwargs['nodata'] = fill with rasterio.open(output, 'w', **kwargs) as out: out.write_band(1, result)
def negative_buffer_and_small_filter(params): """ Applies a negative buffer to wv2 labels since some are too close together and produce conjoined instances when connected components is run (even after erosion/dilation). This may not get rid of all conjoinments and should be adjusted. It relies too on the source projection of the label file to calculate distances for the negative buffer. Currently hardcodes in projections, need to look up utm pojection based on spatial location somehow if I'm to extend this to work with labels anywhere. Returns rasterized labels that are ready to be gridded """ class_int = params['label_vals']['class'] neg_buffer = float(params['label_vals']['neg_buffer']) small_area_filter = float(params['label_vals']['small_area_filter']) big_area_filter = float(params['label_vals']['big_area_filter']) # This is a helper used with sorted for a list of strings by specific indices in # each string. Was used for a long path that ended with a file name # Not needed here but may be with different source imagery and labels # def takefirst_two(elem): # return int(elem[-12:-10]) items = os.listdir(SOURCE_LABELS) labels = [] for name in items: if name.endswith(".shp"): labels.append(os.path.join(SOURCE_LABELS,name)) shp_list = sorted(labels) # need to use Source imagery for geotransform data for rasterized shapes, didn't preserve when save imgs to reorder scenes = os.listdir(SOURCE_IMGS) #hard coded season because it will take more tinkering to have the model and preprocessing work with multichannel scenes = [scene for scene in scenes if 'GS' in scene] img_list = [] for name in scenes: img_list.append(os.path.join(SOURCE_IMGS,name)) img_list = sorted(img_list) for shp_path, img_path in zip(shp_list, img_list): shp_frame = gpd.read_file(shp_path) # keeps the class of interest if it is there and the polygon of raster extent shp_frame = shp_frame[(shp_frame['class'] == class_int) | (shp_frame['DN'] == 1)] with rasterio.open(img_path) as rast: meta = rast.meta.copy() meta.update(compress="lzw") meta['count'] = 1 tifname = os.path.splitext(os.path.basename(shp_path))[0] + '.tif' rasterized_name = os.path.join(NEG_BUFFERED, tifname) with rasterio.open(rasterized_name, 'w+', **meta) as out: out_arr = out.read(1) # we get bounds to deterimine which projection to use for neg buffer shp_frame.loc[0,'DN'] = 0 shp_frame.loc[1:,'DN'] = 1 maxx_bound = shp_frame.bounds.maxx.max() minx_bound = shp_frame.bounds.minx.min() #need to project to correct utm before buffering in units of meters if maxx_bound >= 30 and minx_bound>= 30: shp_frame = shp_frame.to_crs({'init': 'epsg:32736'}) shp_frame['geometry'] = shp_frame['geometry'].buffer(neg_buffer) shp_frame['Shape_Area'] = shp_frame.area shp_frame = shp_frame.to_crs({'init': 'epsg:4326'}) else: shp_frame = shp_frame.to_crs({'init': 'epsg:32735'}) shp_frame['geometry'] = shp_frame['geometry'].buffer(neg_buffer) shp_frame['Shape_Area'] = shp_frame.area shp_frame = shp_frame.to_crs({'init': 'epsg:4326'}) if len(shp_frame) == 1: # added for case, where entire wv2 scenes have no foreground class and need empty masks shapes = ((geom,value) for geom, value in zip(shp_frame.geometry, shp_frame.DN)) burned = features.rasterize(shapes=shapes, fill=0, out=out_arr, transform=out.transform, default_value=1) burned[burned < 0] = 0 out.write_band(1, burned) else: # added for center pivot case, where entire wv2 scenes have no center pivots and need empty masks shp_frame = shp_frame.loc[shp_frame.Shape_Area > small_area_filter] shp_frame = shp_frame.loc[shp_frame.Shape_Area < big_area_filter] shp_frame = shp_frame[shp_frame.DN==1] # get rid of extent polygon # https://gis.stackexchange.com/questions/151339/rasterize-a-shapefile-with-geopandas-or-fiona-python#151861 shapes = ((geom,value) for geom, value in zip(shp_frame.geometry, shp_frame.DN)) burned = features.rasterize(shapes=shapes, fill=0, out=out_arr, transform=out.transform, default_value=1) burned[burned < 0] = 0 out.write_band(1, burned) print('Done applying negbuff of {negbuff} and filtering small labels of area less than {area}'.format(negbuff=neg_buffer,area=small_area_filter))
def test_rasterize_geo_interface(geojson_polygon, basic_image_2x2): assert np.array_equal( rasterize([MockGeoInterface(geojson_polygon)], out_shape=DEFAULT_SHAPE), basic_image_2x2 )
def test_rasterize_shapes_out_dtype_mismatch(basic_geometry): """Shape values must be able to fit in data type for out.""" out = np.zeros(DEFAULT_SHAPE, dtype=np.uint8) with pytest.raises(ValueError): rasterize([(basic_geometry, 10000000)], out=out)
def burn_polygons_to_a_raster(ref_raster, polygons, burn_values, save_path, date_type='uint8', xres=None, yres=None, extent=None, ref_prj=None, nodata=None): # if save_path is None, it will return the array, not saving to disk # burn polygons to a new raster # if ref_raster is None, we must set xres and yres, and extent (read from polygons) and ref_prj (from shapefile) # extent: (minx, miny, maxx, maxy) if save_path is not None and os.path.isfile(save_path): print('%s exist, skip burn_polygons_to_a_raster' % save_path) return save_path if isinstance(burn_values, int): values = [burn_values] * len(polygons) elif isinstance(burn_values, list): values = burn_values if len(burn_values) != len(polygons): raise ValueError( 'polygons and burn_values do not have the same size') else: raise ValueError('unkonw type of burn_values') if date_type == 'uint8': save_dtype = rasterio.uint8 np_dtype = np.uint8 elif date_type == 'uint16': save_dtype = rasterio.uint16 np_dtype = np.uint16 elif date_type == 'int32': save_dtype = rasterio.int32 np_dtype = np.int32 else: raise ValueError('not yet support') if ref_raster is None: # exent (minx, miny, maxx, maxy) height, width = math.ceil((extent[3] - extent[1]) / yres), math.ceil( (extent[2] - extent[0]) / xres) burn_out = np.zeros((height, width), dtype=np_dtype) if nodata is not None: burn_out[:] = nodata # rasterize the shapes burn_shapes = [(item_shape, item_int) for (item_shape, item_int) in zip(polygons, values)] ## new_transform = (burn_boxes[0], resX, 0, burn_boxes[3], 0, -resY ) # (X_min, resX, 0, Y_max, 0, -resY) # GDAL-style transforms, have been deprecated after raster 1.0 # affine.Affine() vs. GDAL-style geotransforms: https://rasterio.readthedocs.io/en/stable/topics/migrating-to-v1.html transform = (xres, 0, extent[0], 0, -yres, extent[3] ) # (resX, 0, X_min, 0, -resY, Y_max) out_label = rasterize(burn_shapes, out=burn_out, transform=transform, fill=0, all_touched=False, dtype=save_dtype) if save_path is None: return out_label with rasterio.open(save_path, 'w', driver='GTiff', height=height, width=width, count=1, dtype=save_dtype, crs=ref_prj, transform=transform, nodata=nodata) as dst: dst.write_band(1, out_label.astype(save_dtype)) else: with rasterio.open(ref_raster) as src: transform = src.transform burn_out = np.zeros((src.height, src.width), dtype=np_dtype) if nodata is not None: burn_out[:] = nodata # rasterize the shapes burn_shapes = [(item_shape, item_int) for (item_shape, item_int) in zip(polygons, values)] # out_label = rasterize(burn_shapes, out=burn_out, transform=transform, fill=0, all_touched=False, dtype=save_dtype) if save_path is None: return out_label # test: save it to disk kwargs = src.meta kwargs.update(dtype=save_dtype, count=1, nodata=nodata) # # remove nodta in the output # if 'nodata' in kwargs.keys(): # del kwargs['nodata'] with rasterio.open(save_path, 'w', **kwargs) as dst: dst.write_band(1, out_label.astype(save_dtype))
def rasterize_polygons_to_ref_raster(ref_raster, poly_path, burn_value, attribute_name, save_path, datatype='Byte', ignore_edge=True): ''' rasterize polygons to a raster with the same size of ref_raster :param ref_raster: reference raster :param poly_path: shapefile path :param burn_value: if attribute_name is None, then we will use burn_value :param attribute_name: the attribute to be burned into raster :param save_path: :param datatype: datatype :param ignore_edge: if True, will burn a buffer areas (~5 pixel as 255) :return: ''' if isinstance(burn_value, int) is False or isinstance(burn_value, float): raise ValueError('The burn value should be int or float') # need to check the projection # need to check: the shape file and raster should have the same projection. if get_projection_proj4(ref_raster) != get_projection_proj4(poly_path): raise ValueError( 'error, the input raster (e.g., %s) and vector (%s) files don\'t have the same projection' % (ref_raster, poly_path)) # read polygons (can read geojson file directly) shapefile = gpd.read_file(poly_path) polygons = shapefile.geometry.values if attribute_name is None: class_labels = [burn_value] * len(polygons) else: class_labels = shapefile[attribute_name].tolist() # output datatype: https://gdal.org/programs/gdal_rasterize.html # -ot {Byte/Int16/UInt16/UInt32/Int32/Float32/Float64/ # CInt16/CInt32/CFloat32/CFloat64}] if datatype == 'Byte': dtype = rasterio.uint8 elif datatype == 'UInt16': dtype = rasterio.uint16 else: dtype = rasterio.int32 with rasterio.open(ref_raster) as src: transform = src.transform burn_out = np.zeros((src.height, src.width)) out_label = burn_out if len(polygons) > 0: if ignore_edge is False: # rasterize the shapes burn_shapes = [ (item_shape, item_class_int) for (item_shape, item_class_int) in zip(polygons, class_labels) ] # out_label = rasterize(burn_shapes, out=burn_out, transform=transform, fill=0, all_touched=False, dtype=dtype) else: # burn a buffer area (4 to 5 pixel) of edge as 255 xres, yres = src.res outer_list = [poly.buffer(2 * xres) for poly in polygons] inner_list = [poly.buffer(-2 * xres) for poly in polygons] # after negative buffer, some small polygons may be deleted, need to check inner_list = [ item for item in inner_list if item.is_valid and item.is_empty is False ] if len(inner_list) > 0: # rasterize the outer burn_shapes = [(item_shape, 255) for item_shape in outer_list] out_label = rasterize(burn_shapes, out=burn_out, transform=transform, fill=0, all_touched=False, dtype=dtype) # rasterize the inner parts burn_shapes = [ (item_shape, item_class_int) for (item_shape, item_class_int) in zip(inner_list, class_labels) ] out_label = rasterize(burn_shapes, out=out_label, transform=transform, fill=0, all_touched=False, dtype=dtype) else: print( 'After negative buffer operation, there is no polygon valid for rasterizing' ) else: print('Warning, no Polygon in %s, will save a dark label' % poly_path) # save it to disk kwargs = src.meta kwargs.update(dtype=dtype, count=1) if 'nodata' in kwargs.keys(): del kwargs['nodata'] with rasterio.open(save_path, 'w', **kwargs) as dst: dst.write_band(1, out_label.astype(dtype)) return True
def SwathProfile(axis, gid, geometry): """ Calculate Elevation Swath Profile for Valley Unit (axis, gid) """ refaxis_shapefile = os.path.join(workdir, 'AX%03d_REFAXIS.shp' % axis) network_shapefile = os.path.join(workdir, 'RHT_AXIS_TILED.shp') elevation_raster = '/var/local/fct/RMC/RGEALTI.tif' with rio.open(elevation_raster) as ds: # Read elevations click.echo('Read elevations') window = as_window(geometry.bounds, ds.transform) elevations = ds.read(1, window=window, boundless=True, fill_value=ds.nodata) transform = ds.transform * ds.transform.translation( window.col_off, window.row_off) height, width = elevations.shape buf = geometry.buffer(100.0) # Create DGO mask mask = features.rasterize([geometry], out_shape=elevations.shape, transform=transform, fill=0, default_value=1, dtype='uint8') # Measure and distance from reference axis click.echo('Measure and distance from reference axis') refaxis_pixels = list() def accept_pixel(i, j): x, y = ta.xy(i, j, transform) return all([ i >= -height, i < 2 * height, j >= -width, j < 2 * width ]) and buf.contains(Point(x, y)) coord = itemgetter(0, 1) # mmin = float('inf') # mmax = float('-inf') with fiona.open(refaxis_shapefile) as fs: for feature in fs: m0 = feature['properties']['M0'] length = feature['properties']['LENGTH'] # if m0 < mmin: # mmin = m0 # if m0 + length > mmax: # mmax = m0 + length coordinates = np.array([ coord(p) + (m0, ) for p in reversed(feature['geometry']['coordinates']) ], dtype='float32') coordinates[1:, 2] = m0 + np.cumsum( np.linalg.norm(coordinates[1:, :2] - coordinates[:-1, :2], axis=1)) coordinates[:, :2] = ta.worldtopixel(coordinates[:, :2], transform, gdal=False) for a, b in zip(coordinates[:-1], coordinates[1:]): for i, j, m in rasterize_linestringz(a, b): if accept_pixel(i, j): # distance[i, j] = 0 # measure[i, j] = m refaxis_pixels.append((i, j, m)) # mmin = math.floor(mmin / mdelta) * mdelta # mmax = math.ceil(mmax / mdelta) * mdelta mask1 = np.float32(mask) mask1[mask1 == 0] = ds.nodata measure, distance = nearest_value_and_distance( np.flip(np.array(refaxis_pixels), axis=0), mask1, ds.nodata) distance = 5.0 * distance distance[(elevations == ds.nodata) | (mask == 0)] = ds.nodata measure[(elevations == ds.nodata) | (mask == 0)] = ds.nodata # Relative elevations (Height above nearest drainage) click.echo('Relative elevations (Height above nearest drainage)') stream_pixels = list() def accept_feature(feature): properties = feature['properties'] return properties['AXH'] == axis coord = itemgetter(0, 1, 2) unique = set() with fiona.open(network_shapefile) as fs: for feature in fs: if accept_feature(feature): coordinates = np.array( [coord(p) for p in feature['geometry']['coordinates']], dtype='float32') coordinates[:, :2] = ta.worldtopixel(coordinates[:, :2], transform, gdal=False) for a, b in zip(coordinates[:-1], coordinates[1:]): for i, j, z in rasterize_linestringz(a, b): if accept_pixel(i, j) and (i, j) not in unique: # distance[i, j] = 0 # measure[i, j] = m # z = elevations[i, j] stream_pixels.append((i, j, z)) unique.add((i, j)) reference, _ = nearest_value_and_distance(np.array(stream_pixels), mask1, ds.nodata) relz = elevations - reference relz[(elevations == ds.nodata) | (mask == 0)] = ds.nodata # Swath profiles click.echo('Swath profiles') xbins = np.arange(np.min(distance[mask == 1]), np.max(distance[mask == 1]), 10.0) binned = np.digitize(distance, xbins) x = 0.5 * (xbins[1:] + xbins[:-1]) density = np.zeros_like(x, dtype='int32') # Profile density for i in range(1, len(xbins)): density[i - 1] = np.sum((mask == 1) & (binned == i)) # Absolute elevation swath profile swath_absolute = np.full((len(xbins) - 1, 5), np.nan, dtype='float32') for i in range(1, len(xbins)): swath_elevations = elevations[(mask == 1) & (binned == i)] if swath_elevations.size: swath_absolute[i - 1, :] = np.percentile( swath_elevations, [5, 25, 50, 75, 95]) # Relative-to-stream elevation swath profile swath_rel_stream = np.full((len(xbins) - 1, 5), np.nan, dtype='float32') for i in range(1, len(xbins)): swath_elevations = relz[(mask == 1) & (binned == i)] if swath_elevations.size: swath_rel_stream[i - 1, :] = np.percentile( swath_elevations, [5, 25, 50, 75, 95]) # Relative-to-valley-floor elevation swath profile click.echo('Fit valley floor') swath_rel_valley = np.full((len(xbins) - 1, 5), np.nan, dtype='float32') def fit_valley_floor(fit_mask=None, error_threshold=1.0, iterations=100): if fit_mask is None: mask0 = (mask == 1) else: mask0 = (mask == 1) & fit_mask size = elevations.shape[0] * elevations.shape[1] matrix = np.stack([ measure.reshape(size), np.ones(size, dtype='float32'), elevations.reshape(size) ]).T matrix = matrix[mask0.reshape(size), :] samples = matrix.shape[0] // 10 model = LinearModel([0, 1], [2]) (slope, z0), _, _ = ransac(matrix, model, samples, iterations, error_threshold, 2 * samples) relative = elevations - (z0 + slope * measure) for i in range(1, len(xbins)): swath_elevations = relative[(mask == 1) & (binned == i)] if swath_elevations.size: swath_rel_valley[i - 1, :] = np.percentile( swath_elevations, [5, 25, 50, 75, 95]) try: fit_valley_floor() except RuntimeError: try: fit_valley_floor(fit_mask=(relz <= 10.0)) except RuntimeError: swath_rel_valley = np.array([]) values = dict(x=x, density=density, swath_abs=swath_absolute, swath_rel=swath_rel_stream, swath_vb=swath_rel_valley) return axis, gid, values
def test_rasterize_invalid_out_dtype(basic_geometry): """A non-supported data type for out should raise an exception.""" out = np.zeros(DEFAULT_SHAPE, dtype=np.int64) with pytest.raises(ValueError): rasterize([basic_geometry], out=out)
def test_rasterize_invalid_geom(): """Invalid GeoJSON should fail with exception""" with pytest.raises(ValueError): rasterize([{'type'}], out_shape=DEFAULT_SHAPE) with pytest.raises(ValueError): rasterize([{'type': 'Invalid'}], out_shape=DEFAULT_SHAPE) with pytest.raises(ValueError): rasterize([{'type': 'Point'}], out_shape=DEFAULT_SHAPE) with pytest.raises(ValueError): # Empty coordinates should fail rasterize([{ 'type': 'Point', 'coordinates': [] }], out_shape=DEFAULT_SHAPE) with pytest.raises(ValueError): # Empty GeometryCollection should fail rasterize([{ 'type': 'GeometryCollection', 'geometries': [] }], out_shape=DEFAULT_SHAPE) with pytest.raises(ValueError): # GeometryCollection with bad geometry should fail rasterize([{ 'type': 'GeometryCollection', 'geometries': [{ 'type': 'Invalid', 'coordinates': [] }] }], out_shape=DEFAULT_SHAPE)
def test_rasterize_polygon(geojson_polygon, basic_image_2x2): assert np.array_equal( rasterize([geojson_polygon], out_shape=DEFAULT_SHAPE), basic_image_2x2)
def test_rasterize_internal_driver_manager(basic_geometry): """Rasterize should work without explicitly calling driver manager.""" assert rasterize([basic_geometry], out_shape=DEFAULT_SHAPE).sum() == 4
def test_rasterize_missing_shapes(): """Shapes are required for this operation.""" with pytest.raises(ValueError) as ex: rasterize([], out_shape=DEFAULT_SHAPE) assert 'No valid geometry objects' in str(ex.value)
def test_rasterize_invalid_value(basic_geometry): """A shape value that requires an int64 should raise an exception.""" with pytest.raises(ValueError, match="Values out of range for supported dtypes"): rasterize( [(basic_geometry, 1000000000000)], out_shape=DEFAULT_SHAPE )
def test_rasterize_missing_out(basic_geometry): """If both out and out_shape are missing, should raise exception.""" with pytest.raises(ValueError): rasterize([basic_geometry], out=None, out_shape=None)
def test_rasterize_default_value_for_none(basic_geometry, basic_image_2x2): """All shapes should rasterize to the default value.""" assert np.all( rasterize([(basic_geometry, None)], out_shape=DEFAULT_SHAPE, fill=2) == 2 )
def test_rasterize_invalid_geom(input): """Invalid GeoJSON should fail with exception""" with pytest.raises(ValueError), pytest.warns(ShapeSkipWarning): rasterize(input, out_shape=DEFAULT_SHAPE)
def _mapper(x, y, z, data, args): """Iterate over OSM QA Tiles and return a label for each tile Iterate over the .mbtiles input. Decode the provided vector tile. Depending upon the desired type of eventual machine learning training, return a label list: - For 'object-detection' tasks, each list element is a bounding box like [xmin, ymin, xmax, ymax, class_index]. There is one list element for each feature matching the provided classes. - For 'classification' tasks, the entire list is a "one-hot" vector representing which class the tile matches Parameters ------------ x, y, z: int tile indices data: str Encoded vector tile data args: dict Additional arguments passed to the tile worker Returns --------- label: tuple The first element is a tile index of the form x-y-z. The second element is a list representing the label of the tile """ ml_type = args.get('ml_type') classes = args.get('classes') if data is None: return ('{!s}-{!s}-{!s}'.format(x, y, z), _create_empty_label(ml_type, classes)) tile = mapbox_vector_tile.decode(data) # for each class, determine if any features in the tile match if tile['osm']['features']: if ml_type == 'classification': class_counts = np.zeros(len(classes) + 1, dtype=np.int) for i, cl in enumerate(classes): ff = create_filter(cl.get('filter')) class_counts[i + 1] = int(bool([f for f in tile['osm']['features'] if ff(f)])) # if there are no classes, activate the background if np.sum(class_counts) == 0: class_counts[0] = 1 return ('{!s}-{!s}-{!s}'.format(x, y, z), class_counts) elif ml_type == 'object-detection': bboxes = _create_empty_label(ml_type, classes) for feat in tile['osm']['features']: for i, cl in enumerate(classes): ff = create_filter(cl.get('filter')) if ff(feat): geo = shape(feat['geometry']) if cl.get('buffer'): geo = geo.buffer(cl.get('buffer'), 4) bb = _pixel_bbox(geo.bounds) + [i + 1] bboxes = np.append(bboxes, np.array([bb]), axis=0) return ('{!s}-{!s}-{!s}'.format(x, y, z), bboxes) elif ml_type == 'segmentation': geos = [] for feat in tile['osm']['features']: for i, cl in enumerate(classes): ff = create_filter(cl.get('filter')) if ff(feat): feat['geometry']['coordinates'] = _convert_coordinates(feat['geometry']['coordinates']) geo = shape(feat['geometry']) try: geo = geo.intersection(clip_mask) except TopologicalError as e: print(e, 'skipping') break if cl.get('buffer'): geo = geo.buffer(cl.get('buffer'), 4) if not geo.is_empty: geos.append((mapping(geo), i + 1)) result = rasterize(geos, out_shape=(256, 256)) return ('{!s}-{!s}-{!s}'.format(x, y, z), result) return ('{!s}-{!s}-{!s}'.format(x, y, z), np.array())
def prepare_roads(roads_in, aoi_in, ntl_in, include_power=True): """Prepare a roads feature layer for use in algorithm. Parameters ---------- roads_in : str, Path Path to a roads feature layer. This implementation is specific to OSM data and won't assign proper weights to other data inputs. aoi_in : str, Path or GeoDataFrame AOI to clip roads. ntl_in : str, Path Path to a raster file, only used for correct shape and affine of roads raster. Returns ------- roads_raster : numpy array Roads as a raster array with the value being the cost of traversing. affine : affine.Affine Affine raster transformation for the new raster (same as ntl_in). """ ntl_rd = rasterio.open(ntl_in) shape = ntl_rd.read(1).shape affine = ntl_rd.transform if isinstance(aoi_in, gpd.GeoDataFrame): aoi = aoi_in else: aoi = gpd.read_file(aoi_in) roads = gpd.read_file(roads_in) roads = clip_line_poly(roads, aoi) roads["weight"] = 1 roads.loc[roads["highway"] == "motorway", "weight"] = 1 / 10 roads.loc[roads["highway"] == "trunk", "weight"] = 1 / 9 roads.loc[roads["highway"] == "primary", "weight"] = 1 / 8 roads.loc[roads["highway"] == "secondary", "weight"] = 1 / 7 roads.loc[roads["highway"] == "tertiary", "weight"] = 1 / 6 roads.loc[roads["highway"] == "unclassified", "weight"] = 1 / 5 roads.loc[roads["highway"] == "residential", "weight"] = 1 / 4 roads.loc[roads["highway"] == "service", "weight"] = 1 / 3 # Power lines get weight 0 roads.loc[roads["power"] == "line", "weight"] = 0 roads = roads[roads.weight != 1] # sort by weight descending so that lower weight (bigger roads) are # processed last and overwrite higher weight roads roads = roads.sort_values(by="weight", ascending=False) roads_for_raster = [(row.geometry, row.weight) for _, row in roads.iterrows()] roads_raster = rasterize( roads_for_raster, out_shape=shape, fill=1, default_value=0, all_touched=True, transform=affine, ) return roads_raster, affine
def vesper_text_to_raster(control_textfile, krig_epsg=0, nodata_value=-9999): """Convert an vesper kriged text file output to a prediction and a standard error (SE) tif raster, and create a confidence interval (CI) metadata file. If the output files already exists, they will be overwritten. Kriging using Vesper creates 3 new files: *_kriged_*.txt, *_report_*.txt, *_parameter_*.txt. There is a 100 character file path limitation set within vesper for the kriged and report files. By using the control filename as the input, the truncated files can be renamed to their correct names and is used as a base for derived tiff and metadata The *_kriged_*.txt file contains the data to create the desired outputs. Args: control_textfile (str): The control file for the associated vesper krige raster (see note above) to convert. krig_epsg (int): The EPSG number to assign to the output raster nodata_value (int): The value to assign to nodata pixels. Outputs: *_CI_*.txt The confidence interval (CI) metadata file. *_PRED_*.tif The kriged column prediction tif *_SE_*.tif The kriged column standard error (SE) tif """ if not os.path.exists(control_textfile): raise IOError("Invalid path: {}".format(control_textfile)) for argCheck in [('krig_epsg', krig_epsg), ('nodata_value', nodata_value)]: if not isinstance(argCheck[1], (int, long)): raise TypeError('{} must be a integer.'.format(argCheck[0])) start_time = time.time() # There is a 100 character file path limitation set for the kriged and report outputs from vesper. By using the # control filename we can find these truncated files and correct their names. if len(control_textfile) > 100: search_dir = os.path.dirname(control_textfile) search_file = os.path.basename(control_textfile[:101]).replace('control', '*') suffix = control_textfile[101:] for ea_file in glob.glob(os.path.join(search_dir, search_file)): os.rename(ea_file, ea_file + suffix) logging.debug('Renaming file {} to {}'.format(ea_file, ea_file + suffix)) krige_textfile = control_textfile.replace('control', 'kriged') out_CITxt = control_textfile.replace('control', 'CI') # using pd.read_table takes care of the multiple white space delimiter dfKrige = pd.read_table(krige_textfile, delim_whitespace=True) median_val = dfKrige['SE_Pred'].median() with open(out_CITxt, 'w') as ci_file: ci_file.writelines("Median Prediction SE : {:.5f}\n".format(median_val)) ci_file.writelines("95% Confidence Interval : {:.5f}\n\n".format(2 * 1.96 * median_val)) ci_file.writelines("Date/time : " + datetime.datetime.now().strftime("%d-%b-%Y %H:%M") + "\n") userhome = os.path.expanduser('~') ci_file.writelines("Username : "******"\n") LOGGER.debug("CI File contents : \n\tMedian Prediction SE : {:.5f}\n" "\t95% Confidence Interval : {:.5f}".format(median_val, 2 * 1.96 * median_val)) x_field, y_field = predictCoordinateColumnNames(dfKrige.columns.tolist()) gdfKrig, gdfCRS = add_point_geometry_to_dataframe(dfKrige, [x_field, y_field], krig_epsg) cellsize_x = float(dfKrige[x_field].sort_values().drop_duplicates().diff(1).mode()) cellsize_y = float(dfKrige[y_field].sort_values().drop_duplicates().diff(1).mode()) pixel_size = min(cellsize_x, cellsize_y) pixel_size_str = numeric_pixelsize_to_string(pixel_size) LOGGER.debug("Cellsize: {} X: {} Y: {}".format(pixel_size, cellsize_x, cellsize_y)) out_SETif = control_textfile.replace('control', 'SE_{}'.format(pixel_size_str)).replace('.txt', '.tif') out_PredTif = control_textfile.replace('control', 'PRED_{}'.format(pixel_size_str)).replace('.txt', '.tif') # create an affine transformation matrix to associate the array to the coordinates. transform, x_cols, y_rows, new_bbox = create_raster_transform(list(gdfKrig.total_bounds), pixel_size=pixel_size) LOGGER.debug('Width (xCols): {} Height (yRows): {}'.format(x_cols, y_rows)) # create the two tifs and populate with data. This method is by far the quickest of all 3 methods trialed. with rasterio.open(os.path.normpath(out_PredTif), 'w', driver='GTiff', width=x_cols, height=y_rows, count=1, crs=rasterio.crs.CRS.from_epsg(krig_epsg), dtype='float32', nodata=-9999, transform=transform) as outPred: # uses points to burn values into the corresponding raster pixel. shapes = ((geom, value) for geom, value in zip(gdfKrig['geometry'], gdfKrig['Predicted'])) burned = features.rasterize(shapes=shapes, out_shape=(y_rows,x_cols), transform=outPred.transform, fill=nodata_value) outPred.write(burned, indexes=1) with rasterio.open(os.path.normpath(out_SETif), 'w', driver='GTiff', width=x_cols, height=y_rows, count=1, crs= rasterio.crs.CRS.from_epsg(krig_epsg), dtype='float32', nodata=-9999, transform=transform) as outSE: # uses points to burn values into the corresponding raster pixel. shapes = ((geom, value) for geom, value in zip(gdfKrig['geometry'], gdfKrig['SE_Pred'])) burned = features.rasterize(shapes=shapes, out_shape=(y_rows,x_cols), transform=outSE.transform, fill=nodata_value) outSE.write(burned, indexes=1) LOGGER.info('{:<30}\t{dur:<15}\t{}'.format(inspect.currentframe().f_code.co_name, '', dur=datetime.timedelta(seconds=time.time() - start_time))) return out_PredTif, out_SETif, out_CITxt
def create_mask(self, rst=None, crs=None, xres=None, yres=None, bounds=None): """ Rasterize the vector features into a boolean raster which has the extent/dimensions of \ the provided raster file. Alternatively, user can specify a grid to rasterize on using xres, yres, bounds and crs. Only xres is mandatory, by default yres=xres and bounds/crs are set to self's. Vector features which fall outside the bounds of the raster file are not written to the new mask file. :param rst: A Raster object or string to filename :type rst: Raster object or str :param crs: A pyproj or rasterio CRS object (Default to rst.crs if not None then self.crs) :type crs: pyproj.crs.crs.CRS, rasterio.crs.CRS :param xres: Output raster spatial resolution in x. Only is rst is None. :type xres: float :param yres: Output raster spatial resolution in y. Only if rst is None. (Default to xres) :type yres: float :param bounds: Output raster bounds (left, bottom, right, top). Only if rst is None (Default to self bounds) :type bounds: tuple :returns: array containing the mask :rtype: numpy.array """ # If input rst is string, open as Raster if isinstance(rst, str): from geoutils.georaster import Raster rst = Raster(rst) # If no rst given, use provided dimensions if rst is None: # At minimum, xres must be set if xres is None: raise ValueError('at least rst or xres must be set') if yres is None: yres = xres # By default, use self's CRS and bounds if crs is None: crs = self.ds.crs if bounds is None: bounds = self.ds.total_bounds # Calculate raster shape left, bottom, right, top = bounds height = abs((right - left) / xres) width = abs((top - bottom) / yres) if width % 1 != 0 or height % 1 != 0: warnings.warn( "Bounds not a multiple of xres/yres, use rounded bounds") width = int(np.round(width)) height = int(np.round(height)) out_shape = (height, width) # Calculate raster transform transform = rio.transform.from_bounds(left, bottom, right, top, width, height) # otherwise use directly rst's dimensions else: out_shape = rst.shape transform = rst.transform crs = rst.crs # Reproject vector into rst CRS # Note: would need to check if CRS are different vect = self.ds.to_crs(crs) # Rasterize geomtry mask = features.rasterize(shapes=vect.geometry, fill=0, out_shape=out_shape, transform=transform, default_value=1, dtype='uint8').astype('bool') # Force output mask to be of same dimension as input rst if rst is not None: mask = mask.reshape((rst.count, rst.height, rst.width)) return mask
def process_building(geom, crs, proj, btype, bid): # geom, crs, proj = args ct = geom.centroid lon, lat = pyproj.transform(proj, proj_84, ct.x, ct.y) # open building data files try: hor5kpath = os.path.join(base_path, r"5khorizons") horpath = os.path.join(base_path, r"horizons") horfname = "{}_{}.tar.gz".format(btype, bid) building_path = os.path.join(horpath, horfname) if not os.path.exists(building_path): logging.info("{} no datafiles found for {}".format( str(datetime.datetime.now()), building_path)) return zipdirpath = tempfile.mkdtemp(prefix="/tmp/") shutil.unpack_archive(building_path, extract_dir=zipdirpath, format='gztar') # Load slope/aspect if not existing if not os.path.exists(os.path.join(zipdirpath, 'slope')): horfnamesa = "{}_{}sa.tar.gz".format(btype, bid) building_path2 = os.path.join(horpath, horfnamesa) shutil.unpack_archive(building_path2, extract_dir=zipdirpath, format='gztar') def read_data(ds): with rasterio.open(os.path.join(zipdirpath, ds)) as src: return src.read()[0], src.transform, not src.meta['transform'][ 0] == 0.5, src.meta slopes, fwd, bad, meta = read_data('slope') if bad: logging.info("BAD SLOPE") return aspects, fwd_a, bad, meta = read_data('aspect') if bad: logging.info("BAD ASPECT") return slopes[np.where(np.isnan(slopes))] = 99999 aspects[np.where(np.isnan(aspects))] = 99999 horizons = {} for az in range(-120, 121, 5): horizons[float(az)] = read_data("{}_{}_{}".format(btype, bid, az))[0] horizons2 = defaultdict(lambda: 0.0) hor5kfname = "{}_{}_hor.txt".format(btype, bid) hor2_path = os.path.join(hor5kpath, hor5kfname) with open(hor2_path, mode='r') as ff: for line in ff: vals = list(map(float, line.strip().split(';'))) horizons2[vals[0]] = vals[1] # pprint.pprint(horizons2) # logging.info("{} read {}_{}s".format(str(datetime.datetime.now()), # btype, # bid)) # PROCESS h, w = slopes.shape geom2 = geom.buffer(6.0) buildingmap = rasterize([(geom2, 1)], out_shape=(h, w), fill=0, all_touched=True, transform=fwd, dtype=rasterio.uint8) # get 30min of year (we could optimize this) t = 0 d = datetime.datetime(2017, 1, 1, 0, 0) while d.year == 2017: t += 1 d += datetime.timedelta(minutes=30) # Create output file fpath = os.path.join(outpath, "rad30min_{}_{}.tif".format(btype, bid)) with rasterio.open(fpath, mode='w', driver="GTiff", width=w, height=h, count=t, transform=fwd, crs=crs, nodata=NODATA, dtype='float32') as dst: # Iterate over all radiation data, each file contains the 30 minutes values of one day d = datetime.date(2017, 1, 1) b = 0 while d.year < 2018: # logging.info("{}_{}: {}".format(btype, bid, str(d))) year = d.year month = d.month day = d.day fname = "SISin{year}{month:02d}{day:02d}0000003231000101UD.nc".format( year=year, month=month, day=day) src_sis = rasterio.open(os.path.join(cmsaf_path, "sis", fname)) fwd_sis = src_sis.transform c_sis, r_sis = (lon, lat) * ~fwd_sis data_sis = src_sis.read( window=Window(int(c_sis), int(r_sis), 1, 1)) fname = "SIDin{year}{month:02d}{day:02d}0000003231000101UD.nc".format( year=year, month=month, day=day) src_sid = rasterio.open(os.path.join(cmsaf_path, "sid", fname)) fwd_sid = src_sid.transform c_sid, r_sid = (lon, lat) * ~fwd_sid data_sid = src_sid.read( window=Window(int(c_sid), int(r_sid), 1, 1)) # Each nc file contains the 30 minute periods of a day as band # We iterate over each band dd = datetime.datetime(year, month, day) end_dd = datetime.datetime(year, month, day) + datetime.timedelta(days=1) i = 0 while dd < end_dd: rad = np.zeros((h, w)) # Solar irradiation config for spa.c algorithm s = csolar2.solar.Solar(lat, lon, dd, 0, elevation=600, temperature=11, pressure=1020) # global rad hor sis = float(max(data_sis[i, 0, 0], 0)) # direct rad hor sid = float(max(data_sid[i, 0, 0], 0)) # Iterate over all cells for _h in range(h): for _w in range(w): _rad = 0.0 if slopes[_h, _w] < 99999 and aspects[_h, _w] < 99999: # slope beta_deg = slopes[_h, _w] # azimuth alpha_deg = transform_angle(aspects[_h, _w]) # transform alpha # direct rad dif dif = sis - sid assert dif >= 0.0 dir_tilted, dif_tilted, sis_tilted = csolar2.solar.get_tilted_rad( s, sis, sid, 0.2, beta_deg, alpha_deg) sun_elevation = s.get_elevation_angle() sun_azimuth = round( s.get_azimuth() / 5.0) * 5.0 # We do nothing if we have no irradiation if sis_tilted >= 0.0 and sun_elevation >= 0.0 and sun_azimuth >= -120 and sun_azimuth <= 120.0: h1 = horizons[sun_azimuth][_h, _w] h2 = horizons2[sun_azimuth] max_hor = max(h1, h2) if max_hor < sun_elevation: _rad = dir_tilted + dif_tilted else: _rad = dif_tilted rad[_h, _w] = _rad dst.write(rad.astype(np.float32), b + 1) dd += datetime.timedelta(minutes=30) i += 1 b += 1 src_sis.close() src_sid.close() d += datetime.timedelta(days=1) except Exception as e: logging.error("{}: ERROR: {} \n {}".format( str(datetime.datetime.now()), str(e), traceback.format_exc()))
def rasterize(self, rst=None, crs=None, xres=None, yres=None, bounds=None, in_value=None, out_value=0): """ Return an array with input geometries burned in. By default, output raster has the extent/dimensions of the provided raster file. Alternatively, user can specify a grid to rasterize on using xres, yres, bounds and crs. Only xres is mandatory, by default yres=xres and bounds/crs are set to self's. Burn value is set by user and can be either a single number, or an iterable of same length as self.ds. Default is an index from 1 to len(self.ds). :param rst: A raster to be used as reference for the output grid :type rst: Raster object or str :param crs: A pyproj or rasterio CRS object (Default to rst.crs if not None then self.crs) :type crs: pyproj.crs.crs.CRS, rasterio.crs.CRS :param xres: Output raster spatial resolution in x. Only is rst is None. :type xres: float :param yres: Output raster spatial resolution in y. Only if rst is None. (Default to xres) :type yres: float :param bounds: Output raster bounds (left, bottom, right, top). Only if rst is None (Default to self bounds) :type bounds: tuple :param in_value: Value(s) to be burned inside the polygons (Default is self.ds.index + 1) :type in_value: int, float, iterable :param out_value: Value to be burned outside the polygons (Default is 0) :type out_value: int, float :returns: array containing the burned geometries :rtype: numpy.array """ # If input rst is string, open as Raster if isinstance(rst, str): from geoutils.georaster import Raster rst = Raster(rst) # If no rst given, use provided dimensions if rst is None: # At minimum, xres must be set if xres is None: raise ValueError('at least rst or xres must be set') if yres is None: yres = xres # By default, use self's CRS and bounds if crs is None: crs = self.ds.crs if bounds is None: bounds = self.ds.total_bounds # Calculate raster shape left, bottom, right, top = bounds height = abs((right - left) / xres) width = abs((top - bottom) / yres) if width % 1 != 0 or height % 1 != 0: warnings.warn( "Bounds not a multiple of xres/yres, use rounded bounds") width = int(np.round(width)) height = int(np.round(height)) out_shape = (height, width) # Calculate raster transform transform = rio.transform.from_bounds(left, bottom, right, top, width, height) # otherwise use directly rst's dimensions else: out_shape = rst.shape transform = rst.transform crs = rst.crs # Reproject vector into rst CRS # Note: would need to check if CRS are different vect = self.ds.to_crs(crs) # Set default burn value, index from 1 to len(self.ds) if in_value is None: in_value = self.ds.index + 1 # Rasterize geometry if isinstance(in_value, collections.abc.Iterable): if len(in_value) != len(vect.geometry): raise ValueError( "in_value must have same length as self.ds.geometry, currently {} != {}" .format(len(in_value), len(vect.geometry))) out_geom = ((geom, value) for geom, value in zip(vect.geometry, in_value)) mask = features.rasterize(shapes=out_geom, fill=out_value, out_shape=out_shape, transform=transform) elif isinstance(in_value, Number): mask = features.rasterize(shapes=vect.geometry, fill=out_value, out_shape=out_shape, transform=transform, default_value=in_value) else: raise ValueError( "in_value must be a single number or an iterable with same length as self.ds.geometry" ) return mask
def test_rasterize_invalid_shapes(): """Invalid shapes should raise an exception rather than be skipped.""" with pytest.raises(ValueError) as ex, pytest.warns(ShapeSkipWarning): rasterize([{'foo': 'bar'}], out_shape=DEFAULT_SHAPE) assert 'No valid geometry objects found for rasterize' in str(ex.value)
def rasterize_polygon(polygon:Polygon, class_id:int, out:np.ndarray, all_touched=True): rasterize([(polygon, class_id)], out=out, all_touched=all_touched, default_value=0)
def main(in_file, out_file, boundary): if os.path.exists(out_file): confirm = input( "The output file '{}' already exists. Do you wish to replace it? [y/n] " .format(out_file)) if confirm.lower().strip() not in ['y', 'yes']: sys.exit() print('Loading DEM...') with rasterio.open(in_file) as ds: grid = ds.read()[0] bounds = ds.bounds affine = ds.profile['affine'] width = ds.profile['width'] if boundary: mask = (grid != ds.profile['nodata']).astype('uint8') with fiona.open(boundary, 'r') as shp: grid_projection = Proj('+init=EPSG:4326') shp_projection = Proj(shp.crs) ll = transform(grid_projection, shp_projection, bounds.left, bounds.bottom) ur = transform(grid_projection, shp_projection, bounds.right, bounds.top) features = list(shp.items(bbox=(*ll, *ur))) num_features = len(features) for i, feature in enumerate(features): geometry = transform_geom(shp.crs, {'init': 'EPSG:4326'}, feature[1]['geometry']) mask |= rasterize(((geometry, 1), ), out_shape=mask.shape, transform=affine, fill=0, dtype=numpy.dtype('uint8'), default_value=1) print('Masking DEM... ({}%)'.format( round(i / float(num_features) * 100)), end='\r') print('') grid = numpy.ma.masked_where(mask == 0, grid) print('Writing CSV...') with open(out_file, 'w') as f_out: csv_file = csv.writer(f_out) csv_file.writerow(['ID1', 'ID2', 'Lat', 'Lon', 'El']) for i, value in enumerate(grid.ravel()): if is_masked(value): continue col = i % width row = i // width x, y = affine * (col, row) # Todo: adjust to center of cell? csv_file.writerow([row, col, round(y, 7), round(x, 7), value]) print('Done.')
def FindPolygonOutlet(basin, zone, root, overwrite): """ DOCME """ output = os.path.join(basin, zone, 'ZONEHYDRO_OUTLETS.shp') # output2 = os.path.join(basin, zone, 'ZONEHYDRO_OUTLET.shp') flow_raster = os.path.join(basin, zone, 'FLOW.tif') zone_shapefile = os.path.join(basin, zone, 'ZONEHYDRO_BDC.shp') min_lca = 1e6 / 25 if os.path.exists(output) and not overwrite: # if os.path.exists(output1): # click.secho('Output already exists : %s' % output1, fg='yellow') # if os.path.exists(output2): # click.secho('Output already exists : %s' % output2, fg='yellow') return 0 with rio.open(flow_raster) as ds: flow = ds.read(1) mask = np.zeros_like(flow, dtype=np.uint8) def shapes(): with fiona.open(zone_shapefile) as fs: for feature in fs: yield feature['geometry'], 1 rasterize(shapes(), fill=0, transform=ds.transform, out=mask) outlets = list() with fiona.open(zone_shapefile) as fs: for f in fs: driver = fs.driver crs = fs.crs geometry = np.float32(f['geometry']['coordinates'][0]) outlets.extend(ta.polygon_outlets(geometry, flow, mask, riotogdal(ds.transform))) def isdata(i, j): return i >= 0 and i < ds.height and j >=0 and j < ds.width def path(i, j, flow): ix, jx = i, j while isdata(ix, jx) and flow[ix, jx] != 0 and flow[ix, jx] != -1: yield ix, jx direction = int(np.log2(flow[ix, jx])) ix = ix + ci[direction] jx = jx + cj[direction] ci = [-1, -1, 0, 1, 1, 1, 0, -1] cj = [ 0, 1, 1, 1, 0, -1, -1, -1] def common_outlet(a, b, flow): ai, aj = a path_a = set(path(ai, aj, flow)) bi, bj = b for i, j in path(bi, bj, flow): if (i, j) in path_a: return i, j return None outlet = None score = 0 zone_area = np.sum(mask) count = 0 schema = {'geometry': 'Point', 'properties': [('CDZONEHYDRO', 'str:4'), ('DRAINAGE', 'int'), ('SCORE', 'float')]} options=dict(driver='ESRI Shapefile', schema=schema, crs=crs) queue = [(-lca, (i, j)) for (i, j), lca in outlets] outlets = list() cum_area = 0 heapify(queue) while queue: lca, (i, j) = heappop(queue) lca = -lca if outlets and (lca < min_lca or cum_area > 0.95*zone_area): break # if lca / zone_area >= 0.005: # if outlet is None: # outlet = (i, j) # score = lca # else: # o = common_outlet(outlet, (i, j), flow) # if o is not None: # outlet = o # score += lca outlets.append(((i, j), lca)) cum_area += lca # count += 1 # if count > 100: # break with fiona.open(output, 'w', **options) as dst: for (i, j), lca in outlets: geom = { 'type': 'Point', 'coordinates': ds.xy(i, j) } props = { 'CDZONEHYDRO': zone, 'DRAINAGE': lca, 'SCORE': lca/zone_area*100 } dst.write({'geometry': geom, 'properties': props}) # if outlet is not None: # with fiona.open(output2, 'w', **options) as dst: # geom = { # 'type': 'Point', # 'coordinates': ds.xy(*outlet) # } # props = { # 'CDZONEHYDRO': zone, # 'DRAINAGE': score, # 'SCORE': score/zone_area*100 # } # dst.write({'geometry': geom, 'properties': props}) # else: # click.secho('No outlet found for zone %s' % zone, fg='red') return len(outlets)
def test_rasterize_point(geojson_point): expected = np.zeros(shape=DEFAULT_SHAPE, dtype='uint8') expected[2, 2] = 1 assert np.array_equal(rasterize([geojson_point], out_shape=DEFAULT_SHAPE), expected)
def test_rasterize_out_image(basic_geometry, basic_image_2x2): """Rasterize operation should succeed for an out image.""" out = np.zeros(DEFAULT_SHAPE) rasterize([basic_geometry], out=out) assert np.array_equal(basic_image_2x2, out)
(geom, int(value)) for geom, value in zip( truth_patches['geometry'], truth_patches['rstr_cd']) #.map(labels)) ] #rasterize using the shape and transform of the satellite image out_shape = src.shape transform = src.transform #out_meta = src.meta # 'shapes' in rasterio.rasterize is iterable of (geometry, value) pairs # or iterable over array_to_rast = features.rasterize( shapes=shapes, out_shape=out_shape, transform=transform, fill=-1, all_touched=False, #default_value=1, dtype='float32', ) out_meta = { "driver": "GTiff", "dtype": 'float32', "nodata": None, "height": src.height, "width": src.width, "transform": transform, "count": 1, # Specify to any crs by defining here. if you don't do so, # it sets up to wgs84 geographic by default (weird tho)