Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add refactored parking cost handler to base repo #3201

Merged
merged 2 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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