From 2bb9b2cb8a1615b5c375612b1bae2618cba19a6e Mon Sep 17 00:00:00 2001 From: Coldeign Date: Thu, 15 Feb 2024 10:15:08 +0100 Subject: [PATCH 1/2] Modified ParkingCostHandler for better readability, ~not finished yet Implementing TestParkingCostHandler, ~not finished yet --- .../ParkingCostHandler.java | 244 +++++++++--------- .../ParkingCostHandlerCopy.java | 213 +++++++++++++++ .../TestParkingCostHandler.java | 19 ++ 3 files changed, 358 insertions(+), 118 deletions(-) create mode 100644 contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerCopy.java create mode 100644 contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java diff --git a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java index a1d21a71b4b..d23837f2b20 100644 --- a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java +++ b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java @@ -19,12 +19,8 @@ package playground.vsp.simpleParkingCostHandler; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; -import org.apache.commons.lang.StringUtils; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; import org.matsim.api.core.v01.events.ActivityEndEvent; @@ -42,18 +38,17 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.core.controler.events.AfterMobsimEvent; -import org.matsim.core.controler.listener.AfterMobsimListener; import org.matsim.core.router.StageActivityTypeIdentifier; import com.google.inject.Inject; +import org.matsim.utils.objectattributes.attributable.Attributes; /** * @author ikaddoura */ final class ParkingCostHandler implements TransitDriverStartsEventHandler, ActivityEndEventHandler, PersonDepartureEventHandler, PersonLeavesVehicleEventHandler, PersonEntersVehicleEventHandler { - + private final Map, Double> personId2lastLeaveVehicleTime = new HashMap<>(); private final Map, String> personId2previousActivity = new HashMap<>(); private final Map, Id> personId2relevantModeLinkId = new HashMap<>(); @@ -61,13 +56,13 @@ final class ParkingCostHandler implements TransitDriverStartsEventHandler, Activ private final Set> ptDrivers = new HashSet<>(); private final Set> hasAlreadyPaidDailyResidentialParkingCosts = new HashSet<>(); private double compensationTime = Double.NaN; - + @Inject private ParkingCostConfigGroup parkingCostConfigGroup; - + @Inject private EventsManager events; - + @Inject private Scenario scenario; @@ -92,135 +87,148 @@ public void handleEvent(TransitDriverStartsEvent event) { @Override - public void handleEvent(ActivityEndEvent event) { + public void handleEvent(ActivityEndEvent event) { if (ptDrivers.contains(event.getPersonId())) { - // skip pt drivers - } else { - if (!(StageActivityTypeIdentifier.isStageActivity(event.getActType()))) { - - personId2previousActivity.put(event.getPersonId(), event.getActType()); - - if (personId2relevantModeLinkId.get(event.getPersonId()) != null) { - personId2relevantModeLinkId.remove(event.getPersonId()); - } - } - } + return; + } + if ((StageActivityTypeIdentifier.isStageActivity(event.getActType()))) { + return; + } + + personId2previousActivity.put(event.getPersonId(), event.getActType()); + personId2relevantModeLinkId.remove(event.getPersonId()); } @Override public void handleEvent(PersonDepartureEvent event) { - if (! ptDrivers.contains(event.getPersonId())) { - // There might be several departures during a single trip. - if (event.getLegMode().equals(parkingCostConfigGroup.getMode())) { - personId2relevantModeLinkId.put(event.getPersonId(), event.getLinkId()); - } + if (ptDrivers.contains(event.getPersonId())) { + return; + } + // There might be several departures during a single trip. + if (!event.getLegMode().equals(parkingCostConfigGroup.getMode())) { + return; } + personId2relevantModeLinkId.put(event.getPersonId(), event.getLinkId()); } @Override public void handleEvent(PersonEntersVehicleEvent event) { - if (! ptDrivers.contains(event.getPersonId())) { - if (personId2relevantModeLinkId.get(event.getPersonId()) != null) { - - Link link = scenario.getNetwork().getLinks().get(personId2relevantModeLinkId.get(event.getPersonId())); - - if (parkingCostConfigGroup.getActivityPrefixesToBeExcludedFromParkingCost().stream() - .noneMatch(s -> personId2previousActivity.get(event.getPersonId()).startsWith(s))){ - - if (personId2previousActivity.get(event.getPersonId()).startsWith(parkingCostConfigGroup.getActivityPrefixForDailyParkingCosts())) { - // daily residential parking costs - - if (! hasAlreadyPaidDailyResidentialParkingCosts.contains(event.getPersonId())){ - hasAlreadyPaidDailyResidentialParkingCosts.add(event.getPersonId()); - - double residentialParkingFeePerDay = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()) != null) { - residentialParkingFeePerDay = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()); - } - - if (residentialParkingFeePerDay > 0.) { - double amount = -1. * residentialParkingFeePerDay; - events.processEvent(new PersonMoneyEvent(event.getTime(), event.getPersonId(), amount, "residential parking", "city", "link " + link.getId().toString())); - } - } - - } else { - // other parking cost types - - double parkingStartTime = 0.; - if (personId2lastLeaveVehicleTime.get(event.getPersonId()) != null) { - parkingStartTime = personId2lastLeaveVehicleTime.get(event.getPersonId()); - } - int parkingDurationHrs = (int) Math.ceil((event.getTime() - parkingStartTime) / 3600.); - - double extraHourParkingCosts = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()) != null) { - extraHourParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()); - } - - double firstHourParkingCosts = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()) != null) { - firstHourParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()); - } - - double dailyParkingCosts = firstHourParkingCosts + 29 * extraHourParkingCosts; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName()) != null) { - dailyParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName()); - } - - double maxDailyParkingCosts = dailyParkingCosts; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName()) != null) { - maxDailyParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName()); - } - - double maxParkingDurationHrs = 30; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName()) != null) { - maxParkingDurationHrs = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName()); - } - - double parkingPenalty = 0.; - if (link.getAttributes().getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName()) != null) { - parkingPenalty = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName()); - } - - double costs = 0.; - if (parkingDurationHrs > 0) { - costs += firstHourParkingCosts; - costs += (parkingDurationHrs - 1) * extraHourParkingCosts; - } - if (costs > dailyParkingCosts) { - costs = dailyParkingCosts; - } - if (costs > maxDailyParkingCosts) { - costs = maxDailyParkingCosts; - } - if ((parkingDurationHrs > maxParkingDurationHrs) & (costs < parkingPenalty)) { - costs = parkingPenalty; - } - - if (costs > 0.) { - double amount = -1. * costs; - events.processEvent(new PersonMoneyEvent(event.getTime(), event.getPersonId(), amount, "non-residential parking", "city", "link " + link.getId().toString())); - } - - } - - } + // Preliminaries + if (ptDrivers.contains(event.getPersonId())) { + // drives public transport -> doesn't pay for parking + return; + } + if (personId2relevantModeLinkId.get(event.getPersonId()) == null) { + // + return; + } + + if (parkingCostConfigGroup.getActivityPrefixesToBeExcludedFromParkingCost().stream() + .anyMatch(s -> personId2previousActivity.get(event.getPersonId()).startsWith(s))) { + return; + } + + if (hasAlreadyPaidDailyResidentialParkingCosts.contains(event.getPersonId())){ + // has already paid daily residential parking costs + return; + } + Link link = scenario.getNetwork().getLinks().get(personId2relevantModeLinkId.get(event.getPersonId())); + Attributes attributes = link.getAttributes(); + + if (personId2previousActivity.get(event.getPersonId()).startsWith(parkingCostConfigGroup.getActivityPrefixForDailyParkingCosts())) { + // daily residential parking costs + hasAlreadyPaidDailyResidentialParkingCosts.add(event.getPersonId()); + + double residentialParkingFeePerDay = (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName())).orElse(0.); + + if (residentialParkingFeePerDay > 0.) { + double amount = -1. * residentialParkingFeePerDay; + events.processEvent(createPersonMoneyEvent(event, amount, link, true)); } + return; } - } + // other parking cost types + + double costs = 0.; + + int parkingDurationHrs = (int) Math.ceil((event.getTime() - calcParkingStartTime(event)) / 3600.); + double firstHourParkingCosts = calcFirstHourParkingCosts(attributes); + double extraHourParkingCosts = calcExtraHourParkingCosts(attributes); + if (parkingDurationHrs > 0) { + costs += firstHourParkingCosts; + costs += (parkingDurationHrs - 1) * extraHourParkingCosts; + } + + double dailyParkingCosts = calcDailyParkingCosts(attributes, firstHourParkingCosts, extraHourParkingCosts); + + if (costs > dailyParkingCosts) { + costs = dailyParkingCosts; + } + + double maxDailyParkingCosts = calcMaxDailyParkingCosts(attributes, dailyParkingCosts); + + if (costs > maxDailyParkingCosts) { + costs = maxDailyParkingCosts; + } + + double maxParkingDurationHrs = calcMaxParkingDurationHrs(attributes); + double parkingPenalty = calcParkingPenalty(attributes); + + if ((parkingDurationHrs > maxParkingDurationHrs) & (costs < parkingPenalty)) { + costs = parkingPenalty; + } + + if (costs <= 0.) { + return; + } + double amount = -1. * costs; + events.processEvent(createPersonMoneyEvent(event, amount, link, false)); + } @Override public void handleEvent(PersonLeavesVehicleEvent event) { - if (! ptDrivers.contains(event.getPersonId())) { - personId2lastLeaveVehicleTime.put(event.getPersonId(), event.getTime()); + if (ptDrivers.contains(event.getPersonId())) { + return; } + personId2lastLeaveVehicleTime.put(event.getPersonId(), event.getTime()); } + + // private methods + private double calcParkingStartTime(PersonEntersVehicleEvent event) { + return Optional.ofNullable(personId2lastLeaveVehicleTime.get(event.getPersonId())).orElse(0.); + } + private double calcExtraHourParkingCosts(Attributes attributes) { + return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName())).orElse(0.); + } + private double calcFirstHourParkingCosts(Attributes attributes) { + return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName())).orElse(0.); + } + private double calcDailyParkingCosts(Attributes attributes, double firstHourParkingCosts, double extraHourParkingCosts) { + return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName())).orElse(firstHourParkingCosts + 29 * extraHourParkingCosts); + } + private double calcMaxDailyParkingCosts(Attributes attributes, double dailyParkingCosts) { + return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName())).orElse(dailyParkingCosts); + } + private double calcMaxParkingDurationHrs(Attributes attributes) { + return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName())).orElse(30.); + } + private double calcParkingPenalty(Attributes attributes) { + return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName())).orElse(0.); + } + private PersonMoneyEvent createPersonMoneyEvent(PersonEntersVehicleEvent event, double amount, Link link, boolean isResidentialParking) { + String purpose; + if (isResidentialParking) { + purpose = "residential parking"; + } else { + purpose = "non-residential parking"; + } + return new PersonMoneyEvent(event.getTime(), event.getPersonId(), amount, purpose, "city", "link " + link.getId()); + } } diff --git a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerCopy.java b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerCopy.java new file mode 100644 index 00000000000..b5e2ddc8731 --- /dev/null +++ b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerCopy.java @@ -0,0 +1,213 @@ +/* *********************************************************************** * + * project: org.matsim.* + * * + * *********************************************************************** * + * * + * copyright : (C) 2017 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package playground.vsp.simpleParkingCostHandler; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.events.*; +import org.matsim.api.core.v01.events.handler.*; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.population.Person; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.groups.QSimConfigGroup; +import org.matsim.core.router.StageActivityTypeIdentifier; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** +* @author ikaddoura +*/ + +final class ParkingCostHandlerCopy implements TransitDriverStartsEventHandler, ActivityEndEventHandler, PersonDepartureEventHandler, PersonLeavesVehicleEventHandler, PersonEntersVehicleEventHandler { + + private final Map, Double> personId2lastLeaveVehicleTime = new HashMap<>(); + private final Map, String> personId2previousActivity = new HashMap<>(); + private final Map, Id> personId2relevantModeLinkId = new HashMap<>(); + private final Map, Id> personId2homeLinkId = new HashMap<>(); + private final Set> ptDrivers = new HashSet<>(); + private final Set> hasAlreadyPaidDailyResidentialParkingCosts = new HashSet<>(); + private double compensationTime = Double.NaN; + + @Inject + private ParkingCostConfigGroup parkingCostConfigGroup; + + @Inject + private EventsManager events; + + @Inject + private Scenario scenario; + + @Inject + private QSimConfigGroup qSimConfigGroup; + + + @Override + public void reset(int iteration) { + this.personId2lastLeaveVehicleTime.clear(); + this.personId2previousActivity.clear(); + this.personId2relevantModeLinkId.clear(); + this.hasAlreadyPaidDailyResidentialParkingCosts.clear(); + this.ptDrivers.clear(); + } + + + @Override + public void handleEvent(TransitDriverStartsEvent event) { + ptDrivers.add(event.getDriverId()); + } + + + @Override + public void handleEvent(ActivityEndEvent event) { + if (ptDrivers.contains(event.getPersonId())) { + // skip pt drivers + } else { + if (!(StageActivityTypeIdentifier.isStageActivity(event.getActType()))) { + + personId2previousActivity.put(event.getPersonId(), event.getActType()); + + if (personId2relevantModeLinkId.get(event.getPersonId()) != null) { + personId2relevantModeLinkId.remove(event.getPersonId()); + } + } + } + } + + + @Override + public void handleEvent(PersonDepartureEvent event) { + if (! ptDrivers.contains(event.getPersonId())) { + // There might be several departures during a single trip. + if (event.getLegMode().equals(parkingCostConfigGroup.getMode())) { + personId2relevantModeLinkId.put(event.getPersonId(), event.getLinkId()); + } + } + } + + + @Override + public void handleEvent(PersonEntersVehicleEvent event) { + if (! ptDrivers.contains(event.getPersonId())) { + if (personId2relevantModeLinkId.get(event.getPersonId()) != null) { + + Link link = scenario.getNetwork().getLinks().get(personId2relevantModeLinkId.get(event.getPersonId())); + + if (parkingCostConfigGroup.getActivityPrefixesToBeExcludedFromParkingCost().stream() + .noneMatch(s -> personId2previousActivity.get(event.getPersonId()).startsWith(s))){ + + if (personId2previousActivity.get(event.getPersonId()).startsWith(parkingCostConfigGroup.getActivityPrefixForDailyParkingCosts())) { + // daily residential parking costs + + if (! hasAlreadyPaidDailyResidentialParkingCosts.contains(event.getPersonId())){ + hasAlreadyPaidDailyResidentialParkingCosts.add(event.getPersonId()); + + double residentialParkingFeePerDay = 0.; + if (link.getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()) != null) { + residentialParkingFeePerDay = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName()); + } + + if (residentialParkingFeePerDay > 0.) { + double amount = -1. * residentialParkingFeePerDay; + events.processEvent(new PersonMoneyEvent(event.getTime(), event.getPersonId(), amount, "residential parking", "city", "link " + link.getId().toString())); + } + } + + } else { + // other parking cost types + + double parkingStartTime = 0.; + if (personId2lastLeaveVehicleTime.get(event.getPersonId()) != null) { + parkingStartTime = personId2lastLeaveVehicleTime.get(event.getPersonId()); + } + int parkingDurationHrs = (int) Math.ceil((event.getTime() - parkingStartTime) / 3600.); + + double extraHourParkingCosts = 0.; + if (link.getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()) != null) { + extraHourParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName()); + } + + double firstHourParkingCosts = 0.; + if (link.getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()) != null) { + firstHourParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName()); + } + + double dailyParkingCosts = firstHourParkingCosts + 29 * extraHourParkingCosts; + if (link.getAttributes().getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName()) != null) { + dailyParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName()); + } + + double maxDailyParkingCosts = dailyParkingCosts; + if (link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName()) != null) { + maxDailyParkingCosts = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName()); + } + + double maxParkingDurationHrs = 30; + if (link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName()) != null) { + maxParkingDurationHrs = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName()); + } + + double parkingPenalty = 0.; + if (link.getAttributes().getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName()) != null) { + parkingPenalty = (double) link.getAttributes().getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName()); + } + + double costs = 0.; + if (parkingDurationHrs > 0) { + costs += firstHourParkingCosts; + costs += (parkingDurationHrs - 1) * extraHourParkingCosts; + } + if (costs > dailyParkingCosts) { + costs = dailyParkingCosts; + } + if (costs > maxDailyParkingCosts) { + costs = maxDailyParkingCosts; + } + if ((parkingDurationHrs > maxParkingDurationHrs) & (costs < parkingPenalty)) { + costs = parkingPenalty; + } + + if (costs > 0.) { + double amount = -1. * costs; + events.processEvent(new PersonMoneyEvent(event.getTime(), event.getPersonId(), amount, "non-residential parking", "city", "link " + link.getId().toString())); + } + + } + + } + + } + } + + } + + + @Override + public void handleEvent(PersonLeavesVehicleEvent event) { + if (! ptDrivers.contains(event.getPersonId())) { + personId2lastLeaveVehicleTime.put(event.getPersonId(), event.getTime()); + } + } + +} + diff --git a/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java b/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java new file mode 100644 index 00000000000..1bf57959872 --- /dev/null +++ b/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java @@ -0,0 +1,19 @@ +package playground.vsp.simpleParkingCostHandler; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.matsim.api.core.v01.population.Population; +import org.matsim.testcases.MatsimTestUtils; + +public class TestParkingCostHandler { + @RegisterExtension + private final MatsimTestUtils utils = new MatsimTestUtils(); + private Population population; + + @BeforeEach + public void setUp() { + // TODO: Network, Persons, Event construction (PT drivers, other) + + } + +} From 2987ee15b5cf2a41ae5c2168541dd0e86aab2590 Mon Sep 17 00:00:00 2001 From: Coldeign Date: Fri, 15 Mar 2024 14:04:41 +0100 Subject: [PATCH 2/2] Modified ParkingCostHandler for better readability Implemented ParkingCostHandlerTest to assure functionality --- .../ParkingCostConfigGroup.java | 1 - .../ParkingCostHandler.java | 121 +++--- ...erCopy.java => ParkingCostHandlerOld.java} | 25 +- .../ParkingCostHandlerTest.java | 387 ++++++++++++++++++ .../TestParkingCostHandler.java | 19 - 5 files changed, 481 insertions(+), 72 deletions(-) rename contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/{ParkingCostHandlerCopy.java => ParkingCostHandlerOld.java} (91%) create mode 100644 contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerTest.java delete mode 100644 contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java diff --git a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostConfigGroup.java b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostConfigGroup.java index f85094fead1..8dc2863686c 100644 --- a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostConfigGroup.java +++ b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostConfigGroup.java @@ -36,7 +36,6 @@ public class ParkingCostConfigGroup extends ReflectiveConfigGroup { public ParkingCostConfigGroup() { super(GROUP_NAME); } - private String mode = "car"; private String dailyParkingCostLinkAttributeName = "dailyPCost"; private String firstHourParkingCostLinkAttributeName = "oneHourPCost"; diff --git a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java index d23837f2b20..70dd233f32a 100644 --- a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java +++ b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandler.java @@ -79,13 +79,11 @@ public void reset(int iteration) { this.ptDrivers.clear(); } - @Override public void handleEvent(TransitDriverStartsEvent event) { ptDrivers.add(event.getDriverId()); } - @Override public void handleEvent(ActivityEndEvent event) { if (ptDrivers.contains(event.getPersonId())) { @@ -94,7 +92,6 @@ public void handleEvent(ActivityEndEvent event) { if ((StageActivityTypeIdentifier.isStageActivity(event.getActType()))) { return; } - personId2previousActivity.put(event.getPersonId(), event.getActType()); personId2relevantModeLinkId.remove(event.getPersonId()); } @@ -106,22 +103,25 @@ public void handleEvent(PersonDepartureEvent event) { return; } // There might be several departures during a single trip. - if (!event.getLegMode().equals(parkingCostConfigGroup.getMode())) { - return; + if (event.getLegMode().equals(parkingCostConfigGroup.getMode())) { + personId2relevantModeLinkId.put(event.getPersonId(), event.getLinkId()); } - personId2relevantModeLinkId.put(event.getPersonId(), event.getLinkId()); } + @Override + public void handleEvent(PersonLeavesVehicleEvent event) { + if (!ptDrivers.contains(event.getPersonId())) { + personId2lastLeaveVehicleTime.put(event.getPersonId(), event.getTime()); + } + } @Override public void handleEvent(PersonEntersVehicleEvent event) { // Preliminaries if (ptDrivers.contains(event.getPersonId())) { - // drives public transport -> doesn't pay for parking return; } if (personId2relevantModeLinkId.get(event.getPersonId()) == null) { - // return; } @@ -130,19 +130,18 @@ public void handleEvent(PersonEntersVehicleEvent event) { return; } - if (hasAlreadyPaidDailyResidentialParkingCosts.contains(event.getPersonId())){ - // has already paid daily residential parking costs - return; - } - Link link = scenario.getNetwork().getLinks().get(personId2relevantModeLinkId.get(event.getPersonId())); Attributes attributes = link.getAttributes(); if (personId2previousActivity.get(event.getPersonId()).startsWith(parkingCostConfigGroup.getActivityPrefixForDailyParkingCosts())) { // daily residential parking costs + if (hasAlreadyPaidDailyResidentialParkingCosts.contains(event.getPersonId())){ + // has already paid daily residential parking costs + return; + } hasAlreadyPaidDailyResidentialParkingCosts.add(event.getPersonId()); - double residentialParkingFeePerDay = (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName())).orElse(0.); + double residentialParkingFeePerDay = getResidentialParkingFeePerDay(attributes); if (residentialParkingFeePerDay > 0.) { double amount = -1. * residentialParkingFeePerDay; @@ -153,82 +152,102 @@ public void handleEvent(PersonEntersVehicleEvent event) { // other parking cost types + double costs = calcCosts(event, attributes); + + if (costs <= 0.) { + return; + } + double amount = -1. * costs; + events.processEvent(createPersonMoneyEvent(event, amount, link, false)); + } + private PersonMoneyEvent createPersonMoneyEvent(PersonEntersVehicleEvent event, double amount, Link link, boolean isResidentialParking) { + String purpose; + if (isResidentialParking) { + purpose = "residential parking"; + } else { + purpose = "non-residential parking"; + } + return new PersonMoneyEvent(event.getTime(), event.getPersonId(), amount, purpose, "city", "link " + link.getId()); + } + private double calcCosts(PersonEntersVehicleEvent event, Attributes attributes) { double costs = 0.; - int parkingDurationHrs = (int) Math.ceil((event.getTime() - calcParkingStartTime(event)) / 3600.); - double firstHourParkingCosts = calcFirstHourParkingCosts(attributes); - double extraHourParkingCosts = calcExtraHourParkingCosts(attributes); + int parkingDurationHrs = (int) Math.ceil((event.getTime() - getParkingStartTime(event)) / 3600.); + double firstHourParkingCosts = getFirstHourParkingCosts(attributes); + double extraHourParkingCosts = getExtraHourParkingCosts(attributes); if (parkingDurationHrs > 0) { costs += firstHourParkingCosts; costs += (parkingDurationHrs - 1) * extraHourParkingCosts; } - double dailyParkingCosts = calcDailyParkingCosts(attributes, firstHourParkingCosts, extraHourParkingCosts); + double dailyParkingCosts = getDailyParkingCosts(attributes, firstHourParkingCosts, extraHourParkingCosts); if (costs > dailyParkingCosts) { costs = dailyParkingCosts; } - double maxDailyParkingCosts = calcMaxDailyParkingCosts(attributes, dailyParkingCosts); + double maxDailyParkingCosts = getMaxDailyParkingCosts(attributes, dailyParkingCosts); if (costs > maxDailyParkingCosts) { costs = maxDailyParkingCosts; } - double maxParkingDurationHrs = calcMaxParkingDurationHrs(attributes); - double parkingPenalty = calcParkingPenalty(attributes); + double maxParkingDurationHrs = getMaxParkingDurationHrs(attributes); + double parkingPenalty = getParkingPenalty(attributes); if ((parkingDurationHrs > maxParkingDurationHrs) & (costs < parkingPenalty)) { costs = parkingPenalty; } - - if (costs <= 0.) { - return; - } - double amount = -1. * costs; - events.processEvent(createPersonMoneyEvent(event, amount, link, false)); + return costs; } - @Override - public void handleEvent(PersonLeavesVehicleEvent event) { - if (ptDrivers.contains(event.getPersonId())) { - return; - } - personId2lastLeaveVehicleTime.put(event.getPersonId(), event.getTime()); + private double getResidentialParkingFeePerDay(Attributes attributes) { + return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName())).orElse(0.); } - - - // private methods - private double calcParkingStartTime(PersonEntersVehicleEvent event) { + private double getParkingStartTime(PersonEntersVehicleEvent event) { return Optional.ofNullable(personId2lastLeaveVehicleTime.get(event.getPersonId())).orElse(0.); } - private double calcExtraHourParkingCosts(Attributes attributes) { + private double getExtraHourParkingCosts(Attributes attributes) { return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName())).orElse(0.); } - private double calcFirstHourParkingCosts(Attributes attributes) { + private double getFirstHourParkingCosts(Attributes attributes) { return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName())).orElse(0.); } - private double calcDailyParkingCosts(Attributes attributes, double firstHourParkingCosts, double extraHourParkingCosts) { + private double getDailyParkingCosts(Attributes attributes, double firstHourParkingCosts, double extraHourParkingCosts) { return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName())).orElse(firstHourParkingCosts + 29 * extraHourParkingCosts); } - private double calcMaxDailyParkingCosts(Attributes attributes, double dailyParkingCosts) { + private double getMaxDailyParkingCosts(Attributes attributes, double dailyParkingCosts) { return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName())).orElse(dailyParkingCosts); } - private double calcMaxParkingDurationHrs(Attributes attributes) { + private double getMaxParkingDurationHrs(Attributes attributes) { return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName())).orElse(30.); } - private double calcParkingPenalty(Attributes attributes) { + private double getParkingPenalty(Attributes attributes) { return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName())).orElse(0.); } - private PersonMoneyEvent createPersonMoneyEvent(PersonEntersVehicleEvent event, double amount, Link link, boolean isResidentialParking) { - String purpose; - if (isResidentialParking) { - purpose = "residential parking"; - } else { - purpose = "non-residential parking"; - } - return new PersonMoneyEvent(event.getTime(), event.getPersonId(), amount, purpose, "city", "link " + link.getId()); + public Map, Double> getPersonId2lastLeaveVehicleTime() { + return personId2lastLeaveVehicleTime; + } + + public Map, String> getPersonId2previousActivity() { + return personId2previousActivity; + } + + public Map, Id> getPersonId2relevantModeLinkId() { + return personId2relevantModeLinkId; + } + + public Map, Id> getPersonId2homeLinkId() { + return personId2homeLinkId; + } + + public Set> getPtDrivers() { + return ptDrivers; + } + + public Set> getHasAlreadyPaidDailyResidentialParkingCosts() { + return hasAlreadyPaidDailyResidentialParkingCosts; } } diff --git a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerCopy.java b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerOld.java similarity index 91% rename from contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerCopy.java rename to contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerOld.java index b5e2ddc8731..7483f353d96 100644 --- a/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerCopy.java +++ b/contribs/vsp/src/main/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerOld.java @@ -39,7 +39,7 @@ * @author ikaddoura */ -final class ParkingCostHandlerCopy implements TransitDriverStartsEventHandler, ActivityEndEventHandler, PersonDepartureEventHandler, PersonLeavesVehicleEventHandler, PersonEntersVehicleEventHandler { +final class ParkingCostHandlerOld implements TransitDriverStartsEventHandler, ActivityEndEventHandler, PersonDepartureEventHandler, PersonLeavesVehicleEventHandler, PersonEntersVehicleEventHandler { private final Map, Double> personId2lastLeaveVehicleTime = new HashMap<>(); private final Map, String> personId2previousActivity = new HashMap<>(); @@ -209,5 +209,28 @@ public void handleEvent(PersonLeavesVehicleEvent event) { } } + public Map, Double> getPersonId2lastLeaveVehicleTime() { + return personId2lastLeaveVehicleTime; + } + + public Map, String> getPersonId2previousActivity() { + return personId2previousActivity; + } + + public Map, Id> getPersonId2relevantModeLinkId() { + return personId2relevantModeLinkId; + } + + public Map, Id> getPersonId2homeLinkId() { + return personId2homeLinkId; + } + + public Set> getPtDrivers() { + return ptDrivers; + } + + public Set> getHasAlreadyPaidDailyResidentialParkingCosts() { + return hasAlreadyPaidDailyResidentialParkingCosts; + } } diff --git a/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerTest.java b/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerTest.java new file mode 100644 index 00000000000..e848ba2043a --- /dev/null +++ b/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/ParkingCostHandlerTest.java @@ -0,0 +1,387 @@ +package playground.vsp.simpleParkingCostHandler; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import org.junit.jupiter.api.*; +import org.matsim.api.core.v01.Coord; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.events.*; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.network.Node; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.config.Config; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.OutputDirectoryHierarchy; +import org.matsim.core.events.handler.EventHandler; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.utils.objectattributes.attributable.Attributes; + +import java.util.*; + +public class ParkingCostHandlerTest { + private Injector injector; + @BeforeEach + public void setup() { + Config config = ConfigUtils.createConfig(); + config.controller().setOverwriteFileSetting(OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists); + Scenario scenario = ScenarioUtils.createScenario(config); + Network network = NetworkUtils.createNetwork(config); + + TestsEventsManager eventsManager = new TestsEventsManager(); + ParkingCostConfigGroup configGroup = new ParkingCostConfigGroup(); + + injector = Guice.createInjector(new AbstractModule() { + @Override + public void configure() { + bind(ParkingCostConfigGroup.class).toInstance(configGroup); + bind(EventsManager.class).toInstance(eventsManager); + bind(Scenario.class).toInstance(scenario); + bind(Network.class).toInstance(network); + } + }); + } + // Basic Event Handling Tests + @Test + public void transitDriverStartsEventTest() { + Person ptDriver = new Tester(); + TransitDriverStartsEvent tDSE = new TransitDriverStartsEvent(0, ptDriver.getId(), null, null, null, null); + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + pch.handleEvent(tDSE); + Assertions.assertTrue(pch.getPtDrivers().contains(ptDriver.getId())); + } + @Test + public void personLeavesVehicleEventTest() { + Person ptDriver = new Tester(); + Person tester = new Tester(); + + TransitDriverStartsEvent tDSEvent = new TransitDriverStartsEvent(0, ptDriver.getId(), null, null, null, null); + PersonLeavesVehicleEvent pLVEventParking = new PersonLeavesVehicleEvent(100, tester.getId(), null); + PersonLeavesVehicleEvent pLVEventPtDriver = new PersonLeavesVehicleEvent(100, ptDriver.getId(), null); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + + pch.handleEvent(tDSEvent); + pch.handleEvent(pLVEventParking); + pch.handleEvent(pLVEventPtDriver); + + Assertions.assertTrue(pch.getPtDrivers().contains(ptDriver.getId())); + Assertions.assertFalse(pch.getPersonId2lastLeaveVehicleTime().containsKey(ptDriver.getId())); + Assertions.assertTrue(pch.getPersonId2lastLeaveVehicleTime().containsKey(tester.getId())); + Assertions.assertEquals(100.,pch.getPersonId2lastLeaveVehicleTime().get(tester.getId())); + } + @Test + public void activityEndEventTest() { + Person tester1 = new Tester(); + String actType1 = "test"; + + Person tester2 = new Tester(); + String actType2 = "test-interaction"; + + Person ptDriver = new Tester(); + + ActivityEndEvent aEEventActivity = new ActivityEndEvent(0, tester1.getId(), null, null,actType1, null); + ActivityEndEvent aEEventInteraction = new ActivityEndEvent(0, tester2.getId(), null, null, actType2, null); + TransitDriverStartsEvent tDSEvent = new TransitDriverStartsEvent(0, ptDriver.getId(), null, null, null, null); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + + pch.handleEvent(tDSEvent); + pch.handleEvent(aEEventActivity); + pch.handleEvent(aEEventInteraction); + + Assertions.assertNull(pch.getPersonId2previousActivity().get(tester2.getId())); + Assertions.assertEquals(actType1, pch.getPersonId2previousActivity().get(tester1.getId())); + + Assertions.assertFalse(pch.getPersonId2previousActivity().containsKey(ptDriver.getId())); + Assertions.assertTrue(pch.getPtDrivers().contains(ptDriver.getId())); + } + @Test + public void personDepartureEventTest() { + Person testerWithCar = new Tester(); + Person testerWithoutCar = new Tester(); + Person ptDriver = new Tester(); + + PersonDepartureEvent pDEventCar = new PersonDepartureEvent(0, testerWithCar.getId(),null, "car",null); + PersonDepartureEvent pDEventNoCar = new PersonDepartureEvent(0, testerWithoutCar.getId(), null, "pt", null); + PersonDepartureEvent pDEventPtDriver = new PersonDepartureEvent(0, ptDriver.getId(),null,"pt",null); + TransitDriverStartsEvent tDSEvent = new TransitDriverStartsEvent(0, ptDriver.getId(), null, null, null, null); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + + pch.handleEvent(tDSEvent); + pch.handleEvent(pDEventCar); + pch.handleEvent(pDEventNoCar); + pch.handleEvent(pDEventPtDriver); + + Assertions.assertTrue(pch.getPtDrivers().contains(ptDriver.getId())); + Assertions.assertFalse(pch.getPersonId2relevantModeLinkId().containsKey(ptDriver.getId())); + Assertions.assertFalse(pch.getPersonId2relevantModeLinkId().containsKey(testerWithoutCar.getId())); + Assertions.assertTrue(pch.getPersonId2relevantModeLinkId().containsKey(testerWithCar.getId())); + } + @Test + public void personEntersVehicleEventTest() { + // tests basic functionality and cost calculation + Network network = injector.getInstance(Scenario.class).getNetwork(); + // setup + Node node1 = NetworkUtils.createAndAddNode(network, Id.createNodeId(1), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.createNodeId(2), new Coord(10, 0)); + Link link = NetworkUtils.createAndAddLink(network, Id.createLinkId(1), node1, node2, 10, 30, 100,3); // arbitrary values + setupLinkConfig(link, 10,440,5,15,500,30,30); + // Persons construction + Person ptDriver = new Tester(); // should not pay at all + Person resident = new Tester(); // should pay residential parking costs + Person shopper = new Tester(); // should pay non-residential parking costs + // Event construction + // ptDriver events + TransitDriverStartsEvent tDSEvent = new TransitDriverStartsEvent(0, ptDriver.getId(), null, null, null, null); + PersonLeavesVehicleEvent pLVEventPtDriver = new PersonLeavesVehicleEvent(1000, ptDriver.getId(), null); + PersonEntersVehicleEvent pEVEventPtDriver = new PersonEntersVehicleEvent(11000, ptDriver.getId(),null); + // resident events + Event[] residentEvents = createEvents(resident.getId(),link.getId(),"home","car",10); + // shopper events + Event[] shopperEvents = createEvents(shopper.getId(),link.getId(),"shopping","car",1); + + // Event handling + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + // ptDriver handling + pch.handleEvent(tDSEvent); + pch.handleEvent(pLVEventPtDriver); + pch.handleEvent(pEVEventPtDriver); + // resident handling + handleEventArray(pch, residentEvents); + // shopper handling + handleEventArray(pch, shopperEvents); + + // Assertions + TestsEventsManager eventsManager = (TestsEventsManager) injector.getInstance(EventsManager.class); + // ptDriver assertions + Assertions.assertTrue(pch.getPtDrivers().contains(ptDriver.getId())); + Assertions.assertNull(eventsManager.getEventByPersonId(ptDriver.getId())); + // resident assertions + Assertions.assertTrue(pch.getHasAlreadyPaidDailyResidentialParkingCosts().contains(resident.getId())); + Assertions.assertEquals(-10.0, eventsManager.getEventByPersonId(resident.getId()).getAmount()); + Assertions.assertEquals("residential parking", eventsManager.getEventByPersonId(resident.getId()).getPurpose()); + // shopper assertions + Assertions.assertFalse(pch.getHasAlreadyPaidDailyResidentialParkingCosts().contains(shopper.getId())); + Assertions.assertEquals(-5, eventsManager.getEventByPersonId(shopper.getId()).getAmount()); + Assertions.assertEquals("non-residential parking", eventsManager.getEventByPersonId(shopper.getId()).getPurpose()); + + } + // Parking Cost Calculation Tests + @Test + public void penaltyParkingCostCalculationsTest() { + // Network construction + Network network = injector.getInstance(Scenario.class).getNetwork(); + Node node1 = NetworkUtils.createAndAddNode(network, Id.createNodeId(1), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.createNodeId(2), new Coord(10, 0)); + Link link = NetworkUtils.createAndAddLink(network, Id.createLinkId(1), node1, node2,10,30,100,3); + setupLinkConfig(link, 0, 200,10,10,200,2,100); + // Person construction + Person tester1 = new Tester(); + Person tester2 = new Tester(); + // Event construction + Event[] events = createEvents(tester1.getId(),link.getId(),"other","car",3); + // Event handling + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + handleEventArray(pch, events); + handleEventArray(pch, createEvents(tester2.getId(),link.getId(),"other","car",20)); + // Assertions + TestsEventsManager eventsManager = (TestsEventsManager) injector.getInstance(EventsManager.class); + Assertions.assertEquals(-100, eventsManager.getEventByPersonId(tester1.getId()).getAmount()); + Assertions.assertEquals(-200, eventsManager.getEventByPersonId(tester2.getId()).getAmount()); + } + @Test + public void firstHourParkingCostCalculationTest() { + Network network = injector.getInstance(Scenario.class).getNetwork(); + Node node1 = NetworkUtils.createAndAddNode(network, Id.createNodeId(1), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.createNodeId(2), new Coord(10, 0)); + Link link = NetworkUtils.createAndAddLink(network, Id.createLinkId(1), node1, node2,10,30,100,3); + setupLinkConfig(link, 0, 50,10,10,100,10,0); + + Person tester1 = new Tester(); + Person tester2 = new Tester(); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + handleEventArray(pch, createEvents(tester1.getId(),link.getId(),"other","car",1)); + handleEventArray(pch, createEvents(tester2.getId(),link.getId(),"other","car",0.5)); // rounded up to 1hr + + TestsEventsManager eventsManager = (TestsEventsManager) injector.getInstance(EventsManager.class); + Assertions.assertEquals(-10, eventsManager.getEventByPersonId(tester1.getId()).getAmount()); + Assertions.assertEquals(-10, eventsManager.getEventByPersonId(tester2.getId()).getAmount()); + } + @Test + public void hourlyParkingCostCalculationTest() { + Network network = injector.getInstance(Scenario.class).getNetwork(); + Node node1 = NetworkUtils.createAndAddNode(network, Id.createNodeId(1), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.createNodeId(2), new Coord(10, 0)); + Link link = NetworkUtils.createAndAddLink(network, Id.createLinkId(1), node1, node2,10,30,100,3); + setupLinkConfig(link, 0, 150,10,20,100,10,0); + + Person parking1hr = new Tester(); + Person parking3hr = new Tester(); + Person parking8hr = new Tester(); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + handleEventArray(pch, createEvents(parking1hr.getId(),link.getId(),"other","car",1)); + handleEventArray(pch, createEvents(parking3hr.getId(),link.getId(),"other","car",3)); + handleEventArray(pch, createEvents(parking8hr.getId(),link.getId(),"other","car",8)); + + TestsEventsManager eventsManager = (TestsEventsManager) injector.getInstance(EventsManager.class); + Assertions.assertEquals(-10, eventsManager.getEventByPersonId(parking1hr.getId()).getAmount()); // first hour parking cost only + Assertions.assertEquals(-50, eventsManager.getEventByPersonId(parking3hr.getId()).getAmount()); // 1x 10 + 2x 20 hourly costs + Assertions.assertEquals(-100, eventsManager.getEventByPersonId(parking8hr.getId()).getAmount()); // 150 in hourly costs, capped by maxDailyParkingCosts + } + @Test + public void residentialFeeIsPaidOnceTest() { + Network network = injector.getInstance(Scenario.class).getNetwork(); + Node node1 = NetworkUtils.createAndAddNode(network, Id.createNodeId(1), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.createNodeId(2), new Coord(10, 0)); + Link link = NetworkUtils.createAndAddLink(network, Id.createLinkId(1), node1, node2,10,30,100,3); + setupLinkConfig(link, 100, 0,0,0,0,0,0); + + Person tester = new Tester(); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + handleEventArray(pch, createEvents(tester.getId(),link.getId(),"home","car",10)); + handleEventArray(pch, createEvents(tester.getId(),link.getId(),"home","car",2)); + + TestsEventsManager eventsManager = (TestsEventsManager) injector.getInstance(EventsManager.class); + Assertions.assertEquals(1, eventsManager.getEvents().size()); + Assertions.assertEquals(-100, eventsManager.getEventByPersonId(tester.getId()).getAmount()); + } + @Test + public void handlerDefaultsTest() { + Network network = injector.getInstance(Scenario.class).getNetwork(); + Node node1 = NetworkUtils.createAndAddNode(network, Id.createNodeId(1), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.createNodeId(2), new Coord(10, 0)); + Link link = NetworkUtils.createAndAddLink(network, Id.createLinkId(1), node1, node2,10,30,100,3); + + Person tester = new Tester(); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + handleEventArray(pch, createEvents(tester.getId(),link.getId(),"other","car",10)); + + TestsEventsManager eventsManager = (TestsEventsManager) injector.getInstance(EventsManager.class); + Assertions.assertTrue(eventsManager.getEvents().isEmpty()); + } + @Test + public void testDailyParkingCost() { + Network network = injector.getInstance(Scenario.class).getNetwork(); + Node node1 = NetworkUtils.createAndAddNode(network, Id.createNodeId(1), new Coord(0, 0)); + Node node2 = NetworkUtils.createAndAddNode(network, Id.createNodeId(2), new Coord(10, 0)); + Link link = NetworkUtils.createAndAddLink(network, Id.createLinkId(1), node1, node2,10,30,100,3); + setupLinkConfig(link, 0, 50,10,10,100,10,0); + + Person tester1 = new Tester(); + Person tester2 = new Tester(); + + ParkingCostHandler pch = injector.getInstance(ParkingCostHandler.class); + handleEventArray(pch, createEvents(tester1.getId(),link.getId(),"other","car",10)); + handleEventArray(pch, createEvents(tester2.getId(),link.getId(),"other","car",2)); + + TestsEventsManager eventsManager = (TestsEventsManager) injector.getInstance(EventsManager.class); + Assertions.assertEquals(-50, eventsManager.getEventByPersonId(tester1.getId()).getAmount()); + Assertions.assertEquals(-20, eventsManager.getEventByPersonId(tester2.getId()).getAmount()); + } + + // sets up given cost attributes for a given link + private void setupLinkConfig(Link link, double residentialParkingFee, double dailyParkingCost, double firstHourParkingCost, + double extraHourParkingCost, double maxDailyParkingCost, double maxParkingDurationHrs, double parkingPenaltyCost) { + ParkingCostConfigGroup configGroup = injector.getInstance(ParkingCostConfigGroup.class); + + link.getAttributes().putAttribute(configGroup.getResidentialParkingFeeAttributeName(), residentialParkingFee); + link.getAttributes().putAttribute(configGroup.getDailyParkingCostLinkAttributeName(), dailyParkingCost); + link.getAttributes().putAttribute(configGroup.getFirstHourParkingCostLinkAttributeName(), firstHourParkingCost); + link.getAttributes().putAttribute(configGroup.getExtraHourParkingCostLinkAttributeName(), extraHourParkingCost); + link.getAttributes().putAttribute(configGroup.getMaxDailyParkingCostLinkAttributeName(), maxDailyParkingCost); + link.getAttributes().putAttribute(configGroup.getMaxParkingDurationAttributeName(), maxParkingDurationHrs); + link.getAttributes().putAttribute(configGroup.getParkingPenaltyAttributeName(), parkingPenaltyCost); + } + private Event[] createEvents(Id personId, Id linkId, String actType, String mode, double durationHrs){ + Event[] out = new Event[4]; + out[0] = new PersonLeavesVehicleEvent(0, personId, null); + out[1] = new ActivityEndEvent(0, personId, linkId,null, actType,null); + out[2] = new PersonDepartureEvent(0, personId, linkId, mode,null); + out[3] = new PersonEntersVehicleEvent(durationHrs * 3600, personId,null); + return out; + } + private void handleEventArray(ParkingCostHandler pch, Event[] events) { + for (Event event : events) { + if (event instanceof PersonLeavesVehicleEvent) { + pch.handleEvent((PersonLeavesVehicleEvent) event); + continue; + } + if (event instanceof ActivityEndEvent) { + pch.handleEvent((ActivityEndEvent) event); + continue; + } + if (event instanceof PersonDepartureEvent) { + pch.handleEvent((PersonDepartureEvent) event); + continue; + } + if (event instanceof PersonEntersVehicleEvent) { + pch.handleEvent((PersonEntersVehicleEvent) event); + } + } + } +} +class TestsEventsManager implements EventsManager { + private final List events = new ArrayList<>(); + @Override + public void processEvent(Event event) { + if (event instanceof PersonMoneyEvent) { + events.add((PersonMoneyEvent) event); + } + } + public List getEvents() { + return events; + } + public PersonMoneyEvent getEventByPersonId(Id id) { + for (PersonMoneyEvent event: events) { + if (event.getPersonId().equals(id)) { + return event; + } + } + return null; + } + @Deprecated + public void addHandler(EventHandler handler) {} + @Deprecated + public void removeHandler(EventHandler handler) {} + @Deprecated + public void resetHandlers(int iteration) {} + @Deprecated + public void initProcessing() {} + @Deprecated + public void afterSimStep(double time) {} + @Deprecated + public void finishProcessing() {} +} +class Tester implements Person { + private static long nextId = 0; + private final Id id = Id.createPersonId(nextId++); + @Override + public Id getId() {return this.id;} + @Deprecated + public Attributes getAttributes() {return null;} + @Deprecated + public Map getCustomAttributes() {return null;} + @Deprecated + public List getPlans() {return null;} + @Deprecated + public boolean addPlan(Plan p) {return false;} + @Deprecated + public boolean removePlan(Plan p) {return false;} + @Deprecated + public Plan getSelectedPlan() {return null;} + @Deprecated + public void setSelectedPlan(Plan selectedPlan) {} + @Deprecated + public Plan createCopyOfSelectedPlanAndMakeSelected() {return null;} +} diff --git a/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java b/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java deleted file mode 100644 index 1bf57959872..00000000000 --- a/contribs/vsp/src/test/java/playground/vsp/simpleParkingCostHandler/TestParkingCostHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package playground.vsp.simpleParkingCostHandler; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.matsim.api.core.v01.population.Population; -import org.matsim.testcases.MatsimTestUtils; - -public class TestParkingCostHandler { - @RegisterExtension - private final MatsimTestUtils utils = new MatsimTestUtils(); - private Population population; - - @BeforeEach - public void setUp() { - // TODO: Network, Persons, Event construction (PT drivers, other) - - } - -}