All `Person` attributes default to `java.lang.String` when saving to MATSim xml
KasiaKoz opened this issue · 4 comments
All attributes for a Person
are currently saved as java.lang.String
in the MATSim xml files:
https://github.com/arup-group/pam/blob/main/pam/write.py#L177
Sometimes MATSim requires different java types. Below is an error caused by running the multimodal contrib, which is expecting Person's age to be saved as java.lang.Integer
for example:
| java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
| at org.matsim.contrib.multimodal.router.util.WalkTravelTime.setPerson(WalkTravelTime.java:276) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.contrib.multimodal.router.util.WalkTravelTime.getLinkTravelTime(WalkTravelTime.java:139) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.AStarEuclidean.addToPendingNodes(AStarEuclidean.java:152) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.Dijkstra.relaxNodeLogic(Dijkstra.java:438) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.Dijkstra.relaxNode(Dijkstra.java:409) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.AStarLandmarks.relaxNode(AStarLandmarks.java:137) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.Dijkstra.searchLogic(Dijkstra.java:317) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.Dijkstra.calcLeastCostPath(Dijkstra.java:234) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.AStarLandmarks.calcLeastCostPath(AStarLandmarks.java:124) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.NetworkRoutingModule.calcRoute(NetworkRoutingModule.java:108) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.TripRouter.calcRoute(TripRouter.java:182) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.router.PlanRouter.run(PlanRouter.java:101) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.population.algorithms.PersonPrepareForSim.run(PersonPrepareForSim.java:219) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at org.matsim.core.population.algorithms.ParallelPersonAlgorithmUtils$PersonAlgoThread.run(ParallelPersonAlgorithmUtils.java:145) ~[columbus-2.1.0-jar-with-dependencies.jar:2.1.0]
| at java.lang.Thread.run(Thread.java:834) ~[?:?]
We had a go at 'guessing' java types from python types in genet: arup-group/genet#124, master/genet/utils/java_dtypes.py, which may be of use here.
Now also found on a recent project:
java.lang.ClassCastException: class java.lang.String cannot be cast to class org.matsim.vehicles.PersonVehicles (java.lang.String is in module java.base of loader 'bootstrap'; org.matsim.vehicles.PersonVehicles is in unnamed module of loader 'app')
person attributes after reading and writing out through pam have resulted in:
<attribute class="java.lang.String" name="vehicles">{"car":"person_vehicle_id"}</attribute>
instead of expected
<attribute name="vehicles" class="org.matsim.vehicles.PersonVehicles">{"car":"person_vehicle_id"}</attribute>
thanks Kasia, we now have a BIP (branch in progress); write-leg-attributes which includes a fix for this specific case in the write_matsim (v12) function:
attributes = et.SubElement(person_xml, 'attributes', {})
for k, v in person.attributes.items():
if k == "vehicles": # todo make something more robust for future 'special' classes
attribute = et.SubElement(
attributes, 'attribute', {'class': 'org.matsim.vehicles.PersonVehicles', 'name': str(k)}
)
else:
attribute = et.SubElement(
attributes, 'attribute', {'class': 'java.lang.String', 'name': str(k)}
)
attribute.text = str(v)
This seems to be a working fix but is obviously a bit trash to maintain and will require us to treat the "vehicles" attribute as special/protected forever after.
We also have a type issue in one of the new leg attributes:
for k, v in component.attributes.items():
if k == 'enterVehicleTime': # todo make something more robust for future 'special' classes
attribute = et.SubElement(
attributes, 'attribute', {'class': 'java.lang.Double', 'name': str(k)}
)
else:
attribute = et.SubElement(
attributes, 'attribute', {'class': 'java.lang.String', 'name': str(k)}
)
attribute.text = str(v)
This one would be fixed by your type mapping. But i will also look to roll out more broadly for "attributes".