Skip to content

Commit

Permalink
Merge pull request #3201 from Coldeign/master
Browse files Browse the repository at this point in the history
Add refactored parking cost handler to base repo
  • Loading branch information
paulheinr authored Apr 4, 2024
2 parents 7f5f1b5 + 2987ee1 commit 0ef11df
Show file tree
Hide file tree
Showing 4 changed files with 771 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -42,32 +38,31 @@
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<Id<Person>, Double> personId2lastLeaveVehicleTime = new HashMap<>();
private final Map<Id<Person>, String> personId2previousActivity = new HashMap<>();
private final Map<Id<Person>, Id<Link>> personId2relevantModeLinkId = new HashMap<>();
private final Map<Id<Person>, Id<Link>> personId2homeLinkId = new HashMap<>();
private final Set<Id<Person>> ptDrivers = new HashSet<>();
private final Set<Id<Person>> hasAlreadyPaidDailyResidentialParkingCosts = new HashSet<>();
private double compensationTime = Double.NaN;

@Inject
private ParkingCostConfigGroup parkingCostConfigGroup;

@Inject
private EventsManager events;

@Inject
private Scenario scenario;

Expand All @@ -84,143 +79,175 @@ public void reset(int iteration) {
this.ptDrivers.clear();
}


@Override
public void handleEvent(TransitDriverStartsEvent event) {
ptDrivers.add(event.getDriverId());
}


@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())) {
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) {
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())) {
return;
}
if (personId2relevantModeLinkId.get(event.getPersonId()) == null) {
return;
}

if (parkingCostConfigGroup.getActivityPrefixesToBeExcludedFromParkingCost().stream()
.anyMatch(s -> personId2previousActivity.get(event.getPersonId()).startsWith(s))) {
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 = getResidentialParkingFeePerDay(attributes);

if (residentialParkingFeePerDay > 0.) {
double amount = -1. * residentialParkingFeePerDay;
events.processEvent(createPersonMoneyEvent(event, amount, link, true));
}
return;
}

// 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() - getParkingStartTime(event)) / 3600.);
double firstHourParkingCosts = getFirstHourParkingCosts(attributes);
double extraHourParkingCosts = getExtraHourParkingCosts(attributes);

@Override
public void handleEvent(PersonLeavesVehicleEvent event) {
if (! ptDrivers.contains(event.getPersonId())) {
personId2lastLeaveVehicleTime.put(event.getPersonId(), event.getTime());
if (parkingDurationHrs > 0) {
costs += firstHourParkingCosts;
costs += (parkingDurationHrs - 1) * extraHourParkingCosts;
}

double dailyParkingCosts = getDailyParkingCosts(attributes, firstHourParkingCosts, extraHourParkingCosts);

if (costs > dailyParkingCosts) {
costs = dailyParkingCosts;
}

double maxDailyParkingCosts = getMaxDailyParkingCosts(attributes, dailyParkingCosts);

if (costs > maxDailyParkingCosts) {
costs = maxDailyParkingCosts;
}

double maxParkingDurationHrs = getMaxParkingDurationHrs(attributes);
double parkingPenalty = getParkingPenalty(attributes);

if ((parkingDurationHrs > maxParkingDurationHrs) & (costs < parkingPenalty)) {
costs = parkingPenalty;
}
return costs;
}

private double getResidentialParkingFeePerDay(Attributes attributes) {
return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getResidentialParkingFeeAttributeName())).orElse(0.);
}
private double getParkingStartTime(PersonEntersVehicleEvent event) {
return Optional.ofNullable(personId2lastLeaveVehicleTime.get(event.getPersonId())).orElse(0.);
}
private double getExtraHourParkingCosts(Attributes attributes) {
return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getExtraHourParkingCostLinkAttributeName())).orElse(0.);
}
private double getFirstHourParkingCosts(Attributes attributes) {
return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getFirstHourParkingCostLinkAttributeName())).orElse(0.);
}
private double getDailyParkingCosts(Attributes attributes, double firstHourParkingCosts, double extraHourParkingCosts) {
return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getDailyParkingCostLinkAttributeName())).orElse(firstHourParkingCosts + 29 * extraHourParkingCosts);
}
private double getMaxDailyParkingCosts(Attributes attributes, double dailyParkingCosts) {
return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getMaxDailyParkingCostLinkAttributeName())).orElse(dailyParkingCosts);
}
private double getMaxParkingDurationHrs(Attributes attributes) {
return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getMaxParkingDurationAttributeName())).orElse(30.);
}
private double getParkingPenalty(Attributes attributes) {
return (double) Optional.ofNullable(attributes.getAttribute(parkingCostConfigGroup.getParkingPenaltyAttributeName())).orElse(0.);
}
public Map<Id<Person>, Double> getPersonId2lastLeaveVehicleTime() {
return personId2lastLeaveVehicleTime;
}

public Map<Id<Person>, String> getPersonId2previousActivity() {
return personId2previousActivity;
}

public Map<Id<Person>, Id<Link>> getPersonId2relevantModeLinkId() {
return personId2relevantModeLinkId;
}

public Map<Id<Person>, Id<Link>> getPersonId2homeLinkId() {
return personId2homeLinkId;
}

public Set<Id<Person>> getPtDrivers() {
return ptDrivers;
}

public Set<Id<Person>> getHasAlreadyPaidDailyResidentialParkingCosts() {
return hasAlreadyPaidDailyResidentialParkingCosts;
}
}

Loading

0 comments on commit 0ef11df

Please sign in to comment.