Skip to content

Commit 1b34933

Browse files
committed
improve LTZ policy
1 parent f02c7fd commit 1b34933

File tree

4 files changed

+147
-51
lines changed

4 files changed

+147
-51
lines changed

core/src/main/java/org/eqasim/core/simulation/policies/impl/city_tax/CityTaxPolicyFactory.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
import org.eqasim.core.simulation.policies.config.PoliciesConfigGroup;
1313
import org.eqasim.core.simulation.policies.routing.FixedRoutingPenalty;
1414
import org.eqasim.core.simulation.policies.routing.PolicyLinkFinder;
15+
import org.eqasim.core.simulation.policies.routing.PolicyLinkFinder.PolicyLinks;
1516
import org.eqasim.core.simulation.policies.routing.PolicyLinkFinder.Predicate;
16-
import org.matsim.api.core.v01.IdSet;
17-
import org.matsim.api.core.v01.network.Link;
1817
import org.matsim.api.core.v01.network.Network;
1918
import org.matsim.core.config.Config;
2019
import org.matsim.core.config.ConfigGroup;
@@ -53,17 +52,17 @@ private Policy createPolicy(CityTaxConfigGroup enterConfig, PolicyPersonFilter p
5352
logger.info(" Perimeters: " + enterConfig.perimetersPath);
5453
logger.info(" Tax level: " + enterConfig.tax_EUR + " EUR");
5554

56-
IdSet<Link> linkIds = PolicyLinkFinder
55+
PolicyLinks links = PolicyLinkFinder
5756
.create(new File(
5857
ConfigGroup.getInputFileURL(config.getContext(), enterConfig.perimetersPath).getPath()))
59-
.findLinks(network, Predicate.Entering, false);
58+
.findLinks(network, Predicate.Entering);
6059

61-
logger.info(" Affected entering links: " + linkIds.size());
60+
logger.info(" Affected entering links: " + links.active().size());
6261

6362
return new DefaultPolicy(
64-
new FixedRoutingPenalty(linkIds, calculateEnterTaxPenalty(enterConfig.tax_EUR, modeParameters),
63+
new FixedRoutingPenalty(links.active(), calculateEnterTaxPenalty(enterConfig.tax_EUR, modeParameters),
6564
personFilter),
66-
new CityTaxUtilityPenalty(linkIds, modeParameters, enterConfig.tax_EUR, personFilter));
65+
new CityTaxUtilityPenalty(links.active(), modeParameters, enterConfig.tax_EUR, personFilter));
6766
}
6867

