def get_network_json(self, edges, nodes): if not edges or not nodes: return {} edges_df = geopandas.GeoDataFrame.from_file(edges) nodes_df = geopandas.GeoDataFrame.from_file(nodes) network_json = json.loads(edges_df.to_crs(get_geographic_coordinate_system()).to_json()) network_json['features'].extend( json.loads(nodes_df.to_crs(get_geographic_coordinate_system()).to_json())['features']) return json.dumps(network_json)
def _plot_div_producer(self): """ Since this plot doesn't use plotly to plot, we override _plot_div_producer to return a string containing the html div to use for this plot. The template ``map_div.html`` expects some parameters: Here is some example data (in a YAML-like format for documentation purposes) :: data: DH: connected_buildings: ['B1010', 'B1017', 'B1003'] disconnected_buildings: ['B1000', 'B1009', 'B1016', ..., 'B1015'] path_output_nodes: "{general:scenario}/inputs/networks/DH/gen_3_ind_1/nodes.shp" path_output_edges: "{general:scenario}/inputs/networks/DH/gen_3_ind_1/edges.shp" DC: {} # data does not necessarily contain information for both types of district networks colors: dc: [63, 192, 194] dh: [240, 75, 91] disconnected: [68, 76, 83] district: [255, 255, 255] zone: str serialization of the GeoJSON of the zone.shp district: str serialization of the GeoJSON of the district.shp dc: str serialization of a GeoJSON containing both the nodes.shp + edges.shp of district cooling network dh: str serialization of a GeoJSON containing both the nodes.shp + edges.shp of district heating network :return: a str containing a full html ``<div/>`` that includes the js code to display the map. """ import os import hashlib from jinja2 import Template template = os.path.join(os.path.dirname(__file__), "map_div.html") data = self.data_processing() zone = geopandas.GeoDataFrame.from_file( self.locator.get_zone_geometry()).to_crs( get_geographic_coordinate_system()).to_json(show_bbox=True) district = geopandas.GeoDataFrame.from_file( self.locator.get_surroundings_geometry()).to_crs( get_geographic_coordinate_system()).to_json(show_bbox=True) dc = self.get_network_json(data['DC']['path_output_edges'], data['DC']['path_output_nodes']) dh = self.get_network_json(data['DH']['path_output_edges'], data['DH']['path_output_nodes']) # Generate div id using hash of parameters with open(template, "r") as fp: div = Template(fp.read()).render(hash=hashlib.md5( repr(sorted(data.items())).encode("utf-8")).hexdigest(), data=json.dumps(data), colors=json.dumps(COLORS), zone=zone, district=district, dc=dc, dh=dh) return div
def geometry_extractor_osm(locator, config): """this is where the action happens if it is more than a few lines in ``main``. NOTE: ADD YOUR SCRIPT'S DOCUMENATION HERE (how) NOTE: RENAME THIS FUNCTION (SHOULD PROBABLY BE THE SAME NAME AS THE MODULE) """ # local variables: buffer_m = config.surroundings_helper.buffer buildings_height = config.surroundings_helper.height_ag buildings_floors = config.surroundings_helper.floors_ag shapefile_out_path = locator.get_surroundings_geometry() zone = gdf.from_file(locator.get_zone_geometry()) # trnasform zone file to geographic coordinates zone = zone.to_crs(get_geographic_coordinate_system()) lon = zone.geometry[0].centroid.coords.xy[0][0] lat = zone.geometry[0].centroid.coords.xy[1][0] zone = zone.to_crs(get_projected_coordinate_system(float(lat), float(lon))) # get a polygon of the surrounding area, and one polygon representative of the zone area print("Calculating surrounding area") area_with_buffer = calc_surrounding_area(zone, buffer_m) # get footprints of all the surroundings print("Getting building footprints") area_with_buffer_polygon = area_with_buffer.to_crs( get_geographic_coordinate_system()).geometry.values[0] all_surroundings = osmnx.footprints.footprints_from_polygon( polygon=area_with_buffer_polygon) all_surroundings = all_surroundings.to_crs( get_projected_coordinate_system(float(lat), float(lon))) # erase overlapping area print("Removing unwanted buildings") surroundings = erase_no_surrounding_areas(all_surroundings, zone, area_with_buffer) assert surroundings.shape[ 0] > 0, 'No buildings were found within range based on buffer parameter.' # clean attributes of height, name and number of floors result = clean_attributes(surroundings, buildings_height, buildings_floors, key="CEA") result = result.to_crs( get_projected_coordinate_system(float(lat), float(lon))) # save to shapefile result.to_file(shapefile_out_path)
def edges_df(self): edges_df = geopandas.GeoDataFrame.from_file( self.locator.get_network_layout_edges_shapefile(self.network_type, self.network_name)).to_crs( get_geographic_coordinate_system()) edges_df["_LineWidth"] = 0.1 * edges_df["Pipe_DN"] edges_df["length_m"] = edges_df["length_m"].round(1) # color the edges based on aggregated pipe heat loss P_loss_kPaperm_peak = (self.linear_pressure_loss_Paperm.max() / 1000).round(1) #to kPa/m Mass_flow_kgs_peak = self.mass_flow_kgs_pipes.max().round(1) #in kgs edges_df["Peak pressure loss [kPa/m]"] = P_loss_kPaperm_peak.values edges_df["Peak mass flow rate [kg/s]"] = Mass_flow_kgs_peak.values if self.velocity_mps_pipes is not None: #backward compatibility with detailed thermal network (which does not include this output) velocity_ms_peak = self.velocity_mps_pipes.max().round(1) # in kgs edges_df["Peak velocity [m/s]"] = velocity_ms_peak.values # color the edges based on aggregated pipe heat loss if self.thermal_loss_edges_Wperm is not None: #backward compatibility with detailed thermal network (which does not include this output) yearly_thermal_loss = (self.thermal_loss_edges_Wperm.max()).round(2) edges_df["Peak Thermal losses [W/m]"] = yearly_thermal_loss.values # figure out colors p_loss_min = edges_df.Pipe_DN.min() p = edges_df.Pipe_DN.max() scale_p_loss = lambda x: remap(x, p_loss_min, p, 1.0, 1.0) min_rgb_mpl = [remap(c, 0.0, 255.0, 0.0, 1.0) for c in get_color_array("white")] max_rgb_mpl = [remap(c, 0.0, 255.0, 0.0, 1.0) for c in get_color_array("red")] edges_df["_FillColor"] = edges_df.Pipe_DN.apply( lambda p_loss: json.dumps( [remap(x, 0.0, 1.0, 0.0, 255.0) for x in color_fader_rgb(min_rgb_mpl, max_rgb_mpl, mix=scale_p_loss(p_loss))])).values return edges_df
def get_longitude(scenario_path): import cea.inputlocator data = gdf.from_file( cea.inputlocator.InputLocator(scenario_path).get_zone_geometry()) data = data.to_crs(get_geographic_coordinate_system()) longitude = data.geometry[0].centroid.coords.xy[0][0] return longitude
def polygon_to_zone(buildings_floors, buildings_floors_below_ground, buildings_height, buildings_height_below_ground, poly, shapefile_out_path): poly = poly.to_crs(get_geographic_coordinate_system()) lon = poly.geometry[0].centroid.coords.xy[0][0] lat = poly.geometry[0].centroid.coords.xy[1][0] # get footprints of all the district poly = osmnx.footprints.footprints_from_polygon( polygon=poly['geometry'].values[0]) # clean geometries poly = clean_geometries(poly) # clean attributes of height, name and number of floors result, result_allfields = clean_attributes(poly, buildings_height, buildings_floors, buildings_height_below_ground, buildings_floors_below_ground, key="B") result = result.to_crs( get_projected_coordinate_system(float(lat), float(lon))) # save to shapefile result.to_file(shapefile_out_path) return result_allfields
def route_project_overview(): cea_config = current_app.cea_config project_path = cea_config.project project_name = os.path.basename(project_path) # Get the list of scenarios scenarios = get_scenarios(project_path) # Get scenario descriptions descriptions = {} for scenario in scenarios: descriptions[scenario] = {} locator = cea.inputlocator.InputLocator(os.path.join(project_path, scenario)) zone = locator.get_zone_geometry() if os.path.isfile(zone): try: zone_df = geopandas.read_file(zone).to_crs(get_geographic_coordinate_system()) descriptions[scenario]['Coordinates'] = (float("%.5f" % ((zone_df.total_bounds[1] + zone_df.total_bounds[3])/2)), float("%.5f" % ((zone_df.total_bounds[0] + zone_df.total_bounds[2])/2))) except RuntimeError as e: descriptions[scenario]['Warning'] = 'Could not read the Zone file. ' \ 'Check if your geometries have a coordinate system.' else: descriptions[scenario]['Warning'] = 'Zone file does not exist.' # Clean .cache images for filepath in glob.glob(os.path.join(os.path.join(project_path, '.cache', '*.png'))): print(filepath) image = os.path.basename(filepath).split('.')[0] if image not in scenarios: os.remove(filepath) return render_template('project_overview.html', project_name=project_name, scenarios=scenarios, descriptions=descriptions)
def create_polygon(coordinate_tuple_list, output_path, filename): poly = Polygon(coordinate_tuple_list) gdf = gpd.GeoDataFrame([{'geometry': poly}]) gdf.crs = get_geographic_coordinate_system() # Make sure directory exists os.makedirs(output_path, exist_ok=True) gdf.to_file(os.path.join(output_path, '{filename}.shp'.format(filename=filename))) print('Polygon `{filename}` created in {output_path}'.format(filename=filename, output_path=output_path))
def calc_bounding_box(shapefile_district, shapefile_zone): #connect both files and avoid repetition data_zone = Gdf.from_file(shapefile_zone) data_dis = Gdf.from_file(shapefile_district) data_dis = data_dis.loc[~data_dis["Name"].isin(data_zone["Name"])] data = data_dis.append(data_zone, ignore_index=True, sort=True) data = data.to_crs(get_geographic_coordinate_system()) result = data.total_bounds # in float return result
def _plot_div_producer(self): """ Since this plot doesn't use plotly to plot, we override _plot_div_producer to return a string containing the html div to use for this plot. The template ``network_plot.html`` expects some parameters: - hash: this is used to make the html id's in the plot unique - edges: a GeoJson serialization of the networks edges and data - nodes: a GeoJson serialization of the networks nodes and data, - colors: a JSON dictionary of [R, G, B] arrays for the colors to use - zone: a GeoJson serialization of the zone's buildings - district: a GeoJson serialization of the distrct's buildings :return: a str containing a full html ``<div/>`` that includes the js code to display the map. """ import os import hashlib import random from jinja2 import Template zone_df = geopandas.GeoDataFrame.from_file( self.locator.get_zone_geometry()).to_crs( get_geographic_coordinate_system()) zone_df["_FillColor"] = json.dumps(self.colors["zone"]) zone = zone_df.to_json(show_bbox=True) district_df = geopandas.GeoDataFrame.from_file( self.locator.get_surroundings_geometry()).to_crs( get_geographic_coordinate_system()) district_df["_FillColor"] = json.dumps(self.colors["district"]) district = district_df.to_json(show_bbox=True) edges = self.edges_df.to_json(show_bbox=True) nodes = self.nodes_df.to_json(show_bbox=True) hash = hashlib.md5((str(random.random()) + edges + nodes).encode("utf-8")).hexdigest() template = os.path.join(os.path.dirname(__file__), "network_plot.html") div = Template(open(template).read()).render(hash=hash, edges=edges, nodes=nodes, zone=zone, district=district) return div
def df_to_json(file_location): try: table_df = geopandas.GeoDataFrame.from_file(file_location) from cea.utilities.standardize_coordinates import get_geographic_coordinate_system table_df = table_df.to_crs(get_geographic_coordinate_system( )) # make sure that the geojson is coded in latitude / longitude return json.loads(table_df.to_json()) except IOError as e: print(e) abort(404, 'Input file not found: %s' % file_location)
def get(self, scenario): project = request.args.get('project') if project is None: config = current_app.cea_config else: if not os.path.exists(project): abort(400, 'Project path: "{project}" does not exist'.format(project=project)) config = cea.config.Configuration() config.project = project choices = list_scenario_names_for_project(config) if scenario in choices: locator = cea.inputlocator.InputLocator(os.path.join(config.project, scenario)) zone_path = locator.get_zone_geometry() if os.path.isfile(zone_path): cache_path = os.path.join(config.project, '.cache') image_path = os.path.join(cache_path, scenario + '.png') zone_modified = os.path.getmtime(zone_path) if not os.path.isfile(image_path): image_modified = 0 else: image_modified = os.path.getmtime(image_path) if zone_modified > image_modified: # Make sure .cache folder exists if not os.path.exists(cache_path): os.makedirs(cache_path) try: zone_df = geopandas.read_file(zone_path) zone_df = zone_df.to_crs(get_geographic_coordinate_system()) polygons = zone_df['geometry'] polygons = [list(polygons.geometry.exterior[row_id].coords) for row_id in range(polygons.shape[0])] m = StaticMap(256, 160) for polygon in polygons: out = Polygon(polygon, 'blue', 'black', False) m.add_polygon(out) image = m.render() image.save(image_path) except Exception as e: abort(400, str(e)) import base64 with open(image_path, 'rb') as imgFile: image = base64.b64encode(imgFile.read()) return {'image': image.decode("utf-8")} abort(400, 'Zone file not found') else: abort(400, 'Scenario does not exist', choices=choices)
def calc_building_centroids(input_buildings_shp, temp_path_building_centroids_shp, list_district_scale_buildings, plant_buildings, consider_only_buildings_with_demand=False, type_network="DH", total_demand=False): # # get coordinate system and project to WSG 84 zone_df = gdf.from_file(input_buildings_shp) zone_df = zone_df.loc[zone_df['Name'].isin(list_district_scale_buildings + plant_buildings)] zone_df = zone_df.reset_index(drop=True) # get only buildings with a demand, send out a message if there are less than 2 buildings. if consider_only_buildings_with_demand: total_demand = pd.read_csv(total_demand) if type_network == "DH": field = "QH_sys_MWhyr" elif type_network == "DC": field = "QC_sys_MWhyr" buildings_with_load = total_demand[ total_demand[field] > 0.0].Name.tolist() selected_buildings = list( set(buildings_with_load).union(set(plant_buildings))) if len(selected_buildings) >= 2: zone_df = zone_df.loc[zone_df['Name'].isin(selected_buildings)] zone_df = zone_df.reset_index(drop=True) else: raise Exception( "We could not find two or more buildings with thermal energy demand the network layout " "will not work unless the consider_only_buildings_with_demand parameter is set to False" ) zone_df = zone_df.to_crs(get_geographic_coordinate_system()) lon = zone_df.geometry[0].centroid.coords.xy[0][0] lat = zone_df.geometry[0].centroid.coords.xy[1][0] # get coordinate system and re project to UTM zone_df = zone_df.to_crs(get_projected_coordinate_system(lat, lon)) # create points with centroid points = zone_df.copy() points.geometry = zone_df['geometry'].centroid # # decrease the number of units of the points building_centroids_df = simplify_points_accurracy(points, SHAPEFILE_TOLERANCE, points.crs) # saving result building_centroids_df.to_file(temp_path_building_centroids_shp, driver='ESRI Shapefile') return building_centroids_df
def route_save_building_properties(): data = request.get_json() changes = data['changes'] tables = data['tables'] geojson = data['geojson'] crs = data['crs'] out = {'tables': {}, 'geojsons': {}} # TODO: Maybe save the files to temp location in case something fails locator = cea.inputlocator.InputLocator(current_app.cea_config.scenario) for db in INPUTS: db_info = INPUTS[db] location = getattr(locator, db_info['location'])() if len(tables[db]) != 0: if db_info['type'] == 'shp': from cea.utilities.standardize_coordinates import get_geographic_coordinate_system table_df = geopandas.GeoDataFrame.from_features( geojson[db]['features'], crs=get_geographic_coordinate_system()) out['geojsons'][db] = json.loads( table_df.to_json(show_bbox=True)) table_df = table_df.to_crs(crs[db]) table_df.to_file(location, driver='ESRI Shapefile', encoding='ISO-8859-1') table_df = pandas.DataFrame(table_df.drop(columns='geometry')) out['tables'][db] = json.loads( table_df.set_index('Name').to_json(orient='index')) elif db_info['type'] == 'dbf': table_df = pandas.read_json(json.dumps(tables[db])) cea.utilities.dbf.dataframe_to_dbf(table_df, location) out['tables'][db] = json.loads( table_df.set_index('Name').to_json(orient='index')) else: # delete file if empty out['tables'][db] = {} if os.path.isfile(location): if db_info['type'] == 'shp': import glob for filepath in glob.glob( os.path.join( locator.get_building_geometry_folder(), '%s.*' % db)): os.remove(filepath) elif db_info['type'] == 'dbf': os.remove(location) if db_info['type'] == 'shp': out['geojsons'][db] = {} return jsonify(out)
def route_get_building_properties(): import cea.glossary div = request.args.get('div', default=False) # FIXME: Find a better way to ensure order of tabs tabs = ['zone', 'age', 'occupancy', 'architecture', 'internal-loads', 'indoor-comfort', 'air-conditioning-systems', 'supply-systems', 'emission-intensity', 'surroundings'] locator = cea.inputlocator.InputLocator(current_app.cea_config.scenario) store = {'tables': {}, 'geojsons': {}, 'columns': {}, 'column_types': {}, 'crs': {}, 'glossary': {}} glossary = cea.glossary.read_glossary_df() for db in INPUTS: db_info = INPUTS[db] location = getattr(locator, db_info['location'])() try: if db_info['type'] == 'shp': from cea.utilities.standardize_coordinates import shapefile_to_WSG_and_UTM, \ get_geographic_coordinate_system table_df, lat, lon = shapefile_to_WSG_and_UTM(location) # save projected coordinate system store['crs'][db] = get_projected_coordinate_system(lat, lon) store['geojsons'][db] = json.loads( table_df.to_crs(get_geographic_coordinate_system()).to_json(show_bbox=True)) table_df = pandas.DataFrame(table_df.drop(columns='geometry')) if 'REFERENCE' in db_info['fieldnames'] and 'REFERENCE' not in table_df.columns: table_df['REFERENCE'] = None store['tables'][db] = json.loads(table_df.set_index('Name').to_json(orient='index')) else: assert db_info['type'] == 'dbf', 'Unexpected database type: %s' % db_info['type'] table_df = cea.utilities.dbf.dbf_to_dataframe(location) if 'REFERENCE' in db_info['fieldnames'] and 'REFERENCE' not in table_df.columns: table_df['REFERENCE'] = None store['tables'][db] = json.loads(table_df.set_index('Name').to_json(orient='index')) store['columns'][db] = db_info['fieldnames'] store['column_types'][db] = {k: v.__name__ for k, v in db_info['fieldtypes'].items()} filenames = glossary['FILE_NAME'].str.split(pat='/').str[-1] store['glossary'].update(json.loads(glossary[filenames == '%s.%s' % (db.replace('-', '_'), db_info['type'])] [['VARIABLE', 'UNIT', 'DESCRIPTION']].set_index('VARIABLE').to_json( orient='index'))) except IOError as e: print(e) store['tables'][db] = {} return render_template('table.html' if not div else 'input_editor_electron.html', store=store, tabs=tabs, last_updated=dir_last_updated())
def _plot_div_producer(self): import os import hashlib from jinja2 import Template template = os.path.join(os.path.dirname(__file__), "map_div.html") data = self.data_processing() zone = geopandas.GeoDataFrame.from_file(self.locator.get_zone_geometry())\ .to_crs(get_geographic_coordinate_system()).to_json(show_bbox=True) district = geopandas.GeoDataFrame.from_file(self.locator.get_district_geometry()) \ .to_crs(get_geographic_coordinate_system()).to_json(show_bbox=True) dc = self.get_newtork_json(data['path_output_edges_DC'], data['path_output_nodes_DC']) dh = self.get_newtork_json(data['path_output_edges_DH'], data['path_output_nodes_DH']) # Generate div id using hash of parameters div = Template(open(template).read())\ .render(hash=hashlib.md5(repr(sorted(data.items()))).hexdigest(), data=json.dumps(data), colors=COLORS, zone=zone, district=district, dc=dc, dh=dh) return div
def calc_connectivity_network(path_streets_shp, building_centroids_df, temp_path_potential_network_shp): """ This script outputs a potential network connecting a series of building points to the closest street network the street network is assumed to be a good path to the district heating or cooling network :param path_streets_shp: path to street shapefile :param building_centroids_df: path to substations in buildings (or close by) :param path_potential_network: output path shapefile :return: """ # first get the street network street_network = gdf.from_file(path_streets_shp) # check coordinate system street_network = street_network.to_crs(get_geographic_coordinate_system()) lon = street_network.geometry[0].centroid.coords.xy[0][0] lat = street_network.geometry[0].centroid.coords.xy[1][0] street_network = street_network.to_crs(get_projected_coordinate_system(lat, lon)) crs = street_network.crs street_network = simplify_liness_accurracy(street_network.geometry.values, SHAPEFILE_TOLERANCE, crs) # create terminals/branches form street to buildings prototype_network = create_terminals(building_centroids_df, crs, street_network) config = cea.config.Configuration() locator = cea.inputlocator.InputLocator(scenario=config.scenario) # first split in intersections prototype_network = one_linestring_per_intersection(prototype_network.geometry.values, crs) # snap endings of all vectors to ending of all other vectors prototype_network = snappy_endings(prototype_network.geometry.values, SNAP_TOLERANCE, crs) # calculate intersections gdf_points_snapped = calculate_end_points_intersections(prototype_network, crs) # snap these points to the lines and transform lines gdf_points_snapped, prototype_network = snap_points(gdf_points_snapped, prototype_network, SNAP_TOLERANCE, crs) # save for verification purposes prototype_network.to_file(locator.get_temporary_file("prototype_network.shp"), driver='ESRI Shapefile') # get segments potential_network_df = split_line_by_nearest_points(prototype_network, gdf_points_snapped, 1.0, crs) # calculate Shape_len field potential_network_df["Shape_Leng"] = potential_network_df["geometry"].apply(lambda x: x.length) potential_network_df.to_file(temp_path_potential_network_shp, driver='ESRI Shapefile') return crs
def calc_bounding_box_projected_coordinates(shapefile_zone, shapefile_surroundings): # connect both files and avoid repetition data_zone = Gdf.from_file(shapefile_zone) data_dis = Gdf.from_file(shapefile_surroundings) data_dis = data_dis.loc[~data_dis["Name"].isin(data_zone["Name"])] data = data_dis.append(data_zone, ignore_index = True, sort=True) data = data.to_crs(get_geographic_coordinate_system()) lon = data.geometry[0].centroid.coords.xy[0][0] lat = data.geometry[0].centroid.coords.xy[1][0] crs = get_projected_coordinate_system(float(lat), float(lon)) data = data.to_crs(get_projected_coordinate_system(float(lat), float(lon))) result = data.total_bounds result = [np.float32(x) for x in result] # in float32 so the raster works return result, crs, lon, lat
def calc_substation_location(input_buildings_shp, output_substations_shp, connected_buildings, consider_only_buildings_with_demand=False, type_network="DH", total_demand=False): # # get coordinate system and project to WSG 84 poly = gdf.from_file(input_buildings_shp) if connected_buildings != []: # get only buildings described poly = poly.loc[poly['Name'].isin(connected_buildings)] poly = poly.reset_index(drop=True) # get only buildings with a demand, send out a message if there are less than 2 buildings. if consider_only_buildings_with_demand: total_demand = pd.read_csv(total_demand) if type_network == "DH": field = "QH_sys_MWhyr" elif type_network == "DC": field = "QC_sys_MWhyr" buildings_with_load_df = total_demand[total_demand[field] > 0.0] if buildings_with_load_df.shape[0] >= 2: buildings_with_load = buildings_with_load_df['Name'].tolist() poly = poly.loc[poly['Name'].isin(buildings_with_load)] poly = poly.reset_index(drop=True) else: raise Exception( "We could not find two or more buildings with thermal energy demand the network layout " "will not work unless the consider_only_buildings_with_demand parameter is set to False" ) poly = poly.to_crs(get_geographic_coordinate_system()) lon = poly.geometry[0].centroid.coords.xy[0][0] lat = poly.geometry[0].centroid.coords.xy[1][0] # get coordinate system and re project to UTM poly = poly.to_crs(get_projected_coordinate_system(lat, lon)) # create points points = poly.copy() points.geometry = poly['geometry'].centroid # saving result points.to_file(output_substations_shp, driver='ESRI Shapefile') return points, poly
def df_to_json(file_location, bbox=False): from cea.utilities.standardize_coordinates import get_lat_lon_projected_shapefile, get_projected_coordinate_system try: table_df = geopandas.GeoDataFrame.from_file(file_location) # Save coordinate system lat, lon = get_lat_lon_projected_shapefile(table_df) crs = get_projected_coordinate_system(lat, lon) # make sure that the geojson is coded in latitude / longitude out = table_df.to_crs(get_geographic_coordinate_system()) out = json.loads(out.to_json(show_bbox=bbox)) return out, crs except (IOError, DriverError) as e: print(e) return None, None except Exception as e: traceback.print_exc() return None, None
def route_get_images(scenario): cea_config = current_app.cea_config project_path = cea_config.project locator = cea.inputlocator.InputLocator( os.path.join(project_path, scenario)) zone_path = locator.get_zone_geometry() if not os.path.isfile(zone_path): abort(404, 'Zone file not found') cache_path = os.path.join(project_path, '.cache') image_path = os.path.join(cache_path, scenario + '.png') zone_modified = os.path.getmtime(zone_path) if not os.path.isfile(image_path): image_modified = 0 else: image_modified = os.path.getmtime(image_path) if zone_modified > image_modified: # Make sure .cache folder exists if not os.path.exists(cache_path): os.makedirs(cache_path) zone_df = geopandas.read_file(zone_path) zone_df = zone_df.to_crs(get_geographic_coordinate_system()) polygons = zone_df['geometry'] polygons = [ list(polygons.geometry.exterior[row_id].coords) for row_id in range(polygons.shape[0]) ] m = StaticMap(256, 160) for polygon in polygons: out = Polygon(polygon, 'blue', 'black', False) m.add_polygon(out) image = m.render() image.save(image_path) import base64 with open(image_path, 'rb') as imgFile: image = base64.b64encode(imgFile.read()) return image
def df_to_json(file_location, bbox=False, trigger_abort=True): from cea.utilities.standardize_coordinates import get_lat_lon_projected_shapefile, get_projected_coordinate_system try: table_df = geopandas.GeoDataFrame.from_file(file_location) # Save coordinate system lat, lon = get_lat_lon_projected_shapefile(table_df) crs = get_projected_coordinate_system(lat, lon) # make sure that the geojson is coded in latitude / longitude out = table_df.to_crs(get_geographic_coordinate_system()) out = json.loads(out.to_json(show_bbox=bbox)) return out, crs except IOError as e: print(e) if trigger_abort: abort(400, 'Input file not found: %s' % file_location) except RuntimeError as e: print(e) if trigger_abort: abort(400, e.message)
def calc_substation_location(input_buildings_shp, output_substations_shp, connected_buildings): # # get coordinate system and project to WSG 84 poly = gdf.from_file(input_buildings_shp) if connected_buildings != []: #get only buildings poly = poly.loc[poly['Name'].isin(connected_buildings)] poly = poly.reset_index(drop=True) poly = poly.to_crs(get_geographic_coordinate_system()) lon = poly.geometry[0].centroid.coords.xy[0][0] lat = poly.geometry[0].centroid.coords.xy[1][0] # get coordinate system and re project to UTM poly = poly.to_crs(get_projected_coordinate_system(lat, lon)) # create points points = poly.copy() points.geometry = poly['geometry'].centroid # saving result points.to_file(output_substations_shp, driver='ESRI Shapefile')
def calc_connectivity_network(path_streets_shp, path_connection_point_buildings_shp, path_potential_network): """ This script outputs a potential network connecting a series of building points to the closest street network the street network is assumed to be a good path to the district heating or cooling network :param path_streets_shp: path to street shapefile :param path_connection_point_buildings_shp: path to substations in buildings (or close by) :param path_potential_network: output path shapefile :return: """ # first get the building centroids and the street network buiding_centroids = gdf.from_file(path_connection_point_buildings_shp) street_network = gdf.from_file(path_streets_shp) # check coordinate system street_network = street_network.to_crs(get_geographic_coordinate_system()) lon = street_network.geometry[0].centroid.coords.xy[0][0] lat = street_network.geometry[0].centroid.coords.xy[1][0] street_network = street_network.to_crs(get_projected_coordinate_system(lat, lon)) crs = street_network.crs # # decrease the number of units of the points tolerance = 6 buiding_centroids = simplify_points_accurracy(buiding_centroids, tolerance, crs) street_network = simplify_liness_accurracy(street_network.geometry.values, tolerance, crs) # create terminals/branches form street to buildings prototype_network = create_terminals(buiding_centroids, crs, street_network) config = cea.config.Configuration() locator=cea.inputlocator.InputLocator(scenario=config.scenario) prototype_network.to_file(locator.get_temporary_file("prototype_network.shp"), driver='ESRI Shapefile') # first split in intersections prototype_network = one_linestring_per_intersection(prototype_network.geometry.values, crs) # snap endings of all vectors to ending of all other vectors prototype_network = snappy_endings(prototype_network.geometry.values, 0.5, crs) # calculate intersections gdf_points_snapped = calculate_end_points_intersections(prototype_network, crs) # snap these points to the lines and transform lines gdf_points_snapped, prototype_network = snap_points(gdf_points_snapped, prototype_network, crs) # get segments gdf_segments = split_line_by_nearest_points(prototype_network, gdf_points_snapped, 1.0, crs) # calculate Shape_len field gdf_segments["Shape_Leng"] = gdf_segments["geometry"].apply(lambda x: x.length) # add length to segments # gdf_segments.plot() # import matplotlib.pyplot as plt # gdf_points.plot() # gdf_points_snapped.plot() # plt.show() # x=1 gdf_segments.to_file(path_potential_network, driver='ESRI Shapefile') return crs
def route_create_scenario_save(): cea_config = current_app.cea_config # Make sure that the scenario folder exists try: os.makedirs( os.path.join(cea_config.project, request.form.get('scenario-name'))) except OSError as e: print(e.message) cea_config.scenario_name = request.form.get('scenario-name') cea_config.save() scenario_path = cea_config.scenario locator = cea.inputlocator.InputLocator(scenario_path) if request.form.get('input-files') == 'import': zone = request.form.get('zone') surroundings = request.form.get('surroundings') terrain = request.form.get('terrain') streets = request.form.get('streets') typology = request.form.get('typology') # since we're creating a new scenario, go ahead and and make sure we have # the folders _before_ we try copying to them locator.ensure_parent_folder_exists(locator.get_zone_geometry()) locator.ensure_parent_folder_exists(locator.get_terrain()) locator.ensure_parent_folder_exists(locator.get_building_typology()) locator.ensure_parent_folder_exists(locator.get_street_network()) if zone: for filename in glob_shapefile_auxilaries(zone): shutil.copy(filename, locator.get_building_geometry_folder()) if surroundings: for filename in glob_shapefile_auxilaries(surroundings): shutil.copy(filename, locator.get_building_geometry_folder()) if terrain: shutil.copyfile(terrain, locator.get_terrain()) if streets: shutil.copyfile(streets, locator.get_street_network()) if typology: shutil.copyfile(typology, locator.get_building_typology()) elif zone: zone_df = geopandas.read_file(zone) if 'category' not in zone_df.columns: # set 'MULTI_RES' as default calculate_typology_file(zone_df, None, 'MULTI_RES', locator.get_building_typology()) else: calculate_typology_file(zone_df, None, 'Get it from open street maps', locator.get_building_typology()) elif request.form.get('input-files') == 'copy': source_scenario = os.path.join(cea_config.project, request.form.get('scenario')) shutil.copytree( cea.inputlocator.InputLocator(source_scenario).get_input_folder(), locator.get_input_folder()) elif request.form.get('input-files') == 'generate': tools = request.form.getlist('tools') if tools is not None: for tool in tools: if tool == 'zone-helper': # FIXME: Setup a proper endpoint for site creation data = json.loads(request.form.get('poly-string')) site = geopandas.GeoDataFrame( crs=get_geographic_coordinate_system(), geometry=[shape(data['geometry'])]) site_path = locator.get_site_polygon() locator.ensure_parent_folder_exists(site_path) site.to_file(site_path) print('site.shp file created at %s' % site_path) cea.api.zone_helper(cea_config) elif tool == 'surroundings-helper': cea.api.surroundings_helper(cea_config) elif tool == 'streets-helper': cea.api.streets_helper(cea_config) elif tool == 'terrain-helper': cea.api.terrain_helper(cea_config) elif tool == 'weather-helper': cea.api.weather_helper(cea_config) return redirect(url_for('inputs_blueprint.route_get_building_properties'))
def post(self): """Create new scenario""" config = current_app.cea_config payload = api.payload new_scenario_path = os.path.join(config.project, payload['name']) # Make sure that the scenario folder exists try: os.makedirs(new_scenario_path) except OSError as e: print(e.message) locator = cea.inputlocator.InputLocator(new_scenario_path) # Run database_initializer to copy databases to input if 'databases-path' in payload: try: cea.api.data_initializer( config, scenario=new_scenario_path, databases_path=payload['databases-path']) except Exception as e: trace = traceback.format_exc() return { 'message': 'data_initializer: {}'.format(e.message), 'trace': trace }, 500 if payload['input-data'] == 'import': files = payload['files'] if files is not None: try: # since we're creating a new scenario, go ahead and and make sure we have # the folders _before_ we try copying to them locator.ensure_parent_folder_exists( locator.get_zone_geometry()) locator.ensure_parent_folder_exists(locator.get_terrain()) locator.ensure_parent_folder_exists( locator.get_building_typology()) locator.ensure_parent_folder_exists( locator.get_street_network()) if 'zone' in files: for filename in glob_shapefile_auxilaries( files['zone']): shutil.copy(filename, locator.get_building_geometry_folder()) if 'surroundings' in files: for filename in glob_shapefile_auxilaries( files['surroundings']): shutil.copy(filename, locator.get_building_geometry_folder()) if 'terrain' in files: shutil.copyfile(files['terrain'], locator.get_terrain()) if 'streets' in files: shutil.copyfile(files['streets'], locator.get_street_network()) from cea.datamanagement.zone_helper import calculate_age, calculate_typology_file if 'typology' in files and files['typology'] != '': shutil.copyfile(files['typology'], locator.get_building_typology()) elif 'zone' in files: zone_df = geopandas.read_file(files['zone']) if 'category' not in zone_df.columns: # set 'MULTI_RES' as default calculate_typology_file( locator, zone_df, None, 'MULTI_RES', locator.get_building_typology()) else: calculate_typology_file( locator, zone_df, None, 'Get it from open street maps', locator.get_building_typology()) except Exception as e: trace = traceback.format_exc() return {'message': e.message, 'trace': trace}, 500 elif payload['input-data'] == 'copy': try: source_scenario = os.path.join(config.project, payload['copy-scenario']) shutil.copytree( cea.inputlocator.InputLocator( source_scenario).get_input_folder(), locator.get_input_folder()) except OSError as e: trace = traceback.format_exc() return {'message': e.message, 'trace': trace}, 500 elif payload['input-data'] == 'generate': tools = payload['tools'] if tools is not None: for tool in tools: try: if tool == 'zone': # FIXME: Setup a proper endpoint for site creation site = geopandas.GeoDataFrame( crs=get_geographic_coordinate_system(), geometry=[ shape(payload['geojson']['features'][0] ['geometry']) ]) site_path = locator.get_site_polygon() locator.ensure_parent_folder_exists(site_path) site.to_file(site_path) print('site.shp file created at %s' % site_path) cea.api.zone_helper(config, scenario=new_scenario_path) elif tool == 'surroundings': cea.api.surroundings_helper( config, scenario=new_scenario_path) elif tool == 'streets': cea.api.streets_helper(config, scenario=new_scenario_path) elif tool == 'terrain': cea.api.terrain_helper(config, scenario=new_scenario_path) elif tool == 'weather': cea.api.weather_helper(config, scenario=new_scenario_path) except Exception as e: trace = traceback.format_exc() return { 'message': '{}_helper: {}'.format(tool, e.message), 'trace': trace }, 500 return {'scenarios': list_scenario_names_for_project(config)}
def nodes_df(self): nodes_df = geopandas.GeoDataFrame.from_file( self.locator.get_network_layout_nodes_shapefile( self.network_type, self.network_name)).to_crs(get_geographic_coordinate_system()) P_loss_kPa_peak = (self.pressure_at_nodes_Pa.max() / 1000).round( 1) #to kPa nodes_df["Peak pressure [kPa]"] = P_loss_kPa_peak.values #temperature at all nodes temperature_supply = self.temperature_supply_nodes_C.round(1) temperature_return = self.temperature_return_nodes_C.round(1) index_max = self.buildings_hourly.idxmax(axis=0) delta_T = (temperature_supply - temperature_return).round(1) #temperature at the plant index_max_T_plant = self.buildings_hourly.sum(axis=1).idxmax(axis=0) temperature_supply_plant = self.temperature_supply_return_plant_C[ 'temperature_supply_K'] - 273 temperature_return_plant = self.temperature_supply_return_plant_C[ 'temperature_return_K'] - 273 T_supply_peak = round(temperature_supply_plant[index_max_T_plant], 1) delta_T_peak = round((temperature_supply_plant - temperature_return_plant)[index_max_T_plant], 1) #energy generation at the plant annual_loads = self.hourly_loads.values Q_loss_kWh = self.total_thermal_losses_kWh.values Peak_load_MWh = round((annual_loads + Q_loss_kWh).max() / 1000, 1) if self.mass_flow_kgs_nodes is not None: #backward compatibility with detailed thermal network (which does not include this output) Mass_flow_kgs_peak = self.mass_flow_kgs_nodes.max().round(1) nodes_df["Peak mass flow rate [kg/s]"] = Mass_flow_kgs_peak.values peak_demands = self.buildings_hourly.apply(pd.Series.max) pumping_peak = self.plant_pumping_requirement_kWh.max().round(1) def get_peak_building_demand(row): if row["Type"] == "CONSUMER": return peak_demands[row["Building"]] else: return None def get_pumping_node(row): if row["Type"] == "PLANT": return pumping_peak[0] else: return None def get_T_plant_node(row): if row["Type"] == "PLANT": return T_supply_peak else: return None def get_Peak_plant_node(row): if row["Type"] == "PLANT": return Peak_load_MWh else: return None def get_DT_plant_node(row): if row["Type"] == "PLANT": return delta_T_peak else: return None def get_peak_supply_temp(row): if row["Building"] in index_max.index.values: return temperature_supply[row['Name']][index_max[ row["Building"]]] else: return None def get_peak_delta_temp(row): if row["Building"] in index_max.index.values: return delta_T[row['Name']][index_max[row["Building"]]] else: return None nodes_df["Peak Supply Temperature [C]"] = nodes_df.apply( get_peak_supply_temp, axis=1) nodes_df["Peak delta Temperature [C]"] = nodes_df.apply( get_peak_delta_temp, axis=1) nodes_df["Peak Thermal Demand [kW]"] = nodes_df.apply( get_peak_building_demand, axis=1) nodes_df["Pumping Power [kW]"] = nodes_df.apply(get_pumping_node, axis=1) nodes_df["Supply Temperature [C]"] = nodes_df.apply(get_T_plant_node, axis=1) nodes_df["Delta Temperature [C]"] = nodes_df.apply(get_DT_plant_node, axis=1) nodes_df["Peak Thermal Generation [MW]"] = nodes_df.apply( get_Peak_plant_node, axis=1) nodes_df["_Radius"] = self.get_radius(nodes_df) # Figure out the colors (based on the average supply temperatures) P_loss_min = nodes_df["Peak Thermal Demand [kW]"].min() P_loss_max = nodes_df["Peak Thermal Demand [kW]"].max() scale_p_loss = lambda x: remap(x, P_loss_min, P_loss_max, 0.0, 1.0) # matplotlib works on RGB in ranges [0.0, 1.0] - scale the input colors to that, transform and then scale back # to web versions ([0, 255]) min_rgb_mpl = [ remap(c, 0.0, 255.0, 0.0, 1.0) for c in get_color_array("white") ] max_rgb_mpl = [ remap(c, 0.0, 255.0, 0.0, 1.0) for c in get_color_array("red") ] nodes_df["Peak Thermal Demand [kW]"] = nodes_df[ "Peak Thermal Demand [kW]"].fillna(0.0) nodes_df["_FillColor"] = json.dumps(get_color_array("black")) nodes_df["_FillColor"] = nodes_df["Peak Thermal Demand [kW]"].apply( lambda p_loss: json.dumps([ remap(x, 0.0, 1.0, 0.0, 255.0) for x in color_fader_rgb( min_rgb_mpl, max_rgb_mpl, mix=scale_p_loss(p_loss)) ])).values return nodes_df
def put(self): form = api.payload config = current_app.cea_config locator = cea.inputlocator.InputLocator(config.scenario) tables = form['tables'] geojsons = form['geojsons'] crs = form['crs'] schedules = form['schedules'] out = {'tables': {}, 'geojsons': {}} # TODO: Maybe save the files to temp location in case something fails for db in INPUTS: db_info = INPUTS[db] location = getattr(locator, db_info['location'])() file_type = db_info['file_type'] if len(tables[db]) != 0: if file_type == 'shp': from cea.utilities.standardize_coordinates import get_geographic_coordinate_system table_df = geopandas.GeoDataFrame.from_features( geojsons[db]['features'], crs=get_geographic_coordinate_system()) out['geojsons'][db] = json.loads( table_df.to_json(show_bbox=True)) table_df = table_df.to_crs(crs[db]) table_df.to_file(location, driver='ESRI Shapefile', encoding='ISO-8859-1') table_df = pandas.DataFrame( table_df.drop(columns='geometry')) out['tables'][db] = json.loads( table_df.set_index('Name').to_json(orient='index')) elif file_type == 'dbf': table_df = pandas.read_json(json.dumps(tables[db]), orient='index') # Make sure index name is 'Name; table_df.index.name = 'Name' table_df = table_df.reset_index() cea.utilities.dbf.dataframe_to_dbf(table_df, location) out['tables'][db] = json.loads( table_df.set_index('Name').to_json(orient='index')) else: # delete file if empty out['tables'][db] = {} if os.path.isfile(location): if file_type == 'shp': import glob for filepath in glob.glob( os.path.join( locator.get_building_geometry_folder(), '%s.*' % db)): os.remove(filepath) elif file_type == 'dbf': os.remove(location) if file_type == 'shp': out['geojsons'][db] = {} if schedules: for building in schedules: schedule_dict = schedules[building] schedule_path = locator.get_building_weekly_schedules(building) schedule_data = schedule_dict['SCHEDULES'] schedule_complementary_data = { 'MONTHLY_MULTIPLIER': schedule_dict['MONTHLY_MULTIPLIER'], 'METADATA': schedule_dict['METADATA'] } data = pandas.DataFrame() for day in ['WEEKDAY', 'SATURDAY', 'SUNDAY']: df = pandas.DataFrame({ 'HOUR': range(1, 25), 'DAY': [day] * 24 }) for schedule_type, schedule in schedule_data.items(): df[schedule_type] = schedule[day] data = data.append(df, ignore_index=True) save_cea_schedule(data.to_dict('list'), schedule_complementary_data, schedule_path) print('Schedule file written to {}'.format(schedule_path)) return out
def get(self, scenario): building_limit = 500 project = request.args.get('project') if project is None: config = current_app.cea_config else: if not os.path.exists(project): abort( 400, 'Project path: "{project}" does not exist'.format( project=project)) config = cea.config.Configuration() config.project = project choices = list_scenario_names_for_project(config) if scenario in choices: locator = cea.inputlocator.InputLocator( os.path.join(config.project, scenario)) zone_path = locator.get_zone_geometry() if os.path.isfile(zone_path): cache_path = os.path.join(config.project, '.cache') image_path = os.path.join(cache_path, scenario + '.png') zone_modified = os.path.getmtime(zone_path) if not os.path.isfile(image_path): image_modified = 0 else: image_modified = os.path.getmtime(image_path) if zone_modified > image_modified: print(f'Generating preview image for scenario: {scenario}') # Make sure .cache folder exists os.makedirs(cache_path, exist_ok=True) try: zone_df = geopandas.read_file(zone_path) zone_df = zone_df.to_crs( get_geographic_coordinate_system()) polygons = zone_df['geometry'] m = StaticMap(256, 160) if len(polygons) <= building_limit: polygons = [ list(polygons.geometry.exterior[row_id].coords) for row_id in range(polygons.shape[0]) ] for polygon in polygons: out = Polygon(polygon, 'blue', 'black', False) m.add_polygon(out) else: print( f'Number of buildings({len(polygons)}) exceed building limit({building_limit}): ' f'Generating simplified image') # Generate only the shape outline of the zone area convex_hull = polygons.unary_union.convex_hull polygon = convex_hull.exterior.coords out = Polygon(polygon, None, 'blue', False) m.add_polygon(out) image = m.render() image.save(image_path) except Exception as e: abort(400, str(e)) import base64 with open(image_path, 'rb') as imgFile: image = base64.b64encode(imgFile.read()) return {'image': image.decode("utf-8")} abort(400, 'Zone file not found') else: abort(400, 'Scenario does not exist', choices=choices)
def post(self): """Create new scenario""" config = cea.config.Configuration() project = api.payload.get('project') if project is not None: config.project = project scenario_name = api.payload.get('scenario_name') if scenario_name is not None: new_scenario_path = os.path.join(config.project, str(scenario_name).strip()) # Make sure that the scenario folder exists try: os.makedirs(new_scenario_path) except OSError as e: trace = traceback.format_exc() return {'message': e.message, 'trace': trace}, 500 else: return {'message': 'scenario_name parameter cannot be empty'}, 500 locator = cea.inputlocator.InputLocator(new_scenario_path) # Run database_initializer to copy databases to input databases_path = api.payload.get('databases_path') if databases_path is not None: try: cea.api.data_initializer(config, scenario=new_scenario_path, databases_path=databases_path) except Exception as e: trace = traceback.format_exc() return {'message': 'data_initializer: {}'.format(e.message), 'trace': trace}, 500 input_data = api.payload.get('input_data') if input_data == 'import': files = api.payload.get('files') if files is not None: try: cea.api.create_new_scenario(config, scenario=new_scenario_path, zone=files.get('zone'), surroundings=files.get('surroundings'), streets=files.get('streets'), terrain=files.get('terrain'), typology=files.get('typology')) except Exception as e: trace = traceback.format_exc() return {'message': 'create_new_scenario: {}'.format(e.message), 'trace': trace}, 500 elif input_data == 'copy': source_scenario_name = api.payload.get('copy_scenario') try: source_scenario = os.path.join(config.project, source_scenario_name) shutil.copytree(cea.inputlocator.InputLocator(source_scenario).get_input_folder(), locator.get_input_folder()) except OSError as e: trace = traceback.format_exc() return {'message': e.message, 'trace': trace}, 500 elif input_data == 'generate': tools = api.payload.get('tools') if tools is not None: for tool in tools: try: if tool == 'zone': # FIXME: Setup a proper endpoint for site creation site_geojson = api.payload.get('geojson') if site_geojson is None: raise ValueError('Could not find GeoJson for site polygon') site = geopandas.GeoDataFrame(crs=get_geographic_coordinate_system(), geometry=[shape(site_geojson['features'][0]['geometry'])]) site_path = locator.get_site_polygon() locator.ensure_parent_folder_exists(site_path) site.to_file(site_path) print('site.shp file created at %s' % site_path) cea.api.zone_helper(config, scenario=new_scenario_path) elif tool == 'surroundings': cea.api.surroundings_helper(config, scenario=new_scenario_path) elif tool == 'streets': cea.api.streets_helper(config, scenario=new_scenario_path) elif tool == 'terrain': cea.api.terrain_helper(config, scenario=new_scenario_path) elif tool == 'weather': cea.api.weather_helper(config, scenario=new_scenario_path) except Exception as e: trace = traceback.format_exc() return {'message': '{}_helper: {}'.format(tool, e.message), 'trace': trace}, 500 config.restricted_to = None return {'scenarios_list': list_scenario_names_for_project(config)}