From 3b1b93867f232e9286d88b622fe06692e35f47bb Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 16 Oct 2023 16:32:14 +0200 Subject: [PATCH] Person vehicle types attribute (#2877) * added todos * separate container class for vehicle types * add vehicle type logic and test for it * add a todo * put vehicle types into person attributes from commercial traffic * changes from ricardo --- .../SmallScaleCommercialTrafficUtils.java | 17 +++--- .../core/controler/PrepareForSimImpl.java | 45 +++++++++++----- .../ObjectAttributesConverter.java | 2 + .../PersonVehicleTypesAttributeConverter.java | 42 +++++++++++++++ .../PersonVehiclesAttributeConverter.java | 7 ++- .../matsim/vehicles/PersonVehicleTypes.java | 33 ++++++++++++ .../org/matsim/vehicles/VehicleUtils.java | 35 +++++++++++-- .../core/controler/PrepareForSimImplTest.java | 52 +++++++++++++++++-- 8 files changed, 203 insertions(+), 30 deletions(-) create mode 100644 matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehicleTypesAttributeConverter.java create mode 100644 matsim/src/main/java/org/matsim/vehicles/PersonVehicleTypes.java diff --git a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java index 0387c0d7873..3d8563424a1 100644 --- a/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java +++ b/contribs/application/src/main/java/org/matsim/smallScaleCommercialTrafficGeneration/SmallScaleCommercialTrafficUtils.java @@ -50,8 +50,10 @@ import org.matsim.core.network.NetworkUtils; import org.matsim.core.population.PopulationUtils; import org.matsim.core.utils.io.IOUtils; +import org.matsim.vehicles.Vehicle; import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; +import org.matsim.vehicles.Vehicles; import java.io.BufferedWriter; import java.io.IOException; @@ -169,7 +171,7 @@ private static void writeCSVWithCategoryHeader(HashMap idCounter = new HashMap<>(); Population populationFromCarrier = (Population) scenario.getScenarioElement("allpersons"); + Vehicles allVehicles = VehicleUtils.getOrCreateAllvehicles(scenario); + for (Person person : populationFromCarrier.getPersons().values()) { Plan plan = popFactory.createPlan(); @@ -238,11 +242,12 @@ else if (subpopulation.contains("goodsTraffic")) if (relatedCarrier.getAttributes().getAsMap().containsKey("tourStartArea")) newPerson.getAttributes().putAttribute("tourStartArea", relatedCarrier.getAttributes().getAttribute("tourStartArea")); - VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, (new HashMap<>() { - { - put(mode, (Id.createVehicleId(person.getId().toString()))); - } - })); + + Id vehicleId = Id.createVehicleId(person.getId().toString()); + + VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, Map.of(mode, vehicleId)); + VehicleUtils.insertVehicleTypesIntoAttributes(newPerson, Map.of(mode, allVehicles.getVehicles().get(vehicleId).getType().getId())); + population.addPerson(newPerson); } diff --git a/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java b/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java index 628a67c532a..3278f06e295 100644 --- a/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java +++ b/matsim/src/main/java/org/matsim/core/controler/PrepareForSimImpl.java @@ -19,9 +19,11 @@ * * * *********************************************************************** */ - package org.matsim.core.controler; +package org.matsim.core.controler; +import jakarta.inject.Inject; +import jakarta.inject.Provider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.matsim.api.core.v01.Id; @@ -49,7 +51,6 @@ import org.matsim.core.router.TripStructureUtils; import org.matsim.core.router.TripStructureUtils.Trip; import org.matsim.core.scenario.Lockable; -import org.matsim.core.utils.collections.Tuple; import org.matsim.core.utils.timing.TimeInterpretation; import org.matsim.facilities.ActivityFacilities; import org.matsim.facilities.FacilitiesFromPopulation; @@ -57,12 +58,8 @@ import org.matsim.vehicles.VehicleType; import org.matsim.vehicles.VehicleUtils; -import jakarta.inject.Inject; -import jakarta.inject.Provider; - import javax.annotation.Nullable; import java.util.*; -import java.util.stream.Collectors; import static org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource.modeVehicleTypesFromVehiclesData; @@ -230,13 +227,35 @@ private void createAndAddVehiclesForEveryNetworkMode() { for (Person person : scenario.getPopulation().getPersons().values()) { - var modeToVehicle = modeVehicleTypes.entrySet().stream() - // map mode type to vehicle id - .map(modeType -> Tuple.of(modeType, createVehicleId(person, modeType.getKey()))) - // create a corresponding vehicle - .peek(tuple -> createAndAddVehicleIfNecessary(tuple.getSecond(), tuple.getFirst().getValue())) - // write mode-string to vehicle-id into a map - .collect(Collectors.toMap(tuple -> tuple.getFirst().getKey(), Tuple::getSecond)); + + Map> modeToVehicle = new HashMap<>(); + + // optional attribute, that can be null + Map> modeTypes = VehicleUtils.getVehicleTypes(person); + + for (Map.Entry modeType : modeVehicleTypes.entrySet()) { + + String mode = modeType.getKey(); + + Id vehicleId = createVehicleId(person, mode); + + // get the type + VehicleType type = modeType.getValue(); + + // Use the person attribute to map to a more specific vehicle type + if (modeTypes != null && modeTypes.containsKey(mode)) { + Id typeId = modeTypes.get(mode); + type = scenario.getVehicles().getVehicleTypes().get(typeId); + if (type == null) { + throw new IllegalStateException("Vehicle type " + typeId + " specified for person " + person.getId() + ", but not found in scenario."); + } + } + + createAndAddVehicleIfNecessary(vehicleId, type); + + // write mode-string to vehicle-id into a map + modeToVehicle.put(mode, vehicleId); + } VehicleUtils.insertVehicleIdsIntoAttributes(person, modeToVehicle); } diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java index ee7eed06f03..6c0e0afbfe3 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/ObjectAttributesConverter.java @@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger; import org.matsim.utils.objectattributes.attributeconverters.*; import org.matsim.api.core.v01.Coord; +import org.matsim.vehicles.PersonVehicleTypes; import org.matsim.vehicles.PersonVehicles; import java.util.*; @@ -60,6 +61,7 @@ public ObjectAttributesConverter() { this.converters.put(Coord.class.getName(), new CoordConverter()); this.converters.put(Coord[].class.getName(), new CoordArrayConverter()); this.converters.put(PersonVehicles.class.getName(), new PersonVehiclesAttributeConverter()); + this.converters.put(PersonVehicleTypes.class.getName(), new PersonVehicleTypesAttributeConverter()); } //this is for reading diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehicleTypesAttributeConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehicleTypesAttributeConverter.java new file mode 100644 index 00000000000..9c01e8ab9c6 --- /dev/null +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehicleTypesAttributeConverter.java @@ -0,0 +1,42 @@ +package org.matsim.utils.objectattributes.attributeconverters; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; +import org.matsim.utils.objectattributes.AttributeConverter; +import org.matsim.vehicles.PersonVehicleTypes; +import org.matsim.vehicles.VehicleType; + +import java.util.HashMap; +import java.util.Map; + +/** + * Converter to store vehicle types as person attribute. + */ +public class PersonVehicleTypesAttributeConverter implements AttributeConverter { + + private final Logger logger = LogManager.getLogger(PersonVehicleTypesAttributeConverter.class); + + @Override + public PersonVehicleTypes convert(String value) { + PersonVehicleTypes vehicles = new PersonVehicleTypes(); + Map stringMap = new StringStringMapConverter().convert(value); + for (Map.Entry entry: stringMap.entrySet()) { + vehicles.addModeVehicleType(entry.getKey(), Id.create(entry.getValue(), VehicleType.class)); + } + return vehicles; + } + + @Override + public String convertToString(Object o) { + if(!(o instanceof PersonVehicleTypes vehicles)){ + logger.error("Object is not of type PersonVehicles: " + o.getClass()); + return null; + } + Map stringMap = new HashMap<>(); + for (Map.Entry> entry: vehicles.getModeVehicleTypes().entrySet()) { + stringMap.put(entry.getKey(), entry.getValue().toString()); + } + return new StringStringMapConverter().convertToString(stringMap); + } +} diff --git a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java index dd5440df404..96484c502a2 100644 --- a/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java +++ b/matsim/src/main/java/org/matsim/utils/objectattributes/attributeconverters/PersonVehiclesAttributeConverter.java @@ -26,12 +26,11 @@ public PersonVehicles convert(String value) { @Override public String convertToString(Object o) { - if(!(o instanceof PersonVehicles)){ - logger.error("Object is not of type PersonVehicles: " + o.getClass().toString()); + if(!(o instanceof PersonVehicles vehicles)){ + logger.error("Object is not of type PersonVehicles: " + o.getClass()); return null; } - PersonVehicles vehicles = (PersonVehicles)o; - Map stringMap = new HashMap<>(); + Map stringMap = new HashMap<>(); for (Map.Entry> entry: vehicles.getModeVehicles().entrySet()) { stringMap.put(entry.getKey(), entry.getValue().toString()); } diff --git a/matsim/src/main/java/org/matsim/vehicles/PersonVehicleTypes.java b/matsim/src/main/java/org/matsim/vehicles/PersonVehicleTypes.java new file mode 100644 index 00000000000..e07198587bb --- /dev/null +++ b/matsim/src/main/java/org/matsim/vehicles/PersonVehicleTypes.java @@ -0,0 +1,33 @@ +package org.matsim.vehicles; + +import org.matsim.api.core.v01.Id; + +import java.util.HashMap; +import java.util.Map; + +/** + * Container class to store mode specific person vehicle types. + */ +public final class PersonVehicleTypes { + + private final Map> modeVehicleTypes = new HashMap<>(); + + public PersonVehicleTypes() { + } + + public void addModeVehicleType(String mode, Id vehicleType) { + modeVehicleTypes.put(mode, vehicleType); + } + + public Id getVehicleType(String mode) { + return modeVehicleTypes.get(mode); + } + + public Map> getModeVehicleTypes() { + return modeVehicleTypes; + } + + public void putModeVehicleTypes(Map> vehicleTypes) { + modeVehicleTypes.putAll(vehicleTypes); + } +} diff --git a/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java b/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java index e3594e65885..0141e814600 100644 --- a/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java +++ b/matsim/src/main/java/org/matsim/vehicles/VehicleUtils.java @@ -41,6 +41,7 @@ public final class VehicleUtils { private static final VehicleType DEFAULT_VEHICLE_TYPE = VehicleUtils.getFactory().createVehicleType(Id.create("defaultVehicleType", VehicleType.class)); private static final String VEHICLE_ATTRIBUTE_KEY = "vehicles"; + private static final String VEHICLE_TYPES_ATTRIBUTE_KEY = "vehicleTypes"; // should remain under the hood --> should remain private private static final String DOOR_OPERATION_MODE = "doorOperationMode" ; @@ -117,11 +118,11 @@ public static void copyFromTo( VehicleType in, VehicleType out ) { /** * Checks whether a person has a vehicle id for mode - without throwing an * exception if not. - * + * * @param person the person one wants to check for a vehicle id * @param mode the mode for the vehicle id to check * @return whether person has a vehicle id for that mode - * + * * @see {@link VehicleUtils#getVehicleId(Person, String)} */ public static boolean hasVehicleId(Person person, String mode) { @@ -147,6 +148,15 @@ public static Map> getVehicleIds(Person person) { return personVehicles.getModeVehicles(); } + /** + * Retrieve the optional vehicle types per mode that might be assigned to a person. + * Returns null if this is not defined. + */ + public static Map> getVehicleTypes(Person person) { + var personVehicles = (PersonVehicleTypes) person.getAttributes().getAttribute(VehicleUtils.VEHICLE_TYPES_ATTRIBUTE_KEY); + return personVehicles != null ? personVehicles.getModeVehicleTypes() : null; + } + /** * Retrieves a vehicleId from the person's attributes. * @@ -179,13 +189,32 @@ public static void insertVehicleIdsIntoAttributes(Person person, Map> modeToVehicleCopy = new HashMap<>(modeToVehicle); PersonVehicles personVehicles; if (attr == null) { - personVehicles = new PersonVehicles(modeToVehicleCopy); + personVehicles = new PersonVehicles(); } else { personVehicles = (PersonVehicles) attr; } personVehicles.addModeVehicleList(modeToVehicleCopy); person.getAttributes().putAttribute(VEHICLE_ATTRIBUTE_KEY, personVehicles); } + + /** + * Attaches vehicle types to a person, so that the router knows which vehicle to use for which mode and person. + * @param modeToVehicleType mode string mapped to vehicle type ids. The provided map is copied and stored as unmodifiable map. + */ + public static void insertVehicleTypesIntoAttributes(Person person, Map> modeToVehicleType) { + Object attr = person.getAttributes().getAttribute(VEHICLE_TYPES_ATTRIBUTE_KEY); + + Map> modeToTypesCopy = new HashMap<>(modeToVehicleType); + PersonVehicleTypes personVehiclesTypes; + if (attr == null) { + personVehiclesTypes = new PersonVehicleTypes(); + } else { + personVehiclesTypes = (PersonVehicleTypes) attr; + } + personVehiclesTypes.putModeVehicleTypes(modeToTypesCopy); + person.getAttributes().putAttribute(VEHICLE_TYPES_ATTRIBUTE_KEY, personVehiclesTypes); + } + //******** general VehicleType attributes ************ public static VehicleType.DoorOperationMode getDoorOperationMode( VehicleType vehicleType ){ diff --git a/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java b/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java index b01bd15d4b7..d711ebceaaa 100644 --- a/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java +++ b/matsim/src/test/java/org/matsim/core/controler/PrepareForSimImplTest.java @@ -19,10 +19,7 @@ package org.matsim.core.controler; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import org.junit.Assert; import org.junit.Test; @@ -55,6 +52,11 @@ import org.matsim.core.utils.timing.TimeInterpretation; import com.google.inject.Provider; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; + +import static org.assertj.core.api.Assertions.assertThat; /** * Mostly tests adaptation of old plans to routing mode and the related replacement of helper modes for access and egress @@ -760,6 +762,48 @@ public void testOutdatedFallbackAndHelperModesReplacement() { } } + @Test + public void vehicleTypes() { + + Config config = ConfigUtils.createConfig(); + config.controller().setOverwriteFileSetting(OverwriteFileSetting.deleteDirectoryIfExists); + Scenario scenario = ScenarioUtils.createScenario(config); + createAndAddNetwork(scenario); + Population pop = scenario.getPopulation(); + PopulationFactory f = pop.getFactory(); + + // add truck type + VehicleType truckType = scenario.getVehicles().getFactory().createVehicleType(Id.create("truck", VehicleType.class)); + scenario.getVehicles().addVehicleType(truckType); + + // Create test person + Person p1 = f.createPerson(Id.createPersonId("1")); + { + VehicleUtils.insertVehicleTypesIntoAttributes(p1, Map.of(TransportMode.car, Id.create("truck", VehicleType.class))); + + Plan plan = f.createPlan(); + Activity act = f.createActivityFromCoord("home", new Coord(0, 0)); + act.setEndTime(3600); + plan.addActivity(act); + plan.addLeg(f.createLeg(TransportMode.car)); + plan.addActivity(f.createActivityFromCoord("work", new Coord(1000, 0))); + p1.addPlan(plan); + pop.addPerson(p1); + } + + // run prepare + final PrepareForSimImpl prepareForSimImpl = new PrepareForSimImpl(config.global(), scenario, scenario.getNetwork(), + pop, scenario.getActivityFacilities(), new DummyTripRouterProvider(), config.qsim(), config.facilities(), + config.plans(), new MainModeIdentifierImpl(), TimeInterpretation.create(config)); + + prepareForSimImpl.run(); + + Id id = VehicleUtils.getVehicleId(p1, TransportMode.car); + assertThat(scenario.getVehicles().getVehicles().get(id).getType()) + .isEqualTo(truckType); + + } + private class DummyTripRouterProvider implements Provider { @Override public TripRouter get() {