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 group priority to direct search #5945

Merged
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 @@ -41,14 +41,15 @@
import org.opentripplanner.street.search.request.StreetSearchRequest;
import org.opentripplanner.street.search.state.State;
import org.opentripplanner.transit.model.framework.FeedScopedId;
import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService;
import org.opentripplanner.transit.model.site.AreaStop;
import org.opentripplanner.transit.service.DefaultTransitService;
import org.opentripplanner.transit.service.TransitModel;

/**
* This tests that the feed for the Cobb County Flex service is processed correctly. This service
* contains both flex zones but also scheduled stops. Inside the zone passengers can get on or off
* anywhere so there it works more like a taxi.
* contains both flex zones but also scheduled stops. Inside the zone, passengers can get on or off
* anywhere, so there it works more like a taxi.
* <p>
* Read about the details at: https://www.cobbcounty.org/transportation/cobblinc/routes-and-schedules/flex
*/
Expand Down Expand Up @@ -212,6 +213,7 @@ private static List<Itinerary> getItineraries(
var result = TransitRouter.route(
request,
serverContext,
TransitGroupPriorityService.empty(),
transitStartOfTime,
additionalSearchDays,
new DebugTimingAggregator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Collectors;
import org.opentripplanner.routing.algorithm.raptoradapter.transit.request.PriorityGroupConfigurator;
import org.opentripplanner.routing.api.request.request.TransitRequest;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService;

/**
* This class is used to report all transit-groups used for transit-group-priority. The report is
Expand All @@ -17,14 +17,14 @@
public class TransitGroupPriorityReport {

public static String build(Collection<TripPattern> patterns, TransitRequest request) {
var c = PriorityGroupConfigurator.of(
var service = new TransitGroupPriorityService(
request.priorityGroupsByAgency(),
request.priorityGroupsGlobal()
);

var map = new TreeMap<Integer, DebugEntity>();
for (var it : patterns) {
int groupId = c.lookupTransitGroupPriorityId(it);
int groupId = service.lookupTransitGroupPriorityId(it);
var de = map.computeIfAbsent(groupId, DebugEntity::new);
de.add(
it.getRoute().getAgency().getId().toString(),
Expand Down
203 changes: 99 additions & 104 deletions src/main/java/org/opentripplanner/model/plan/Itinerary.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,11 +177,6 @@ public Leg lastLeg() {
return getLegs().get(getLegs().size() - 1);
}

/** Get the first transit leg if one exist */
public Optional<Leg> firstTransitLeg() {
return getLegs().stream().filter(TransitLeg.class::isInstance).findFirst();
}

/**
* An itinerary can be flagged for removal with a system notice.
* <p>
Expand Down Expand Up @@ -225,105 +220,6 @@ public Itinerary withTimeShiftToStartAt(ZonedDateTime afterTime) {
return newItin;
}

/** @see #equals(Object) */
@Override
public final int hashCode() {
return super.hashCode();
}

/**
* Return {@code true} it the other object is the same object using the {@link
* Object#equals(Object)}. An itinerary is a temporary object and the equals method should not be
* used for comparision of 2 instances, only to check that to objects are the same instance.
*/
@Override
public final boolean equals(Object o) {
return super.equals(o);
}

/**
* Used to convert a list of itineraries to a SHORT human-readable string.
*
* @see #toStr()
* <p>
* It is great for comparing lists of itineraries in a test: {@code
* assertEquals(toStr(List.of(it1)), toStr(result))}.
*/
public static String toStr(List<Itinerary> list) {
return list.stream().map(Itinerary::toStr).collect(Collectors.joining(", "));
}

@Override
public String toString() {
return ToStringBuilder
.of(Itinerary.class)
.addStr("from", firstLeg().getFrom().toStringShort())
.addStr("to", lastLeg().getTo().toStringShort())
.addTime("start", firstLeg().getStartTime())
.addTime("end", lastLeg().getEndTime())
.addNum("nTransfers", numberOfTransfers)
.addDuration("duration", duration)
.addDuration("nonTransitTime", nonTransitDuration)
.addDuration("transitTime", transitDuration)
.addDuration("waitingTime", waitingDuration)
.addNum("generalizedCost", generalizedCost, UNKNOWN)
.addNum("generalizedCost2", generalizedCost2)
.addNum("waitTimeOptimizedCost", waitTimeOptimizedCost, UNKNOWN)
.addNum("transferPriorityCost", transferPriorityCost, UNKNOWN)
.addNum("nonTransitDistance", nonTransitDistanceMeters, "m")
.addBool("tooSloped", tooSloped)
.addNum("elevationLost", elevationLost, 0.0)
.addNum("elevationGained", elevationGained, 0.0)
.addCol("legs", legs)
.addObj("fare", fare)
.addObj("emissionsPerPerson", emissionsPerPerson)
.toString();
}

/**
* Used to convert an itinerary to a SHORT human readable string - including just a few of the
* most important fields. It is much shorter and easier to read then the {@link
* Itinerary#toString()}.
* <p>
* It is great for comparing to itineraries in a test: {@code assertEquals(toStr(it1),
* toStr(it2))}.
* <p>
* Example: {@code A ~ Walk 2m ~ B ~ BUS 55 12:04 12:14 ~ C [cost: 1066]}
* <p>
* Reads: Start at A, walk 2 minutes to stop B, take bus 55, board at 12:04 and alight at 12:14
* ...
*/
public String toStr() {
// No translater needed, stop indexes are never passed to the builder
PathStringBuilder buf = new PathStringBuilder(null);
buf.stop(firstLeg().getFrom().name.toString());

for (Leg leg : legs) {
if (leg.isWalkingLeg()) {
buf.walk((int) leg.getDuration().toSeconds());
} else if (leg instanceof TransitLeg transitLeg) {
buf.transit(
transitLeg.getMode().name(),
transitLeg.getTrip().logName(),
transitLeg.getStartTime(),
transitLeg.getEndTime()
);
} else if (leg instanceof StreetLeg streetLeg) {
buf.street(streetLeg.getMode().name(), leg.getStartTime(), leg.getEndTime());
}
buf.stop(leg.getTo().name.toString());
}

// The generalizedCost2 is printed as is, it is a special cost and the scale depends on the
// use-case.
buf.summary(
RaptorCostConverter.toRaptorCost(generalizedCost),
getGeneralizedCost2().orElse(RaptorConstants.NOT_SET)
);

return buf.toString();
}

/** Total duration of the itinerary in seconds */
public Duration getDuration() {
return duration;
Expand Down Expand Up @@ -698,6 +594,105 @@ public Duration walkDuration() {
return walkDuration;
}

/** @see #equals(Object) */
@Override
public final int hashCode() {
return super.hashCode();
}

/**
* Return {@code true} it the other object is the same object using the {@link
* Object#equals(Object)}. An itinerary is a temporary object and the equals method should not be
* used for comparision of 2 instances, only to check that to objects are the same instance.
*/
@Override
public final boolean equals(Object o) {
return super.equals(o);
}

@Override
public String toString() {
return ToStringBuilder
.of(Itinerary.class)
.addStr("from", firstLeg().getFrom().toStringShort())
.addStr("to", lastLeg().getTo().toStringShort())
.addTime("start", firstLeg().getStartTime())
.addTime("end", lastLeg().getEndTime())
.addNum("nTransfers", numberOfTransfers)
.addDuration("duration", duration)
.addDuration("nonTransitTime", nonTransitDuration)
.addDuration("transitTime", transitDuration)
.addDuration("waitingTime", waitingDuration)
.addNum("generalizedCost", generalizedCost, UNKNOWN)
.addNum("generalizedCost2", generalizedCost2)
.addNum("waitTimeOptimizedCost", waitTimeOptimizedCost, UNKNOWN)
.addNum("transferPriorityCost", transferPriorityCost, UNKNOWN)
.addNum("nonTransitDistance", nonTransitDistanceMeters, "m")
.addBool("tooSloped", tooSloped)
.addNum("elevationLost", elevationLost, 0.0)
.addNum("elevationGained", elevationGained, 0.0)
.addCol("legs", legs)
.addObj("fare", fare)
.addObj("emissionsPerPerson", emissionsPerPerson)
.toString();
}

/**
* Used to convert a list of itineraries to a SHORT human-readable string.
*
* @see #toStr()
* <p>
* It is great for comparing lists of itineraries in a test: {@code
* assertEquals(toStr(List.of(it1)), toStr(result))}.
*/
public static String toStr(List<Itinerary> list) {
return list.stream().map(Itinerary::toStr).collect(Collectors.joining(", "));
}

/**
* Used to convert an itinerary to a SHORT human readable string - including just a few of the
* most important fields. It is much shorter and easier to read then the {@link
* Itinerary#toString()}.
* <p>
* It is great for comparing to itineraries in a test: {@code assertEquals(toStr(it1),
* toStr(it2))}.
* <p>
* Example: {@code A ~ Walk 2m ~ B ~ BUS 55 12:04 12:14 ~ C [cost: 1066]}
* <p>
* Reads: Start at A, walk 2 minutes to stop B, take bus 55, board at 12:04 and alight at 12:14
* ...
*/
public String toStr() {
// No translater needed, stop indexes are never passed to the builder
PathStringBuilder buf = new PathStringBuilder(null);
buf.stop(firstLeg().getFrom().name.toString());

for (Leg leg : legs) {
if (leg.isWalkingLeg()) {
buf.walk((int) leg.getDuration().toSeconds());
} else if (leg instanceof TransitLeg transitLeg) {
buf.transit(
transitLeg.getMode().name(),
transitLeg.getTrip().logName(),
transitLeg.getStartTime(),
transitLeg.getEndTime()
);
} else if (leg instanceof StreetLeg streetLeg) {
buf.street(streetLeg.getMode().name(), leg.getStartTime(), leg.getEndTime());
}
buf.stop(leg.getTo().name.toString());
}

// The generalizedCost2 is printed as is, it is a special cost and the scale depends on the
// use-case.
buf.summary(
RaptorCostConverter.toRaptorCost(generalizedCost),
getGeneralizedCost2().orElse(RaptorConstants.NOT_SET)
);

return buf.toString();
}

private static int penaltyCost(TimeAndCost penalty) {
return penalty.cost().toSeconds();
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/opentripplanner/model/plan/Leg.java
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ default Route getRoute() {
/**
* For transit legs, the trip. For non-transit legs, null.
*/
@Nullable
default Trip getTrip() {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.opentripplanner.model.plan.grouppriority;

import java.util.Collection;
import org.opentripplanner.model.plan.Itinerary;
import org.opentripplanner.model.plan.Leg;
import org.opentripplanner.raptor.api.request.RaptorTransitGroupPriorityCalculator;
import org.opentripplanner.transit.model.network.grouppriority.DefaultTransitGroupPriorityCalculator;
import org.opentripplanner.transit.model.network.grouppriority.TransitGroupPriorityService;

/**
* This class will set the {@link Itinerary#getGeneralizedCost2()} value if the feature is
* enabled and no such value is set. The AStar router does not produce itineraries with this,
* so we decorate itineraries with this here to make sure the `c2` is set correct and can be
* used in the itinerary-filter-chain.
*/
public class TransitGroupPriorityItineraryDecorator {

private final TransitGroupPriorityService priorityGroupConfigurator;
private final RaptorTransitGroupPriorityCalculator transitGroupCalculator;

public TransitGroupPriorityItineraryDecorator(
TransitGroupPriorityService priorityGroupConfigurator
) {
this.priorityGroupConfigurator = priorityGroupConfigurator;
this.transitGroupCalculator = new DefaultTransitGroupPriorityCalculator();
}

public void decorate(Collection<Itinerary> itineraries) {
if (!priorityGroupConfigurator.isEnabled()) {
return;
}
for (Itinerary it : itineraries) {
decorate(it);
}
}

public void decorate(Itinerary itinerary) {
if (itinerary.getGeneralizedCost2().isEmpty() && priorityGroupConfigurator.isEnabled()) {
int c2 = priorityGroupConfigurator.baseGroupId();
for (Leg leg : itinerary.getLegs()) {
if (leg.getTrip() != null) {
int newGroupId = priorityGroupConfigurator.lookupTransitGroupPriorityId(leg.getTrip());
c2 = transitGroupCalculator.mergeGroupIds(c2, newGroupId);
}
}
itinerary.setGeneralizedCost2(c2);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class MultiCriteriaRequest<T extends RaptorTripSchedule> {
private final RelaxFunction relaxC1;

@Nullable
private final RaptorTransitGroupCalculator transitPriorityCalculator;
private final RaptorTransitGroupPriorityCalculator transitPriorityCalculator;

private final List<PassThroughPoint> passThroughPoints;

Expand Down Expand Up @@ -63,7 +63,7 @@ public RelaxFunction relaxC1() {
return relaxC1;
}

public Optional<RaptorTransitGroupCalculator> transitPriorityCalculator() {
public Optional<RaptorTransitGroupPriorityCalculator> transitPriorityCalculator() {
return Optional.ofNullable(transitPriorityCalculator);
}

Expand Down Expand Up @@ -140,7 +140,7 @@ public static class Builder<T extends RaptorTripSchedule> {

private final MultiCriteriaRequest<T> original;
private RelaxFunction relaxC1;
private RaptorTransitGroupCalculator transitPriorityCalculator;
private RaptorTransitGroupPriorityCalculator transitPriorityCalculator;
private List<PassThroughPoint> passThroughPoints;
private Double relaxCostAtDestination;

Expand All @@ -163,11 +163,11 @@ public Builder<T> withRelaxC1(RelaxFunction relaxC1) {
}

@Nullable
public RaptorTransitGroupCalculator transitPriorityCalculator() {
public RaptorTransitGroupPriorityCalculator transitPriorityCalculator() {
return transitPriorityCalculator;
}

public Builder<T> withTransitPriorityCalculator(RaptorTransitGroupCalculator value) {
public Builder<T> withTransitPriorityCalculator(RaptorTransitGroupPriorityCalculator value) {
transitPriorityCalculator = value;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import org.opentripplanner.raptor.api.model.DominanceFunction;

public interface RaptorTransitGroupCalculator {
public interface RaptorTransitGroupPriorityCalculator {
/**
* Merge in the transit group id with an existing set. Note! Both the set
* and the group id type is {@code int}.
Expand Down
Loading
Loading