6968
private double calculateEnterTaxPenalty(double enterTax_EUR, ModeParameters parameters) {

core/src/main/java/org/eqasim/core/simulation/policies/impl/limited_traffic_zone/LimitedTrafficZonePolicyFactory.java

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import java.io.BufferedReader;
44
import java.io.File;
55
import java.io.IOException;
6+
import java.util.HashSet;
7+
import java.util.Set;
68

79
import org.apache.logging.log4j.LogManager;
810
import org.apache.logging.log4j.Logger;
@@ -11,8 +13,9 @@
1113
import org.eqasim.core.simulation.policies.PolicyFactory;
1214
import org.eqasim.core.simulation.policies.PolicyPersonFilter;
1315
import org.eqasim.core.simulation.policies.config.PoliciesConfigGroup;
14-
import org.eqasim.core.simulation.policies.routing.FactorRoutingPenalty;
16+
import org.eqasim.core.simulation.policies.routing.FixedRoutingPenalty;
1517
import org.eqasim.core.simulation.policies.routing.PolicyLinkFinder;
18+
import org.eqasim.core.simulation.policies.routing.PolicyLinkFinder.PolicyLinks;
1619
import org.eqasim.core.simulation.policies.routing.PolicyLinkFinder.Predicate;
1720
import org.matsim.api.core.v01.Id;
1821
import org.matsim.api.core.v01.IdSet;
@@ -26,7 +29,8 @@ public class LimitedTrafficZonePolicyFactory implements PolicyFactory {
2629
private static final Logger logger = LogManager.getLogger(LimitedTrafficZonePolicyFactory.class);
2730

2831
static public final String POLICY_NAME = "limitedTrafficZone";
29-
private final double insideFactor = 3600.0;
32+
private final double routingPenalty = 7200.0;
33+
private final double utilityPenalty = 1000.0;
3034

3135
private final Config config;
3236
private final Network network;
@@ -60,33 +64,33 @@ private Policy createPolicy(LimitedTrafficZoneConfigGroup ltzConfig, PolicyPerso
6064
"Only one of perimetersPath and linkListPath can be set for policy " + ltzConfig.policyName);
6165
}
6266

63-
final IdSet<Link> linkIds;
67+
PolicyLinks links;
6468
if (!ltzConfig.perimetersPath.isEmpty()) {
6569
logger.info(" Perimeters: " + ltzConfig.perimetersPath);
6670

67-
linkIds = PolicyLinkFinder
71+
links = PolicyLinkFinder
6872
.create(new File(
6973
ConfigGroup.getInputFileURL(config.getContext(), ltzConfig.perimetersPath).getPath()))
70-
.findLinks(network, Predicate.Inside, true);
71-
72-
logger.info(" Affected inside links: " + linkIds.size());
74+
.findLinks(network, Predicate.Crossing);
7375
} else if (!ltzConfig.linkListPath.isEmpty()) {
7476
logger.info(" Link list: " + ltzConfig.linkListPath);
7577

76-
linkIds = loadLinkList(ConfigGroup.getInputFileURL(config.getContext(), ltzConfig.linkListPath).getPath(),
78+
links = loadLinkList(ConfigGroup.getInputFileURL(config.getContext(), ltzConfig.linkListPath).getPath(),
7779
network, ltzConfig.policyName);
78-
79-
logger.info(" Affected links: " + linkIds.size());
8080
} else {
8181
throw new IllegalStateException(
8282
"One of perimetersPath and linkListPath must be set for policy " + ltzConfig.policyName);
8383
}
8484

85-
return new DefaultPolicy(new FactorRoutingPenalty(linkIds, insideFactor, personFilter), null);
85+
logger.info(" Affected active links (penalized): " + links.active().size());
86+
logger.info(" Affected connecting links (forbidden as origin/destination): " + links.connecting().size());
87+
88+
return new DefaultPolicy(new FixedRoutingPenalty(links.active(), routingPenalty, personFilter),
89+
new LimitedTrafficZoneUtilityPenalty(utilityPenalty, links.connecting(), personFilter));
8690
}
8791

88-
private static IdSet<Link> loadLinkList(String path, Network network, String policy) {
89-
IdSet<Link> linkList = new IdSet<>(Link.class);
92+
private static PolicyLinks loadLinkList(String path, Network network, String policy) {
93+
IdSet<Link> area = new IdSet<>(Link.class);
9094

9195
try {
9296
BufferedReader reader = IOUtils.getBufferedReader(path);
@@ -103,7 +107,7 @@ private static IdSet<Link> loadLinkList(String path, Network network, String pol
103107
+ " which is not included in network");
104108
}
105109

106-
linkList.add(link.getId());
110+
area.add(link.getId());
107111
}
108112
}
109113

@@ -112,6 +116,30 @@ private static IdSet<Link> loadLinkList(String path, Network network, String pol
112116
throw new RuntimeException(e);
113117
}
114118

115-
return linkList;
119+
// Now find all links that consitute the edge
120+
IdSet<Link> active = new IdSet<>(Link.class);
121+
122+
for (Id<Link> currentId : area) {
123+
Link currentLink = network.getLinks().get(currentId);
124+
125+
Set<Id<Link>> incoming = new HashSet<>(currentLink.getFromNode().getInLinks().keySet());
126+
Set<Id<Link>> outgoing = new HashSet<>(currentLink.getToNode().getOutLinks().keySet());
127+
128+
int totalIncoming = incoming.size();
129+
int totalOutgoing = outgoing.size();
130+
131+
incoming.removeIf(area::contains);
132+
outgoing.removeIf(area::contains);
133+
134+
// this link can only be reached and only leads to the policy area, hence, it is
135+
// not an edge
136+
boolean isInternal = incoming.size() == totalIncoming && outgoing.size() == totalOutgoing;
137+
138+
if (!isInternal) {
139+
active.add(currentId);
140+
}
141+
}
142+
143+
return new PolicyLinks(active, PolicyLinkFinder.findConnectingLinks(active, network));
116144
}
117145
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.eqasim.core.simulation.policies.impl.limited_traffic_zone;
2+
3+
import java.util.List;
4+
5+
import org.eqasim.core.simulation.policies.PolicyPersonFilter;
6+
import org.eqasim.core.simulation.policies.utility.UtilityPenalty;
7+
import org.matsim.api.core.v01.Id;
8+
import org.matsim.api.core.v01.IdSet;
9+
import org.matsim.api.core.v01.network.Link;
10+
import org.matsim.api.core.v01.population.Leg;
11+
import org.matsim.api.core.v01.population.Person;
12+
import org.matsim.api.core.v01.population.PlanElement;
13+
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
14+
import org.matsim.core.population.routes.NetworkRoute;
15+
16+
public class LimitedTrafficZoneUtilityPenalty implements UtilityPenalty {
17+
private final IdSet<Link> links;
18+
private final PolicyPersonFilter personFilter;
19+
private final double penalty;
20+
21+
public LimitedTrafficZoneUtilityPenalty(double penalty, IdSet<Link> links, PolicyPersonFilter personFilter) {
22+
this.personFilter = personFilter;
23+
this.links = links;
24+
this.penalty = penalty;
25+
}
26+
27+
@Override
28+
public double calculatePenalty(String mode, Person person, DiscreteModeChoiceTrip trip,
29+
List<? extends PlanElement> elements) {
30+
if (personFilter.applies(person.getId())) {
31+
if (!links.contains(trip.getOriginActivity().getLinkId())) {
32+
if (!links.contains(trip.getDestinationActivity().getLinkId())) {
33+
// not a stopping trip
34+
boolean isCrossing = false;
35+
36+
for (PlanElement element : elements) {
37+
if (element instanceof Leg leg) {
38+
if (leg.getRoute() instanceof NetworkRoute route) {
39+
for (Id<Link> linkId : route.getLinkIds()) {
40+
if (links.contains(linkId)) {
41+
isCrossing = true;
42+
break;
43+
}
44+
}
45+
}
46+
}
47+
48+
if (isCrossing)
49+
break;
50+
}
51+
52+
if (isCrossing) {
53+
return penalty;
54+
}
55+
}
56+
}
57+
}
58+
59+
return 0.0;
60+
}
61+
}

