def main(heat_density_map, region, pix_threshold, DH_threshold, output_dir, in_orig=None, only_return_areas=False): DH_Regions, clipped_hdm, rasterOrigin = DHP.DHReg(heat_density_map, region, pix_threshold, DH_threshold, in_orig) if only_return_areas: clipped_hdm = rasterOrigin = None return DH_Regions """ Outputs: DH_Regions: contains binary values (no units) showing coherent areas clipped_hdm: hdm after applying pixel threshold [MWh/ha] rasterOrigin: Coordination of the top-left corner of raster """ outRasterPath1 = output_dir + os.sep + 'F13_' + 'Pot_areas.tif' outRasterPath2 = output_dir + os.sep + 'F13_' + 'labels.tif' outShapefilePath = output_dir + os.sep + 'F13_' + 'Pot_AT_TH30.shp' DHPot, labels = DHP.DHPotential(DH_Regions, clipped_hdm) """potential of each coherent area in GWh is assigned to its pixels""" geo_transform = [rasterOrigin[0], 100, 0, rasterOrigin[1], 0, -100] CM19.main(outRasterPath1, geo_transform, 'float64', DH_Regions) CM19.main(outRasterPath2, geo_transform, 'int32', labels) polygonize(outRasterPath1, outRasterPath2, outShapefilePath, DHPot) return outRasterPath1, outShapefilePath
def scaling(hdm_cut, hdm_cut_gt, updated_demand, outRasterPath): ''' This module gets a demand value and uses the default distribution of heat density map for distributing this value between pixels. The module returns a numpy array. hdm_cut: numpy array showing demand in MWh/ha hdm_cut_gt: raster geo-transform updated_demand: in GWh/a outRasterPath: path for saving the updated hdm ''' # Sum over pixel values in MWh/ha and return in GWh sum_demand_cut = np.sum(hdm_cut) * 0.001 new_HDM_cut = updated_demand / sum_demand_cut * hdm_cut rm_file(outRasterPath) CM19.main(outRasterPath, hdm_cut_gt, "float32", new_HDM_cut) return outRasterPath
def distribuition_costs(pixT, DH_threshold, dist_grid_cost, out_raster_hdm_last_year, out_raster_maxDHdem, out_raster_invest_Euro, out_raster_coh_area_bool, out_raster_labels, struct=np.ones((3, 3))): rm_file(out_raster_coh_area_bool, out_raster_labels) invest_Euro_arr = raster_array(out_raster_invest_Euro) maxDHdem_arr = raster_array(out_raster_maxDHdem) heat_density_map_last_year, geo_transform = raster_array( out_raster_hdm_last_year, return_gt=True) rast_origin = geo_transform[0], geo_transform[3] coh_areas = np.zeros_like(maxDHdem_arr, 'int8') reg_filter = maxDHdem_arr.astype(bool).astype('int8') # DH_threshold in MWH DH_threshold_MWh = DH_threshold * 1000 for pix_threshold in pixT: # calculate coherent regions with given thresholds and cut them to # LAU2 levels # DH_Regions: boolean array showing DH regions DH_Regions, gt = DHP.DHReg(heat_density_map_last_year, pix_threshold, DH_threshold, rast_origin) # multiplication with reg_filter required to follow out_raster_maxDHdem # pattern and separate connection of regions with pixels that have # value of zero in out_raster_maxDHdem result = DH_Regions.astype(int) * reg_filter labels, nr_coherent = measurements.label(result, structure=struct) if nr_coherent == 0: break for i in range(1, nr_coherent + 1): temp = labels == i q = np.sum(maxDHdem_arr[temp]) q_inv = np.sum(invest_Euro_arr[temp]) q_spec_cost = q_inv / q if q_spec_cost <= dist_grid_cost and q >= DH_threshold_MWh: coh_areas[temp] = 1 heat_density_map_last_year[temp] = 0 labels = None nr_coherent = None labels, numLabels = measurements.label(coh_areas, structure=struct) CM19.main(out_raster_coh_area_bool, geo_transform, 'int8', coh_areas) CM19.main(out_raster_labels, geo_transform, "int16", labels) return numLabels
def main(heat_density_map, pix_threshold, DH_threshold, output_raster1, output_raster2, output_shp1, output_shp2, in_orig=None, only_return_areas=False): # DH_Regions: boolean array showing DH regions DH_Regions, hdm_dh_region_cut, geo_transform, total_heat_demand = DHP.DHReg(heat_density_map, pix_threshold, DH_threshold, in_orig) if only_return_areas: geo_transform = None return DH_Regions DHPot, labels = DHP.DHPotential(DH_Regions, heat_density_map) total_potential = np.around(np.sum(DHPot),2) total_heat_demand = np.around(total_heat_demand, 2) if total_potential == 0: dh_area_flag = False else: dh_area_flag = True graphics = [ { "type": "bar", "xLabel": "DH Area Label", "yLabel": "Potential (GWh/year)", "data": { "labels": [str(x) for x in range(1, 1+len(DHPot))], "datasets": [{ "label": "Potential in coherent areas", "backgroundColor": ["#3e95cd"]*len(DHPot), "data": list(np.around(DHPot,2)) }] } },{ "type": "bar", "xLabel": "", "yLabel": "Demand / Potential (GWh/year)", "data": { "labels": ["Annual heat demand", "DH potential"], "datasets": [{ "label": "Heat Demand Vs. DH Potential (GWh/year)", "backgroundColor": ["#fe7c60", "#3e95cd"], "data": [total_heat_demand, total_potential] }] } }] symbol_vals_str = [] if dh_area_flag: CM19.main(output_raster1, geo_transform, 'int8', DH_Regions) temp_raster = os.path.dirname(output_raster2) + '/temp.tif' CM19.main(temp_raster, geo_transform, 'int32', labels) symbol_vals_str = polygonize(output_raster1, temp_raster, output_shp1, output_shp2, DHPot) rm_file(temp_raster, temp_raster[:-4] + '.tfw') CM19.main(output_raster2, geo_transform, 'float32', hdm_dh_region_cut) return total_potential, total_heat_demand, graphics, symbol_vals_str
def zonStat_selectedArea(inputCSV, hdm_outRasterPath, gfa_outRasterPath, population=0, resolution=100): ''' This function calculates the sum of demand within a pixels with given resolution. The pixel will also overlay to the standard fishnet used for the hotmaps toolbox since the multiplying factor matches to distances from the origin of the standard fishnet. The code assumes a resolution of 100x100 m for the output. annual building demand must be in kWh/a output heat density map raster is in MWh/ha ''' if isinstance(inputCSV, pd.DataFrame): ifile = inputCSV else: if not os.path.isfile(inputCSV): raise InputError('The input csv file does not exist!') ifile = pd.read_csv(inputCSV) demand = ifile['demand'].values GFA = ifile['GFA'].values if np.sum(GFA): GFA_valid = True else: GFA_valid = False X = ifile['X_3035'].values Y = ifile['Y_3035'].values x0 = resolution * np.floor(np.min(X)/resolution).astype(int) y0 = resolution * np.ceil(np.max(Y)/resolution).astype(int) rasterOrigin = (x0, y0) xIndex = np.floor((X-x0)/resolution).astype(int) yIndex = np.floor((y0-Y)/resolution).astype(int) xWidth = np.max(xIndex) - np.min(xIndex) + 1 yWidth = np.max(yIndex) - np.min(yIndex) + 1 index = xIndex + xWidth * yIndex # The number of rows of "index" and "demand" must be equal. sortedData = np.asarray(sorted(zip(index, demand), key=lambda x: x[0])) sortedData_GFA = np.asarray(sorted(zip(index, GFA), key=lambda x: x[0])) unique, counts = np.unique(index, return_counts=True) end = np.cumsum(counts) st = np.concatenate((np.zeros((1)), end[0:end.size-1])) # xIndex and yIndex start from 0. So they should be added by 1 sumDem = np.zeros((np.max(xIndex)+1)*(np.max(yIndex)+1)) item_location = 0 if GFA_valid: sumGFA = np.zeros_like(sumDem) for item in unique: # sum of demand for each index startIndex = int(st[item_location]) endIndex = int(end[item_location]) sumDem[item] = np.sum(sortedData[startIndex:endIndex, 1]) sumGFA[item] = np.sum(sortedData_GFA[startIndex:endIndex, 1]) item_location += 1 else: for item in unique: # sum of demand for each index startIndex = int(st[item_location]) endIndex = int(end[item_location]) sumDem[item] = np.sum(sortedData[startIndex:endIndex, 1]) item_location += 1 ''' xWidth and yWidth in the following refer to columns and rows, respectively and should not wrongly be considered as coordination! ''' # kWh/ha = 10^(-3) * MWh/ha sumDem = 0.001 * sumDem.reshape((yWidth, xWidth)) geo_transform = [rasterOrigin[0], resolution, 0 , rasterOrigin[1], 0, -resolution] CM19.main(hdm_outRasterPath, geo_transform, str(sumDem.dtype), sumDem) abs_heat_demand = np.sum(demand) if GFA_valid: # gross floor area density map sumGFA = sumGFA.reshape((yWidth, xWidth)) CM19.main(gfa_outRasterPath, geo_transform, str(sumGFA.dtype), sumGFA) mean_spec_demand = abs_heat_demand/np.sum(GFA) else: mean_spec_demand = np.nan if population: mean_dem_perCapita = abs_heat_demand/float(population) else: mean_dem_perCapita = np.nan # print("Absolute heat demand: %0.1f GWh\a" # "Mean heat demand per capita: %0.2f kWh\n" # "Mean heat demand per heated surface (ave. specific demand): %0.2f" # " kWh/m2" # % (abs_heat_demand*10**(-6), mean_dem_perCapita, mean_spec_demand)) return (abs_heat_demand*10**(-6), mean_dem_perCapita, mean_spec_demand)
def dh_demand(c1, c2, raster_plotratio, raster_hdm, start_year, last_year, accumulated_energy_saving, dh_connection_rate_1st_year, dh_connection_rate_last_year, depr_period, output_dir, interest, output_layers, dA_slope=0.0486, dA_intercept=0.0007, dataType='float32'): ''' Important Note: 1) Here, for the calculation of plot ratio, I used gross floor area raster in one hectar resolution (unit: m2). It should be divided by 1e4 to get the plot ratio. 2) if you have plot ratio raster, remove the 1e4 factor. 3) Distribution cost is calculated for those pixel that their corresponding pipe diameter is equal or greater than 0. the input heat density map should be in GWh/km2. ''' horizon = last_year - start_year + 1 if horizon > depr_period: raise Warning('Study horizon is longer than depr_period of district. ' 'The calculation will be done only till the end of ' 'depr_period!') horizon = depr_period remaining_years = 0 else: remaining_years = depr_period - horizon energy_reduction_factor = (1 - accumulated_energy_saving)**(1 / (horizon - 1)) hdm_ds = gdal.Open(raster_hdm) hdm_band = hdm_ds.GetRasterBand(1) hdm = hdm_band.ReadAsArray().astype(float) geo_transform = hdm_ds.GetGeoTransform() plotRatio_ds = gdal.Open(raster_plotratio) plotRatio_band = plotRatio_ds.GetRasterBand(1) # gfa in hectar should be devided by 10000 to get right values for # plot ratio (m2/m2). plotRatio = plotRatio_band.ReadAsArray().astype(float) / 10000.0 hdm_ds = plotRatio_ds = None row, col = np.nonzero( (hdm > 0).astype(int) * (plotRatio > 0.0).astype(int)) # unit conversion from MWh/ha to GJ/m2 sparseDemand = 0.00036 * hdm[row, col] PR_sparse = plotRatio[row, col] # the unit for L is m/m2 # L is in m/m2: to get the value for each pixel (ha) you should multiply it # by 10000 because 1 pixel has 10000 m2 L = 1 / (61.8 * PR_sparse**(-0.15)) # initialize the variables q = 0 # q_new = dh_connection_rate_1st_year * sparseDemand q_tot = np.copy(sparseDemand) q_max = np.zeros_like(q_tot) for i in range(horizon): q_tot = sparseDemand * energy_reduction_factor**i q_new = q_tot * ( dh_connection_rate_1st_year + i * (dh_connection_rate_last_year - dh_connection_rate_1st_year) / (horizon - 1)) # q_new is a non-zero sparse matrix. The development of demand can be # checked by comparing just one element of q_new with q_max. if q_new[0] > q_max[0]: q_max = np.copy(q_new) q += q_new / (1 + interest)**i if remaining_years > 0: alpha_horizon = annuity(interest, horizon - 1) alpha_depr = annuity(interest, depr_period) rest_annuity_factor = alpha_depr - alpha_horizon q = q + q_new * rest_annuity_factor linearHeatDensity = q_max / L # this step is performed to avoid negative effective pipe diameter LHD_THRESHOLD = -dA_intercept / dA_slope filtered_LHD = (np.log(linearHeatDensity) < LHD_THRESHOLD).astype(int) elements = np.nonzero(filtered_LHD)[0] dA = dA_slope * (np.log(linearHeatDensity)) + dA_slope cf1, cf2 = cost_factors(c1, c2, PR_sparse) dA[elements] = 0 q_max[elements] = 0 denominator = q / L divisor = cf1[1] + cf2[1] * dA divisor[elements] = 0 investment = divisor / denominator finalInvestment = np.zeros_like(hdm, dtype=dataType) # from Euro/GJ/m2 to Euro/MWh/m2 finalInvestment[row, col] = investment * 3.6 maxDHdem_arr = np.zeros_like(finalInvestment, dtype=dataType) # max DH demand density in MWh within the study horizon maxDHdem_arr[row, col] = q_max * 10000 / 3.6 invest_Euro_arr = maxDHdem_arr * finalInvestment hdm_last_year = np.zeros_like(finalInvestment, dtype=dataType) # total demand in the last year of study horizon in MWh/ha hdm_last_year[row, col] = q_tot * 10000 / 3.6 # total demand in last year in GWh for pixels with Plot Ration > 0 last_year_dem = np.sum(hdm_last_year) / 1000 # Length of pipes (L) length = np.zeros_like(finalInvestment, dtype=dataType) length[row, col] = L length[row, col][elements] = 0 maxDHdem, invest_Euro, hdm_cut_last_year, total_pipe_length = output_layers rm_file(maxDHdem, invest_Euro, hdm_cut_last_year, total_pipe_length) CM19.main(maxDHdem, geo_transform, dataType, maxDHdem_arr) CM19.main(invest_Euro, geo_transform, dataType, invest_Euro_arr) CM19.main(hdm_cut_last_year, geo_transform, dataType, hdm_last_year) CM19.main(total_pipe_length, geo_transform, dataType, length) # sum(MWh/ha) and convert to GWh first_year_dem_all = np.sum(hdm) / 1000 # demand in GWh for pixels with Plot Ratio > 0 first_year_dem = np.sum(sparseDemand) * 10000 / 3600 return first_year_dem_all, first_year_dem, last_year_dem
def distribuition_costs(invest_Euro, maxDHdem, features_path, hdm_1st, hdm_last, MS_1st, pixT, DHT, costT, coh_area_raster, hdm_dh_reg_last_year, label_raster, struct=np.ones((3, 3))): rm_file(coh_area_raster, hdm_dh_reg_last_year, label_raster) invest_Euro_arr = raster_array(invest_Euro) maxDHdem_arr = raster_array(maxDHdem) hdm_arr, geo_transform = raster_array(hdm_last, return_gt=True) rast_origin = geo_transform[0], geo_transform[3] coh_areas = np.zeros_like(maxDHdem_arr, 'int8') reg_filter = maxDHdem_arr.astype(bool).astype('int8') for pix_threshold in pixT: # calculate coherent regions with given thresholds and cut them to # LAU2 levels DH_Regions = CM4.main(hdm_arr, features_path, pix_threshold, DHT, None, rast_origin, only_return_areas=True) # multiplication with reg_filter required to follow maxDHdem # pattern and separate connection of regions with pixels that have # value of zero in maxDHdem result = DH_Regions.astype(int) * reg_filter labels, nr_coherent = measurements.label(result, structure=struct) if nr_coherent == 0: break for i in range(1, nr_coherent + 1): temp = labels == i q = np.sum(maxDHdem_arr[temp]) q_inv = np.sum(invest_Euro_arr[temp]) q_spec_cost = q_inv / q if q_spec_cost <= costT and q >= DHT: coh_areas[temp] = 1 hdm_arr[temp] = 0 labels = None nr_coherent = None hdm_last_arr = raster_array(hdm_last) hdm_1st_arr = raster_array(hdm_1st) labels, numLabels = measurements.label(coh_areas, structure=struct) if numLabels == 0: raise ValueError('For the provided grid cost ceiling, no district ' 'heating potential area can be realized!') if numLabels > 100: raise ValueError('For the given scenario, we found more than 100 ' 'coherent areas. Please reduce the size of your ' 'selection and run the scenario again!') hdm_in_dh_reg = hdm_last_arr * coh_areas CM19.main(coh_area_raster, geo_transform, 'int8', coh_areas) CM19.main(hdm_dh_reg_last_year, geo_transform, "float64", hdm_in_dh_reg) CM19.main(label_raster, geo_transform, "int16", labels) # average demand in dh regions: sum_demand/sum_area_of_dhReg # MWh/ha ave_dem_dh_reg = np.sum(hdm_in_dh_reg) / np.sum(coh_areas)
def add_label_field(heat_dem_coh_last, heat_dem_spec_area, q, q_spec_cost, economic_bool, area_coh_area, out_raster_coh_area_bool, out_raster_labels, out_raster_maxDHdem, out_raster_economic_maxDHdem, out_shp_prelabel, out_shp_label, epsg=3035): color_map = ["#de2d26", "#2ca25f"] label_list = [] outDriver = ogr.GetDriverByName("ESRI Shapefile") # Remove output shapefile if it already exists if os.path.exists(out_shp_label): outDriver.DeleteDataSource(out_shp_label) bool_arr, gt = RA(out_raster_coh_area_bool, return_gt=True) maxDHdem_arr = RA(out_raster_maxDHdem) economic_maxDHdem_arr = maxDHdem_arr * bool_arr.astype(int) CM19.main(out_raster_economic_maxDHdem, gt, "float64", economic_maxDHdem_arr) label_arr = RA(out_raster_labels) numLabels = np.max(label_arr) coords = measurements.center_of_mass(bool_arr, label_arr, index=np.arange(1, numLabels+1)) x0, y0, w, h = gt[0], gt[3], gt[1], gt[5] X0 = x0 + w/2 Y0 = y0 + h/2 for item in coords: xl = round(X0 + 100 * item[1], 1) yl = round(Y0 - 100 * item[0], 1) label_list.append((xl, yl)) inDriver = ogr.GetDriverByName("ESRI Shapefile") inDataSource = inDriver.Open(out_shp_prelabel, 0) inLayer = inDataSource.GetLayer() srs = osr.SpatialReference() srs.ImportFromEPSG(epsg) geom_typ = inLayer.GetGeomType() geom_typ_dict = {1: ogr.wkbPoint, 2: ogr.wkbLineString, 3: ogr.wkbPolygon} # Create the output Layer outDriver = ogr.GetDriverByName("ESRI Shapefile") outDataSource = outDriver.CreateDataSource(out_shp_label) outLayer = outDataSource.CreateLayer("newSHP", srs, geom_type=geom_typ_dict[geom_typ]) Fields = ['Label', 'Economic', 'Dem_last', 'Spec_Dem', 'Potent_DH', 'Distr_Cost', 'Area', 'color', 'fillColor', 'opacity'] Fields_dtype = [ogr.OFTInteger, ogr.OFTString, ogr.OFTString, ogr.OFTString, ogr.OFTString, ogr.OFTString, ogr.OFTString, ogr.OFTString, ogr.OFTString, ogr.OFTString] for i, f in enumerate(Fields): Field = ogr.FieldDefn(f, Fields_dtype[i]) outLayer.CreateField(Field) # Get the output Layer's Feature Definition outLayerDefn = outLayer.GetLayerDefn() econ_bool_dict = {0: " No", 1: " Yes"} for feature in inLayer: geom = feature.GetGeometryRef() centroid = geom.Centroid() x = round(centroid.GetX(), 1) y = round(centroid.GetY(), 1) outFeature = ogr.Feature(outLayerDefn) try: geom_label = label_list.index((x, y)) except: nearest_dist = 1000 for p, point in enumerate(label_list): x_temp, y_temp = point temp_dist = ((x-x_temp)**2 + (y-y_temp)**2)**0.5 if temp_dist < nearest_dist: geom_label = p outFeature.SetField(outLayerDefn.GetFieldDefn(0).GetNameRef(), geom_label+1) outFeature.SetField(outLayerDefn.GetFieldDefn(1).GetNameRef(), econ_bool_dict[int(economic_bool[geom_label])]) # demand in GWh outFeature.SetField(outLayerDefn.GetFieldDefn(2).GetNameRef(), str(round(heat_dem_coh_last[geom_label]/1000, 2)) + " GWh") outFeature.SetField(outLayerDefn.GetFieldDefn(3).GetNameRef(), str(round(heat_dem_spec_area[geom_label], 2)) + " MWh/ha") # potential in GWh outFeature.SetField(outLayerDefn.GetFieldDefn(4).GetNameRef(), str(round(q[geom_label]/1000, 2)) + " GWh") outFeature.SetField(outLayerDefn.GetFieldDefn(5).GetNameRef(), str(round(q_spec_cost[geom_label], 2)) + " EUR/MWh") outFeature.SetField(outLayerDefn.GetFieldDefn(6).GetNameRef(), str(area_coh_area[geom_label]) + " hectare") outFeature.SetField(outLayerDefn.GetFieldDefn(7).GetNameRef(), color_map[int(economic_bool[geom_label])]) outFeature.SetField(outLayerDefn.GetFieldDefn(8).GetNameRef(), color_map[int(economic_bool[geom_label])]) outFeature.SetField(outLayerDefn.GetFieldDefn(9).GetNameRef(), "0.7") outFeature.SetGeometry(geom) # Add new feature to output Layer outLayer.CreateFeature(outFeature) outFeature = None # Save and close DataSources inDataSource = None outDataSource = None if os.path.exists(out_shp_prelabel): outDriver.DeleteDataSource(out_shp_prelabel)
def dh_demand(c1, c2, raster_plotratio, raster_hdm, start_year, last_year, accumulated_energy_saving, dh_connection_rate_1st_year, dh_connection_rate_last_year, depr_period, interest, output_layers, dA_slope=0.0486, dA_intercept=0.0007, dataType='float32'): ''' Important Note: 1) Here, for the calculation of plot ratio, I used gross floor area raster in one hectar resolution (unit: m2). It should be divided by 1e4 to get the plot ratio. 2) if you have plot ratio raster, remove the 1e4 factor. 3) Distribution cost is calculated for those pixel that their corresponding pipe diameter is equal or greater than 0. the input heat density map should be in GWh/km2. ''' horizon = int(last_year) - int(start_year) + 1 horizon = int(horizon) if horizon > int(depr_period): horizon = depr_period remaining_years = 0 else: remaining_years = int(depr_period) - int(horizon) energy_reduction_factor = (1-float(accumulated_energy_saving))**(1/(horizon-1)) hdm_ds = gdal.Open(raster_hdm) hdm_band = hdm_ds.GetRasterBand(1) hdm = hdm_band.ReadAsArray().astype(float) geo_transform = hdm_ds.GetGeoTransform() plotRatio_ds = gdal.Open(raster_plotratio) plotRatio_band = plotRatio_ds.GetRasterBand(1) # gfa in hectar should be devided by 10000 to get right values for # plot ratio (m2/m2). plotRatio = plotRatio_band.ReadAsArray().astype(float)/10000.0 hdm_ds = plotRatio_ds = None row, col = np.nonzero((hdm > 0).astype(int) * (plotRatio > 0.0).astype(int)) # unit conversion from MWh/ha to GJ/m2 sparseDemand = 0.00036*hdm[row, col] PR_sparse = plotRatio[row, col] # the unit for L is m however in each m2 # L is in m: to get the value for each pixel (ha) you should multiply it # by 10000 because 1 pixel has 10000 m2 # The following formulation of L comes from Persson et al. 2019 paper with # the title "Heat Roadmap Europe: Heat distribution costs" L = 1 / ((PR_sparse <= 0.4).astype(int) * (137.5*PR_sparse + 5) + (PR_sparse > 0.4).astype(int) * 60) # initialize the variables q = 0 # q_new = dh_connection_rate_1st_year * sparseDemand q_tot = np.copy(sparseDemand) q_max = np.zeros_like(q_tot) for i in range(horizon): q_tot = sparseDemand * energy_reduction_factor**i q_new = q_tot * (float(dh_connection_rate_1st_year) + i * (float(dh_connection_rate_last_year) - float(dh_connection_rate_1st_year))/(horizon-1)) # q_new is a non-zero sparse matrix. The development of demand can be # checked by comparing just one element of q_new with q_max. if q_new[0] > q_max[0]: q_max = np.copy(q_new) q += q_new / (1 + float(interest))**i if remaining_years > 0: if interest > 0: alpha_horizon = annuity(interest, horizon-1) alpha_depr = annuity(interest, depr_period) rest_annuity_factor = alpha_depr - alpha_horizon q = q + q_new * rest_annuity_factor else: q = q + q_new * remaining_years linearHeatDensity = q_max / L # this step is performed to avoid negative average pipe diameter LHD_THRESHOLD = -dA_intercept/dA_slope filtered_LHD = (np.log(linearHeatDensity) < LHD_THRESHOLD).astype(int) elements = np.nonzero(filtered_LHD)[0] dA = dA_slope * (np.log(linearHeatDensity)) + dA_slope dA[elements] = 0 # lower limit of linear heat densities at 1.5 GJ/m was set. Below this # threshold, pipe diameters of 0.02m were applied uniformly for all hectare # grid cells with present heat density values above zero. # Note: linearHeatDensity is calculated for cells with heat demand above zero dA[((linearHeatDensity < 1.5).astype(int) * (dA > 0).astype(int)).astype(bool)] = 0.02 q_max[elements] = 0 denominator = q / L """ # old code cf1, cf2 = cost_factors(c1, c2, PR_sparse) divisor = cf1[1] + cf2[1]*dA """ divisor = c1 + c2*dA divisor[elements] = 0 investment = divisor/denominator finalInvestment = np.zeros_like(hdm, dtype=dataType) # from Euro/GJ/m2 to Euro/MWh/m2 finalInvestment[row, col] = investment*3.6 maxDHdem_arr = np.zeros_like(finalInvestment, dtype=dataType) # max DH demand density in MWh within the study horizon maxDHdem_arr[row, col] = q_max*10000/3.6 invest_Euro_arr = maxDHdem_arr * finalInvestment hdm_last_year = np.zeros_like(finalInvestment, dtype=dataType) # total demand in the last year of study horizon in MWh/ha hdm_last_year[row, col] = q_tot*10000/3.6 # Length of pipes (L) length = np.zeros_like(finalInvestment, dtype=dataType) length[row, col] = L length[row, col][elements] = 0 maxDHdem, invest_Euro, hdm_cut_last_year, total_pipe_length = output_layers rm_file(maxDHdem, invest_Euro, hdm_cut_last_year, total_pipe_length) CM19.main(maxDHdem, geo_transform, dataType, maxDHdem_arr) CM19.main(invest_Euro, geo_transform, dataType, invest_Euro_arr) CM19.main(hdm_cut_last_year, geo_transform, dataType, hdm_last_year) CM19.main(total_pipe_length, geo_transform, dataType, length) """
def clip_raster(rast, features_path, output_dir, gt, nodata=-9999, save2csv=None, save2raster=None, save2shp=None, unit_multiplier=None, return_array=False, OutputSRS=3035, suffix_namefield=None, add_suffix=None): ''' Clips a raster (given a numpy.array and its geo-transform array) to a polygon layer provided by a Shapefile (or other vector layer). Returns an array. Clip features can be multi-part geometry and with interior ring inside them. The code supports most of raster and clip vector arrangements; however, it does not support cases in which clip vector extent (envlope of it) goes beyond more than 2 sides of the raster. Arguments: rast A NumPy array features_path The path to the clipping layer gt GDAL GeoTransform array nodata The NoData value; defaults to -9999 save2csv should the outputs be saved in a csv file as well? unit_multiplier Factor to be multiplied into the summation to output the desired unit. suffix_namefield What to add at the end of the file name. must be field of shape file ''' def image_to_array(i): ''' Converts a Python Imaging Library (PIL) array to a gdalnumeric image. ''' a = gdalnumeric.fromstring(i.tobytes(), 'b') a.shape = i.im.size[1], i.im.size[0] return a def world_to_pixel(geo_matrix, x, y): ''' Uses a gdal geomatrix (gdal.GetGeoTransform()) to calculate the pixel location of a geospatial coordinate; ''' ulX = geo_matrix[0] ulY = geo_matrix[3] xDist = geo_matrix[1] yDist = geo_matrix[5] rtnX = geo_matrix[2] rtnY = geo_matrix[4] pixel = int((x - ulX) / xDist) line = int((ulY - y) / xDist) return (pixel, line) clip_complete = None # get shapefile name shpName = features_path.replace('\\', '/') shpName = shpName.split('/')[-1][0:-4] # Create a data array for the output csv if save2csv: feat = [] demand = [] if unit_multiplier is None: unit_multiplier = 1.0 xRes, yRes = rast.shape # Create an OGR layer from a boundary shapefile features = ogr.Open(features_path) if features.GetDriver().GetName() == 'ESRI Shapefile': temp = os.path.split(os.path.splitext(features_path)[0]) lyr = features.GetLayer(temp[1]) else: lyr = features.GetLayer() for fid in range(lyr.GetFeatureCount()): flag = np.zeros(4) ''' if fid > 40: continue ''' poly = lyr.GetFeature(fid) geom = poly.GetGeometryRef() if suffix_namefield is not None: suffix = poly.GetField(suffix_namefield) if add_suffix is not None: suffix = '_'+ add_suffix + '_'+ suffix else: suffix = 'feature_' + str(fid) # Convert the feature extent to image pixel coordinates minX, maxX, minY, maxY = geom.GetEnvelope() ulX, ulY = world_to_pixel(gt, minX, maxY) lrX, lrY = world_to_pixel(gt, maxX, minY) # Calculate the pixel size of the new image pxWidth = int(lrX - ulX) pxHeight = int(lrY - ulY) # If the clipping features extend out-of-bounds and ABOVE the raster... if gt[3] < maxY: if gt[3] < minY: continue else: # In such a case. ulY ends up being negative--can't have that! ulY = 0 flag[0] = 1 if gt[0] > minX: if gt[0] > maxX: continue else: ulX = 0 flag[1] = 1 rastXmax = gt[0] + yRes * gt[1] if rastXmax < maxX: if rastXmax < minX: continue else: lrX = yRes flag[2] = 1 rastYmin = gt[3] + xRes * gt[5] if rastYmin > minY: if rastYmin > maxY: continue else: lrY = xRes flag[3] = 1 flag_sum = np.sum(flag) # Multi-band image? try: clip = rast[:, ulY:lrY, ulX:lrX] except IndexError: clip = rast[ulY:lrY, ulX:lrX] geometry_counts = geom.GetGeometryCount() clip_complete = np.zeros((pxHeight, pxWidth), clip.dtype) # perform the process for multi-part features for i in range(geometry_counts): # Do not delete this. Clip is set to None in each iteration and # should be initialized here again. try: clip = rast[:, ulY:lrY, ulX:lrX] except IndexError: clip = rast[ulY:lrY, ulX:lrX] # Create a new geomatrix for the image gt2 = list(gt) gt2[0] = gt2[1]*int(minX/gt2[1]) gt2[3] = gt2[1]*int(maxY/gt2[1]) if gt2[3] < maxY: gt2[3] = gt2[1] * int(maxY/gt2[1] + 1) # Map points to pixels for drawing the boundary on a blank 8-bit, # black and white, mask image. points = [] pixels = [] # check multi-part geometries if geometry_counts > 1: geom1 = geom.GetGeometryRef(i) # check multi-part geometry with interior ring if geom1.GetGeometryName() == 'LINEARRING': pts = geom1 else: # get outer ring of polygon pts = geom1.GetGeometryRef(0) # print(geom1.GetGeometryName() + ' ' + pts.GetGeometryName()) else: # get outer ring of polygon pts = geom.GetGeometryRef(0) for p in range(pts.GetPointCount()): points.append((pts.GetX(p), pts.GetY(p))) for p in points: pixels.append(world_to_pixel(gt2, p[0], p[1])) raster_poly = Image.new('L', (pxWidth, pxHeight), 1) rasterize = ImageDraw.Draw(raster_poly) # Fill with zeroes rasterize.polygon(pixels, 0) premask = image_to_array(raster_poly) # with the calculated geotransform matrix gt2, clip matrix should # have the same dimension as premask clip_new = np.zeros_like(premask, clip.dtype) if flag_sum == 0: mask = premask clip_new = gdalnumeric.choose(mask, (clip, nodata)) else: if flag_sum < 3: row_clip, col_clip = clip.shape index = np.array([-flag[0]*row_clip, flag[3]*row_clip, -flag[1]*col_clip, flag[2]*col_clip] ).astype(int) mask_index = np.where(index == 0, None, index) mask = premask[mask_index[0]:mask_index[1], mask_index[2]:mask_index[3]] clip = gdalnumeric.choose(mask, (clip, nodata)) clip_new[mask_index[0]:mask_index[1], mask_index[2]:mask_index[3]] = clip else: s = 1 # raise InputError('Clip for the feature %d is not ' # 'supported' % fid) m1, n1 = np.nonzero(clip_new) clip_stack = set(list(zip(m1, n1))) m2, n2 = np.nonzero(clip_complete) clip_complete_stack = set(list(zip(m2, n2))) intersect_clip = clip_complete_stack.intersection(clip_stack) if len(intersect_clip) == 0: clip_complete = clip_complete + clip_new else: clip_complete = clip_complete - clip_new mask = None premask = None raster_poly = None rasterize = None geom1 = None pts = None gt3 = gt2 gt2 = None clip = None clip_new = None if save2csv: nuts_region = str(poly.GetField(0)) dem_sum = np.sum(clip_complete) * unit_multiplier if dem_sum > 0: feat.append(nuts_region) demand.append(dem_sum) print('The sum of values within the region %s is: %0.1f' % (nuts_region, dem_sum)) if save2raster: if not save2csv: dem_sum = np.sum(clip_complete) * unit_multiplier if dem_sum > 0: outRasterPath = output_dir + os.sep + shpName + '_feature_' + \ str(fid) + '.tif' outRasterPath = output_dir + os.sep + shpName + '_' + \ suffix + '.tif' CM19.main(outRasterPath, gt3, str(clip_complete.dtype), clip_complete, 0) if save2csv or save2shp: saveCSVorSHP(feat, demand, features_path, output_dir, shpName, save2csv, save2shp, OutputSRS=OutputSRS) if return_array: return clip_complete, gt3