import math import subprocess import xmltodict import geopandas as gpd from shapely.geometry import Point from matsim_activity_to_matsim_schedule import MatsimActivityToMatsimSchedule from hub_function_to_matsim_activity import HubFunctionToMatsimActivity # TODO: Please check this https://github.com/matsim-org/matsim-code-examples/issues/63 for the CRS in matsim should match the city class MatSimEngine: def __init__(self, city, output_file_path): self._city = city self._output_file_path = output_file_path facilities_dict = { 'facilities': { '@name': 'Montreal Facilities', 'facility': [] } } hub_function_to_matsim = HubFunctionToMatsimActivity() matsim_schedule = MatsimActivityToMatsimSchedule() buildings_shape_data = { 'id': [], 'geometry': [] } # 1- Facilities # TODO: Add the ability for a building to have multiple activities # TODO: this should come from the factories, please check idf generation for building in city.buildings: activity = hub_function_to_matsim.dictionary[building.function].split(',') schedule = matsim_schedule.dictionary[activity[0]] start_time, end_time = schedule.split('-') for surface in building.grounds: for coord in surface.solid_polygon.coordinates: buildings_shape_data['id'].append(f"{building.name}") buildings_shape_data['geometry'].append(Point(coord[0], coord[1])) facility = { '@id': building.name, '@x': str(building.centroid[0]), '@y': str(building.centroid[1]), 'activity': [] } if len(building.thermal_zones_from_internal_zones) > 1: raise NotImplementedError("multi-zone buildings aren't yet supported") for thermal_zone in building.thermal_zones_from_internal_zones: capacity = thermal_zone.occupancy.occupancy_density * building.floor_area * building.storeys_above_ground for schedule in thermal_zone.occupancy.occupancy_schedules: print(schedule.values) # TODO: use this schedules values to generate more accurate opentimes. # TODO: check http://www.matsim.org/files/dtd/facilities_v1.dtd seems to be missing values here for new_activity in activity: facility['activity'].append({ '@type': new_activity, 'capacity': { '@value': math.ceil(capacity) }, 'opentime': { '@day': 'wkday', '@start_time': start_time, '@end_time': end_time } }) facilities_dict['facilities']['facility'].append(facility) gdf = gpd.GeoDataFrame( buildings_shape_data, crs=city.srs_name ) gdf.to_file("input_files/buildings_shapefile.shp") # Convert the Python dictionary to an XML string xml_content = xmltodict.unparse(facilities_dict, pretty=True) # Write the XML to the file with open(f"{output_file_path}/{city.name}_facilities.xml", 'w') as file: file.write(xml_content) # 2- Network # First get only the part of the network necessary for the simulation java_path = "java" jar_path = "matsim-network-from-osm.jar" command = [ java_path, "-jar", jar_path, "input_files/merged-network.osm.pbf", "input_files/buildings_shapefile.shp", f"{output_file_path}/network.xml.gz" ] subprocess.run(command) # 3- Population # 3.1 - Public Transport # 4- Config Generation def run(self): java_path = "java" jar_path = "matsim.jar" command = [java_path, "-jar", jar_path] # Must generate this config file first. # command.append(config_file_path) subprocess.run(command)