From 6ecba6d5dde7e62af712798a11abdd50751ffea8 Mon Sep 17 00:00:00 2001 From: Ruben1729 Date: Thu, 8 Feb 2024 17:08:12 -0500 Subject: [PATCH] transfer xmltodict for facilities to lxml --- __pycache__/matsim.cpython-39.pyc | Bin 6509 -> 6570 bytes matsim.py | 123 ++++++++++++++++++------------ requirements.txt | 5 +- 3 files changed, 78 insertions(+), 50 deletions(-) diff --git a/__pycache__/matsim.cpython-39.pyc b/__pycache__/matsim.cpython-39.pyc index 69dfed82e9a0951a5747760ecd9790ccdce69a86..e527997d66d31738769fb7074ff02c8a726ff848 100644 GIT binary patch delta 1382 zcmZvcU1%d!6vyYzS3V}mr1?%iOuyTaZELNHQjt{Kx|KD^Zbgl*%ezAXE2A6#&S6c@quY3Y+7>We-JA}pc<`XHhpD2i`>EZ#G_rn;D6esgmE=bk&~ zoZS3$@3p147LP|6`uXMVkBwii#~oDm`KHz|{P31mGcD8Y_))uMw=K;zTTLb83u+CV z18S@7+HF_eGA%>3HTN7AXc}Kq$%kmF;PVhwP2L$qenx+w82ORiNTg_Zgx-(Q+ewlY zF3X<%lT^9Kx!Lo>^zap~8}$S|Qe=99 z7d=FX@tAr}kCF;s)TE1SSMsFZpdJIo9jXuO@jy=$8C|Ao%pIxU75GXf(v$QWxX5)O zX7mk4Pthk+7pawglkX0kdwkp6z8CiXxB&N72yG1 z;e4KaAQaIM`BJzq>ql?T#Wb9t;|jt8Nr)S0nmj8$f&}uqcmqX=6;dWJM>Amw(nWR~ zmfO@0}b*-|wv2qH}k&--(=E=G|ItHD?j{@LO;8nmH zV4Xrq&@sI#FG(+f#{g8wM{?olYxxE%L02&XRLT3Pi3sg59Ao@E`93v&qDlo{pkvn> zRjFkgO|!YZX;S$GDpaMK)pGvvoF(P-Onj626&CM6!VYoM3uyG{YI+=rw>_U`uI!J2 z7<52VYpv#%x!tc`k%Dq@145dBIY1Z?6j=sq1rU@A{{unIbS5#dF93qU9Dr2;JOxPf zar8s>DBF`7(YQD)iiyx9Rs_Dt~#}IhTgX5xj97^3rY^Z*3T7W zmEr3$u?zQ~ScVJ(_#yB-4JZTp^YA0vM$6Wky5``A$>)Xj92DyF7EC`lbUef_leuE~ EZ##QamH+?% delta 1370 zcmZvbO>7%Q6o7a3&+Fan+OZS=B#zT0q-7Cul7^qE3gAMLDuM=R0*KOL7SG0A%dwsD zx{9r}A{0)DszD=hpha>n2q7U6MdHMv;>3|kJ-|wwxFHpBfCH`y%-cW`p?bXEzIlH$ zZ{E%qv-eKxmaeND`&B>t*8X}=Z=mOTQod@f+rF}96`hLXwtTf-t2Zl_>(r`d$QO&C zi`JTMz3CR#oQhqjTkcg~45Cze8%?T2VJS(Nfj&U@kxu{QmyCWk1=S1eY4G$FVNl#b zS2$V{&I^xiWa*wTgFc}@3kzG{ZG7(WMlZ@$CVRU3+58cH)3wGobnLUbLBMdyUuqsPwNh09jxwfIfD&|aS)EM5xp*S`q+Hr zCWrfj7KKA7fEFy3Sgd5Y=+xGjl0J)J;eU|aXaQ#I5W9y(vmdU z*Awy%?(z9e_Rx=|r}Unjzh+gMwkfa%((k1-%Fth=CwQXKS$PRf(2o2pQt3DHU8K>E zLgs)U-dwM^HS82!Kh&(&Ez+>*PobR9PjqT17tkoZtW43H%0*<*UzJBvy!tmU$oFN% zKrvoKJd*#CMO8@9uc8^c7~Ye|SYyc~eGndF3yrEtr0+kimWAaE=(B)Pzz84>7y=A4 z^pxo{XQ!vxzo*RnE5WDcw(9mlkj%V%wCST3{X=hUU4cl@k+%T_fW_bo6}#k%>osiq zLeU~5a6IyP=RZeNJ}~_+$B|ups6EBvSKAW3XPlXzWO6%FIJz7;cA(@WtZ)kOD8OVe z2Z+XAY(uW?YZn(*mgX0hSC`JcL~_)LoJ3P}Cvsv8t|5WDLFNGqfJMN02Gd|oxf7X{ z-vv#HMxrUSvOf|1f!Bi5MWD`7IX)30638R;bUcT$)QYF#Z!yIstyrlwoN8&+S+~h$ z`eFQ({u2PXh9F{f}@*;a(inEPgvx#ftLZf)q#?6Y|AQxygWhQ)qB|4Tr0xQkY zFH=uGJ_i~4$yw064hXh?*akmbvTJp#ime8Dh8pR`1k~+|6==Eu{vu$Gmecuv077I$ AO8@`> diff --git a/matsim.py b/matsim.py index acf3fc4..df3de96 100644 --- a/matsim.py +++ b/matsim.py @@ -1,6 +1,5 @@ import math import subprocess -import xmltodict import gzip import shutil @@ -9,19 +8,22 @@ from shapely.geometry import Point import hub.helpers.constants as cte 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 class Matsim: def __init__(self, city, output_file_path): - self.city = city - self.output_file_path = output_file_path + self._city = city + self._output_file_path = output_file_path - self.facilities = { - '@name': self.city.name + ' Facilities', + self._facilities = { + 'name': self._city.name + ' Facilities', 'facility': [] } - self.population = etree.Element("population") - def _export(self): self._export_facilities() self._export_network() @@ -34,16 +36,18 @@ class Matsim: '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 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]), + 'id': building.name, + 'x': str(building.centroid[0]), + 'y': str(building.centroid[1]), 'activity': [] } @@ -59,28 +63,48 @@ class Matsim: building_schedules.append(schedule) activity_info = { - '@type': building.function, - 'capacity': { - '@value': math.ceil(capacity) - }, + 'type': building.function, + 'capacity': math.ceil(capacity), '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) - self.facilities['facility'].append(facility) + self._facilities['facility'].append(facility) gdf = gpd.GeoDataFrame( buildings_shape_data, - crs=self.city.srs_name + crs=self._city.srs_name ) gdf.to_file("input_files/buildings_shapefile.shp") # 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 - 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: + file.write("") + file.write(f"") file.write(xml_content) with open(output_file, 'rb') as f_in: @@ -95,34 +119,35 @@ class Matsim: "-jar", jar_path, "input_files/merged-network.osm.pbf", "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) def _export_population(self): + population = etree.Element("population") id = 0 # Generate work facilities work = [] - for facility in self.facilities['facility']: - if facility['activity'][0]['@type'] != cte.RESIDENTIAL: + for facility in self._facilities['facility']: + if facility['activity'][0]['type'] != cte.RESIDENTIAL: work.append({ - 'type': facility['activity'][0]['@type'], - 'capacity': int(facility['activity'][0]['capacity']['@value']), - 'facility': facility['@id'], - 'x': facility['@x'], - 'y': facility['@y'], + 'type': facility['activity'][0]['type'], + 'capacity': int(facility['activity'][0]['capacity']), + 'facility': facility['id'], + 'x': facility['x'], + 'y': facility['y'], 'start_time': '08:00:00', 'end_time': '18:00:00' }) # Generate the population from residential places first current_work = 0 - for facility in self.facilities['facility']: - if facility['activity'][0]['@type'] == cte.RESIDENTIAL: - max_capacity = int(facility['activity'][0]['capacity']['@value']) + for facility in self._facilities['facility']: + if facility['activity'][0]['type'] == cte.RESIDENTIAL: + max_capacity = int(facility['activity'][0]['capacity']['value']) for _ in range(max_capacity): - person = etree.SubElement(self.population, 'person', { + person = etree.SubElement(population, 'person', { 'id': str(id), 'sex': 'm', 'age': '32', @@ -133,10 +158,10 @@ class Matsim: # Residential activity etree.SubElement(plan, 'act', { - 'type': facility['activity'][0]['@type'], - 'facility': facility['@id'], - 'x': facility['@x'], - 'y': facility['@y'], + 'type': facility['activity'][0]['type'], + 'facility': facility['id'], + 'x': facility['x'], + 'y': facility['y'], 'end_time': '7:30:00' }) @@ -158,10 +183,10 @@ class Matsim: # Residential activity (return) etree.SubElement(plan, 'act', { - 'type': facility['activity'][0]['@type'], - 'facility': facility['@id'], - 'x': facility['@x'], - 'y': facility['@y'], + 'type': facility['activity'][0]['type'], + 'facility': facility['id'], + 'x': facility['x'], + 'y': facility['y'], }) work[current_work]['capacity'] -= 1 @@ -171,10 +196,10 @@ class Matsim: id += 1 # 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 - 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: file.write(xml_content) @@ -185,7 +210,7 @@ class Matsim: def _export_config(self): parameterset = [] - for facility in self.facilities['facility']: + for facility in self._facilities['facility']: if facility['activity'][0]['@type'] == cte.RESIDENTIAL: parameterset.append({'@type':'activityParams', 'param': [ {'@name':'activityType','@value':facility['activity'][0]['@type']}, @@ -204,9 +229,9 @@ class Matsim: config = { 'module': [ - {'@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': 'facilities', 'param': {'@name':'inputFacilitiesFile', '@value': f"{self.city.name}_facilities.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': 'facilities', 'param': {'@name':'inputFacilitiesFile', '@value': f"{self._city.name}_facilities.xml.gz"}}, {'@name': 'controler', 'param': [ {'@name': 'outputDirectory', '@value': '/output'}, {'@name': 'firstIteration', '@value': '0'}, @@ -237,7 +262,7 @@ class Matsim: 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) def _convert_schedules(building_schedules): @@ -259,8 +284,8 @@ def _convert_schedules(building_schedules): for day in schedule.day_types: if day[0:3] != 'hol': converted_schedules.append({ - '@day': day[0:3], - '@start_time': opening_hour, - '@end_time': closing_hour + 'day': day[0:3], + 'start_time': opening_hour, + 'end_time': closing_hour }) return converted_schedules diff --git a/requirements.txt b/requirements.txt index c64163c..90cdfe7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,4 @@ -cerc-hub \ No newline at end of file +cerc-hub +geopandas +shapely +lxml