matsim-proto/matsim_engine.py
2024-02-06 05:11:26 +01:00

121 lines
3.6 KiB
Python

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)