transfer xmltodict for facilities to lxml
This commit is contained in:
parent
c566168b19
commit
6ecba6d5dd
Binary file not shown.
123
matsim.py
123
matsim.py
|
@ -1,6 +1,5 @@
|
||||||
import math
|
import math
|
||||||
import subprocess
|
import subprocess
|
||||||
import xmltodict
|
|
||||||
import gzip
|
import gzip
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
@ -9,19 +8,22 @@ from shapely.geometry import Point
|
||||||
import hub.helpers.constants as cte
|
import hub.helpers.constants as cte
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
|
CONFIG_DTD = "http://www.matsim.org/files/dtd/config_v2.dtd"
|
||||||
|
FACILITIES_DTD = "http://www.matsim.org/files/dtd/facilities_v1.dtd"
|
||||||
|
POPULATION_DTD = "http://www.matsim.org/files/dtd/population_v5.dtd"
|
||||||
|
|
||||||
|
|
||||||
# TODO: remove xmltodict completely and replace with lxml as it doesnt allow for repeated mixed ordered tags
|
# TODO: remove xmltodict completely and replace with lxml as it doesnt allow for repeated mixed ordered tags
|
||||||
class Matsim:
|
class Matsim:
|
||||||
def __init__(self, city, output_file_path):
|
def __init__(self, city, output_file_path):
|
||||||
self.city = city
|
self._city = city
|
||||||
self.output_file_path = output_file_path
|
self._output_file_path = output_file_path
|
||||||
|
|
||||||
self.facilities = {
|
self._facilities = {
|
||||||
'@name': self.city.name + ' Facilities',
|
'name': self._city.name + ' Facilities',
|
||||||
'facility': []
|
'facility': []
|
||||||
}
|
}
|
||||||
|
|
||||||
self.population = etree.Element("population")
|
|
||||||
|
|
||||||
def _export(self):
|
def _export(self):
|
||||||
self._export_facilities()
|
self._export_facilities()
|
||||||
self._export_network()
|
self._export_network()
|
||||||
|
@ -34,16 +36,18 @@ class Matsim:
|
||||||
'geometry': []
|
'geometry': []
|
||||||
}
|
}
|
||||||
|
|
||||||
for building in self.city.buildings:
|
facilities_xml = etree.Element('facilities', name=self._facilities['name'])
|
||||||
|
|
||||||
|
for building in self._city.buildings:
|
||||||
for surface in building.grounds:
|
for surface in building.grounds:
|
||||||
for coord in surface.solid_polygon.coordinates:
|
for coord in surface.solid_polygon.coordinates:
|
||||||
buildings_shape_data['id'].append(f"{building.name}")
|
buildings_shape_data['id'].append(f"{building.name}")
|
||||||
buildings_shape_data['geometry'].append(Point(coord[0], coord[1]))
|
buildings_shape_data['geometry'].append(Point(coord[0], coord[1]))
|
||||||
|
|
||||||
facility = {
|
facility = {
|
||||||
'@id': building.name,
|
'id': building.name,
|
||||||
'@x': str(building.centroid[0]),
|
'x': str(building.centroid[0]),
|
||||||
'@y': str(building.centroid[1]),
|
'y': str(building.centroid[1]),
|
||||||
'activity': []
|
'activity': []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,28 +63,48 @@ class Matsim:
|
||||||
building_schedules.append(schedule)
|
building_schedules.append(schedule)
|
||||||
|
|
||||||
activity_info = {
|
activity_info = {
|
||||||
'@type': building.function,
|
'type': building.function,
|
||||||
'capacity': {
|
'capacity': math.ceil(capacity),
|
||||||
'@value': math.ceil(capacity)
|
|
||||||
},
|
|
||||||
'opentime': _convert_schedules(building_schedules)
|
'opentime': _convert_schedules(building_schedules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
facility_xml = etree.SubElement(facilities_xml, 'facility', {
|
||||||
|
'id': facility['id'],
|
||||||
|
'x': facility['x'],
|
||||||
|
'y': facility['y'],
|
||||||
|
})
|
||||||
|
|
||||||
|
activity_xml = etree.SubElement(facility_xml, 'activity', {
|
||||||
|
'type': activity_info['type']
|
||||||
|
})
|
||||||
|
|
||||||
|
etree.SubElement(activity_xml, 'capacity', {
|
||||||
|
'value': activity_info['capacity']
|
||||||
|
})
|
||||||
|
|
||||||
|
etree.SubElement(activity_xml, 'opentime', {
|
||||||
|
'day': activity_info['opentime'][0]['day'],
|
||||||
|
'start_time': activity_info['opentime'][0]['start_time'],
|
||||||
|
'end_time': activity_info['opentime'][0]['end_time']
|
||||||
|
})
|
||||||
|
|
||||||
facility['activity'].append(activity_info)
|
facility['activity'].append(activity_info)
|
||||||
self.facilities['facility'].append(facility)
|
self._facilities['facility'].append(facility)
|
||||||
|
|
||||||
gdf = gpd.GeoDataFrame(
|
gdf = gpd.GeoDataFrame(
|
||||||
buildings_shape_data,
|
buildings_shape_data,
|
||||||
crs=self.city.srs_name
|
crs=self._city.srs_name
|
||||||
)
|
)
|
||||||
gdf.to_file("input_files/buildings_shapefile.shp")
|
gdf.to_file("input_files/buildings_shapefile.shp")
|
||||||
|
|
||||||
# Convert the Python dictionary to an XML string
|
# Convert the Python dictionary to an XML string
|
||||||
xml_content = xmltodict.unparse({'facilities': self.facilities}, pretty=True, short_empty_elements=True)
|
xml_content = etree.tostring(facilities_xml, pretty_print=True, encoding='UTF-8', xml_declaration=True).decode('utf-8')
|
||||||
|
|
||||||
# Write the XML to the file
|
# Write the XML to the file
|
||||||
output_file = f"{self.output_file_path}/{self.city.name}_facilities.xml"
|
output_file = f"{self._output_file_path}/{self._city.name}_facilities.xml"
|
||||||
with open(output_file, 'w') as file:
|
with open(output_file, 'w') as file:
|
||||||
|
file.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>")
|
||||||
|
file.write(f"<!DOCTYPE facilities SYSTEM \"{FACILITIES_DTD}\">")
|
||||||
file.write(xml_content)
|
file.write(xml_content)
|
||||||
|
|
||||||
with open(output_file, 'rb') as f_in:
|
with open(output_file, 'rb') as f_in:
|
||||||
|
@ -95,34 +119,35 @@ class Matsim:
|
||||||
"-jar", jar_path,
|
"-jar", jar_path,
|
||||||
"input_files/merged-network.osm.pbf",
|
"input_files/merged-network.osm.pbf",
|
||||||
"input_files/buildings_shapefile.shp",
|
"input_files/buildings_shapefile.shp",
|
||||||
f"{self.output_file_path}/{self.city.name}_network.xml.gz"
|
f"{self._output_file_path}/{self._city.name}_network.xml.gz"
|
||||||
]
|
]
|
||||||
subprocess.run(command)
|
subprocess.run(command)
|
||||||
|
|
||||||
def _export_population(self):
|
def _export_population(self):
|
||||||
|
population = etree.Element("population")
|
||||||
id = 0
|
id = 0
|
||||||
|
|
||||||
# Generate work facilities
|
# Generate work facilities
|
||||||
work = []
|
work = []
|
||||||
for facility in self.facilities['facility']:
|
for facility in self._facilities['facility']:
|
||||||
if facility['activity'][0]['@type'] != cte.RESIDENTIAL:
|
if facility['activity'][0]['type'] != cte.RESIDENTIAL:
|
||||||
work.append({
|
work.append({
|
||||||
'type': facility['activity'][0]['@type'],
|
'type': facility['activity'][0]['type'],
|
||||||
'capacity': int(facility['activity'][0]['capacity']['@value']),
|
'capacity': int(facility['activity'][0]['capacity']),
|
||||||
'facility': facility['@id'],
|
'facility': facility['id'],
|
||||||
'x': facility['@x'],
|
'x': facility['x'],
|
||||||
'y': facility['@y'],
|
'y': facility['y'],
|
||||||
'start_time': '08:00:00',
|
'start_time': '08:00:00',
|
||||||
'end_time': '18:00:00'
|
'end_time': '18:00:00'
|
||||||
})
|
})
|
||||||
|
|
||||||
# Generate the population from residential places first
|
# Generate the population from residential places first
|
||||||
current_work = 0
|
current_work = 0
|
||||||
for facility in self.facilities['facility']:
|
for facility in self._facilities['facility']:
|
||||||
if facility['activity'][0]['@type'] == cte.RESIDENTIAL:
|
if facility['activity'][0]['type'] == cte.RESIDENTIAL:
|
||||||
max_capacity = int(facility['activity'][0]['capacity']['@value'])
|
max_capacity = int(facility['activity'][0]['capacity']['value'])
|
||||||
for _ in range(max_capacity):
|
for _ in range(max_capacity):
|
||||||
person = etree.SubElement(self.population, 'person', {
|
person = etree.SubElement(population, 'person', {
|
||||||
'id': str(id),
|
'id': str(id),
|
||||||
'sex': 'm',
|
'sex': 'm',
|
||||||
'age': '32',
|
'age': '32',
|
||||||
|
@ -133,10 +158,10 @@ class Matsim:
|
||||||
|
|
||||||
# Residential activity
|
# Residential activity
|
||||||
etree.SubElement(plan, 'act', {
|
etree.SubElement(plan, 'act', {
|
||||||
'type': facility['activity'][0]['@type'],
|
'type': facility['activity'][0]['type'],
|
||||||
'facility': facility['@id'],
|
'facility': facility['id'],
|
||||||
'x': facility['@x'],
|
'x': facility['x'],
|
||||||
'y': facility['@y'],
|
'y': facility['y'],
|
||||||
'end_time': '7:30:00'
|
'end_time': '7:30:00'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -158,10 +183,10 @@ class Matsim:
|
||||||
|
|
||||||
# Residential activity (return)
|
# Residential activity (return)
|
||||||
etree.SubElement(plan, 'act', {
|
etree.SubElement(plan, 'act', {
|
||||||
'type': facility['activity'][0]['@type'],
|
'type': facility['activity'][0]['type'],
|
||||||
'facility': facility['@id'],
|
'facility': facility['id'],
|
||||||
'x': facility['@x'],
|
'x': facility['x'],
|
||||||
'y': facility['@y'],
|
'y': facility['y'],
|
||||||
})
|
})
|
||||||
|
|
||||||
work[current_work]['capacity'] -= 1
|
work[current_work]['capacity'] -= 1
|
||||||
|
@ -171,10 +196,10 @@ class Matsim:
|
||||||
id += 1
|
id += 1
|
||||||
|
|
||||||
# Convert the Python dictionary to an XML string
|
# Convert the Python dictionary to an XML string
|
||||||
xml_content = etree.tostring(self.population, pretty_print=True, encoding='UTF-8', xml_declaration=True).decode('utf-8')
|
xml_content = etree.tostring(population, pretty_print=True, encoding='UTF-8', xml_declaration=True).decode('utf-8')
|
||||||
|
|
||||||
# Write the XML to the file
|
# Write the XML to the file
|
||||||
output_file = f"{self.output_file_path}/{self.city.name}_population.xml"
|
output_file = f"{self._output_file_path}/{self._city.name}_population.xml"
|
||||||
with open(output_file, 'w') as file:
|
with open(output_file, 'w') as file:
|
||||||
file.write(xml_content)
|
file.write(xml_content)
|
||||||
|
|
||||||
|
@ -185,7 +210,7 @@ class Matsim:
|
||||||
def _export_config(self):
|
def _export_config(self):
|
||||||
parameterset = []
|
parameterset = []
|
||||||
|
|
||||||
for facility in self.facilities['facility']:
|
for facility in self._facilities['facility']:
|
||||||
if facility['activity'][0]['@type'] == cte.RESIDENTIAL:
|
if facility['activity'][0]['@type'] == cte.RESIDENTIAL:
|
||||||
parameterset.append({'@type':'activityParams', 'param': [
|
parameterset.append({'@type':'activityParams', 'param': [
|
||||||
{'@name':'activityType','@value':facility['activity'][0]['@type']},
|
{'@name':'activityType','@value':facility['activity'][0]['@type']},
|
||||||
|
@ -204,9 +229,9 @@ class Matsim:
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
'module': [
|
'module': [
|
||||||
{'@name': 'network', 'param': {'@name':'inputNetworkFile', '@value': f"{self.city.name}_network.xml.gz"}},
|
{'@name': 'network', 'param': {'@name':'inputNetworkFile', '@value': f"{self._city.name}_network.xml.gz"}},
|
||||||
{'@name': 'plans', 'param': {'@name':'inputPlansFile', '@value': f"{self.city.name}_population.xml.gz"}},
|
{'@name': 'plans', 'param': {'@name':'inputPlansFile', '@value': f"{self._city.name}_population.xml.gz"}},
|
||||||
{'@name': 'facilities', 'param': {'@name':'inputFacilitiesFile', '@value': f"{self.city.name}_facilities.xml.gz"}},
|
{'@name': 'facilities', 'param': {'@name':'inputFacilitiesFile', '@value': f"{self._city.name}_facilities.xml.gz"}},
|
||||||
{'@name': 'controler', 'param': [
|
{'@name': 'controler', 'param': [
|
||||||
{'@name': 'outputDirectory', '@value': '/output'},
|
{'@name': 'outputDirectory', '@value': '/output'},
|
||||||
{'@name': 'firstIteration', '@value': '0'},
|
{'@name': 'firstIteration', '@value': '0'},
|
||||||
|
@ -237,7 +262,7 @@ class Matsim:
|
||||||
|
|
||||||
xml_content = xmltodict.unparse({'config': config}, pretty=True, short_empty_elements=True)
|
xml_content = xmltodict.unparse({'config': config}, pretty=True, short_empty_elements=True)
|
||||||
|
|
||||||
with open(f"{self.output_file_path}/{self.city.name}_config.xml", 'w') as file:
|
with open(f"{self._output_file_path}/{self._city.name}_config.xml", 'w') as file:
|
||||||
file.write(xml_content)
|
file.write(xml_content)
|
||||||
|
|
||||||
def _convert_schedules(building_schedules):
|
def _convert_schedules(building_schedules):
|
||||||
|
@ -259,8 +284,8 @@ def _convert_schedules(building_schedules):
|
||||||
for day in schedule.day_types:
|
for day in schedule.day_types:
|
||||||
if day[0:3] != 'hol':
|
if day[0:3] != 'hol':
|
||||||
converted_schedules.append({
|
converted_schedules.append({
|
||||||
'@day': day[0:3],
|
'day': day[0:3],
|
||||||
'@start_time': opening_hour,
|
'start_time': opening_hour,
|
||||||
'@end_time': closing_hour
|
'end_time': closing_hour
|
||||||
})
|
})
|
||||||
return converted_schedules
|
return converted_schedules
|
||||||
|
|
|
@ -1 +1,4 @@
|
||||||
cerc-hub
|
cerc-hub
|
||||||
|
geopandas
|
||||||
|
shapely
|
||||||
|
lxml
|
||||||
|
|
Loading…
Reference in New Issue
Block a user