core/src/main/java/org/eqasim/core/simulation/policies/routing/PolicyLinkFinder.java

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ public enum Predicate {
3333
Entering, Exiting, Crossing, Inside
3434
}
3535

36-
public IdSet<Link> findLinks(Network network, Predicate predicate, boolean includeConnecting) {
37-
IdSet<Link> linkIds = new IdSet<>(Link.class);
36+
public PolicyLinks findLinks(Network network, Predicate predicate) {
37+
IdSet<Link> active = new IdSet<>(Link.class);
3838

3939
for (Link link : network.getLinks().values()) {
4040
for (Geometry shape : shapes) {
@@ -48,51 +48,56 @@ public IdSet<Link> findLinks(Network network, Predicate predicate, boolean inclu
4848
boolean toInside = shape.contains(toPoint);
4949

5050
boolean isRelvant = switch (predicate) {
51-
case Exiting -> fromInside && !toInside;
52-
case Entering -> !fromInside && toInside;
53-
case Crossing -> fromInside || toInside;
54-
case Inside -> fromInside && toInside;
51+
case Exiting -> fromInside && !toInside;
52+
case Entering -> !fromInside && toInside;
53+
case Crossing -> fromInside || toInside;
54+
case Inside -> fromInside && toInside;
5555
};
5656

5757
if (isRelvant) {
58-
linkIds.add(link.getId());
58+
active.add(link.getId());
5959
}
6060
}
6161
}
6262

63-
if (includeConnecting) {
64-
boolean continueNextRound = true;
65-
while (continueNextRound) {
66-
continueNextRound = false;
63+
return new PolicyLinks(active, findConnectingLinks(active, network));
64+
}
6765

68-
for (Link link : network.getLinks().values()) {
69-
if (linkIds.contains(link.getId())) {
70-
continue; // skip tagged links
71-
}
66+
static public IdSet<Link> findConnectingLinks(IdSet<Link> active, Network network) {
67+
IdSet<Link> connecting = new IdSet<>(Link.class);
68+
connecting.addAll(active);
7269

73-
Node toNode = link.getToNode();
70+
boolean continueNextRound = true;
71+
while (continueNextRound) {
72+
continueNextRound = false;
7473

75-
boolean allConnectionsTagged = true;
76-
for (Id<Link> outlinkId : toNode.getOutLinks().keySet()) {
77-
if (outlinkId.equals(link.getId())) {
78-
continue; // skip self loops
79-
}
74+
for (Link link : network.getLinks().values()) {
75+
if (connecting.contains(link.getId())) {
76+
continue; // skip tagged links
77+
}
8078

81-
if (!linkIds.contains(outlinkId)) {
82-
allConnectionsTagged = false;
83-
break;
84-
}
79+
Node toNode = link.getToNode();
80+
81+
boolean allConnectionsTagged = true;
82+
for (Id<Link> outlinkId : toNode.getOutLinks().keySet()) {
83+
if (outlinkId.equals(link.getId())) {
84+
continue; // skip self loops
8585
}
8686

87-
if (allConnectionsTagged) {
88-
continueNextRound = true;
89-
linkIds.add(link.getId());
87+
if (!connecting.contains(outlinkId)) {
88+
allConnectionsTagged = false;
89+
break;
9090
}
9191
}
92+
93+
if (allConnectionsTagged) {
94+
continueNextRound = true;
95+
connecting.add(link.getId());
96+
}
9297
}
9398
}
9499

95-
return linkIds;
100+
return connecting;
96101
}
97102

98103
static public PolicyLinkFinder create(File path) {
@@ -117,4 +122,7 @@ static public PolicyLinkFinder create(File path) {
117122
throw new RuntimeException(e);
118123
}
119124
}
125+
126+
public static record PolicyLinks(IdSet<Link> active, IdSet<Link> connecting) {
127+
}
120128
}

0 commit comments

Comments
 (0)