Skip to content

Commit

Permalink
use qualified modes (BICYCLE_RENT) in profile routing
Browse files Browse the repository at this point in the history
This also reduces the qualified mode set sequence class to
a single set of qualified modes. We'll probably be specifying the
access, egress, and transit mode sets separately (rather than as an
ordered list) even in regular routing since we've moved to "long distance"
mode.
  • Loading branch information
abyrd committed Mar 17, 2015
1 parent 4b7079b commit 94fa2eb
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 167 deletions.
Expand Up @@ -15,7 +15,6 @@ the License, or (at your option) any later version.

import java.util.*;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
Expand All @@ -25,7 +24,7 @@ the License, or (at your option) any later version.
import javax.xml.datatype.XMLGregorianCalendar;

import org.onebusaway.gtfs.model.AgencyAndId;
import org.opentripplanner.api.parameter.QualifiedModeSetSequence;
import org.opentripplanner.api.parameter.QualifiedModeSet;
import org.opentripplanner.routing.core.OptimizeType;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.request.BannedStopSet;
Expand Down Expand Up @@ -166,7 +165,7 @@ public abstract class RoutingResource {

/** The set of modes that a user is willing to use, with qualifiers stating whether vehicles should be parked, rented, etc. */
@QueryParam("mode")
protected QualifiedModeSetSequence modes;
protected QualifiedModeSet modes;

/** The minimum time, in seconds, between successive trips on different vehicles.
* This is designed to allow for imperfect schedule adherence. This is a minimum;
Expand Down Expand Up @@ -513,7 +512,7 @@ protected RoutingRequest buildRequest() throws ParameterException {
request.setOptimize(optimize);

/* Temporary code to get bike/car parking and renting working. */
if (modes != null) modes.applyToRequest(request);
if (modes != null) modes.applyToRoutingRequest(request);

if (request.allowBikeRental && bikeSpeed == null) {
//slower bike speed for bike sharing, based on empirical evidence from DC.
Expand Down
39 changes: 35 additions & 4 deletions src/main/java/org/opentripplanner/api/parameter/QualifiedMode.java
@@ -1,11 +1,11 @@
package org.opentripplanner.api.parameter;

import java.security.InvalidParameterException;
import java.util.Set;

import com.beust.jcommander.internal.Sets;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.TraverseMode;

import com.beust.jcommander.internal.Sets;
import java.security.InvalidParameterException;
import java.util.Set;

public class QualifiedMode {

Expand All @@ -27,7 +27,38 @@ public QualifiedMode(String qMode) {
}
}
}

public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(mode);
for (Qualifier qualifier : qualifiers) {
sb.append("_");
sb.append(qualifier);
}
return sb.toString();
}

public void applyToRoutingRequest (RoutingRequest req, boolean usingTransit) {
req.modes.setMode(this.mode, true);
if (this.mode == TraverseMode.BICYCLE) {
if (this.qualifiers.contains(Qualifier.RENT)) {
req.modes.setMode(TraverseMode.WALK, true); // turn on WALK for bike rental mode
req.allowBikeRental = true;
}
if (usingTransit) {
req.bikeParkAndRide = this.qualifiers.contains(Qualifier.PARK);
}
}
if (usingTransit && this.mode == TraverseMode.CAR) {
if (this.qualifiers.contains(Qualifier.PARK)) {
req.parkAndRide = true;
} else {
req.kissAndRide = true;
}
req.modes.setWalk(true); // need to walk after dropping the car off
}
}

}

enum Qualifier {
Expand Down
@@ -0,0 +1,66 @@
package org.opentripplanner.api.parameter;

import com.beust.jcommander.internal.Sets;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.TraverseModeSet;

import java.util.Set;

/**
* A set of qualified modes. The original intent was to allow a sequence of mode sets, but the shift to "long distance
* mode" routing means that it will make more sense to specify access, egress, and transit modes in separate parameters.
* So now this only contains one mode set rather than a sequence of them.
*
* This class and QualifiedMode are clearly somewhat inefficient and allow nonsensical combinations like
* renting and parking a subway. They are not intended for use in routing. Rather, they simply parse the
* language of mode specifications that may be given in the mode query parameter. They are then converted
* into more efficient and useful representation in the routing request.
*/
public class QualifiedModeSet {

public Set<QualifiedMode> qModes = Sets.newHashSet();

public QualifiedModeSet(String s) {
for (String qMode : s.split(",")) {
qModes.add(new QualifiedMode(qMode));
}
}

/**
* Modify an existing routing request, setting fields to reflect these qualified modes.
* This is intended as a temporary solution, and uses the current system of a single mode set,
* accompanied by some flags to help with routing.
*/
public void applyToRoutingRequest(RoutingRequest req) {

if (qModes.isEmpty()) return;

/* Start with an empty mode set. */
TraverseModeSet modes = new TraverseModeSet();
req.setModes(modes);

/* First, copy over all the unqualified modes and see if we are using transit. FIXME HACK */
for (QualifiedMode qMode : qModes) {
modes.setMode(qMode.mode, true);
}
boolean usingTransit = modes.isTransit();

// We used to always set WALK to true, but this forced walking when someone wanted to use a bike.
// We also want it to be possible to force biking-only (e.g. this is done in some consistency tests).
// TODO clearly define mode semantics: does presence of mode mean it is allowable, preferred... ?

for (QualifiedMode qMode : qModes) {
qMode.applyToRoutingRequest(req, usingTransit);
}
}

public String toString() {
StringBuilder sb = new StringBuilder();
for (QualifiedMode qm : qModes) {
sb.append(qm.toString());
sb.append(" ");
}
return sb.toString();
}

}

This file was deleted.

29 changes: 11 additions & 18 deletions src/main/java/org/opentripplanner/api/resource/ProfileResource.java
@@ -1,35 +1,28 @@
package org.opentripplanner.api.resource;

import java.util.List;
import java.util.Map;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import com.beust.jcommander.internal.Maps;
import org.opentripplanner.analyst.SurfaceCache;
import org.opentripplanner.analyst.TimeSurface;
import org.opentripplanner.api.param.HourMinuteSecond;
import org.opentripplanner.api.param.LatLon;
import org.opentripplanner.api.param.QueryParameter;
import org.opentripplanner.api.param.YearMonthDay;
import org.opentripplanner.api.parameter.QualifiedModeSet;
import org.opentripplanner.profile.*;
import org.opentripplanner.routing.core.TraverseModeSet;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.standalone.OTPServer;
import org.opentripplanner.standalone.Router;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.util.Map;

/**
* A Jersey resource class which exposes OTP profile routing functionality as a web service.
*
Expand Down Expand Up @@ -68,9 +61,9 @@ public Response profileRoute (
@QueryParam("orderBy") @DefaultValue("AVG") Option.SortOrder orderBy,
@QueryParam("limit") @DefaultValue("10") int limit, // max options to return PER ACCESS MODE
@QueryParam("suboptimal") @DefaultValue("5") int suboptimalMinutes,
@QueryParam("accessModes") @DefaultValue("WALK,BICYCLE") TraverseModeSet accessModes,
@QueryParam("egressModes") @DefaultValue("WALK") TraverseModeSet egressModes,
@QueryParam("directModes") @DefaultValue("WALK,BICYCLE") TraverseModeSet directModes,
@QueryParam("accessModes") @DefaultValue("WALK,BICYCLE") QualifiedModeSet accessModes,
@QueryParam("egressModes") @DefaultValue("WALK") QualifiedModeSet egressModes,
@QueryParam("directModes") @DefaultValue("WALK,BICYCLE") QualifiedModeSet directModes,
@QueryParam("transitModes") @DefaultValue("TRANSIT") TraverseModeSet transitModes)
throws Exception {

Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/opentripplanner/profile/ProfileRequest.java
@@ -1,7 +1,8 @@
package org.opentripplanner.profile;

import org.joda.time.LocalDate;
import org.opentripplanner.api.parameter.QualifiedModeSetSequence;
import org.opentripplanner.api.parameter.QualifiedModeSet;
import org.opentripplanner.routing.core.TraverseModeSet;

import java.io.Serializable;

Expand Down Expand Up @@ -34,7 +35,8 @@ public class ProfileRequest implements Serializable, Cloneable {
public LocalDate date;
public Option.SortOrder orderBy;
public int limit; // the maximum number of options presented PER ACCESS MODE
public QualifiedModeSetSequence accessModes, egressModes, directModes, transitModes;
public QualifiedModeSet accessModes, egressModes, directModes;
public TraverseModeSet transitModes;
public boolean analyst = false; // if true, propagate travel times out to street network

/*
Expand Down
@@ -1,7 +1,5 @@
package org.opentripplanner.profile;

import java.util.*;

import com.beust.jcommander.internal.Sets;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
Expand All @@ -10,6 +8,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

// Jackson will serialize fields with getters, or @JsonProperty annotations.
public class ProfileResponse {

Expand Down Expand Up @@ -49,11 +49,11 @@ public ProfileResponse (Collection<Option> allOptions, Option.SortOrder orderBy,
Collections.sort(transitOptions, c);
// Group options by access mode, retaining ordering.
// ListMultimap can hold duplicate key-value pairs and maintains the insertion ordering of values for a given key.
// TODO update this to also use the egress mode in the key
// TODO update this to also use the egress mode in the key, and to consider the qualifiers on the modes
ListMultimap<TraverseMode, Option> transitOptionsByAccessMode = ArrayListMultimap.create();
for (Option option : transitOptions) {
for (StreetSegment segment : option.access) {
transitOptionsByAccessMode.put(segment.mode, option);
transitOptionsByAccessMode.put(segment.qmode.mode, option);
}
}
// Retain the top N transit options for each access mode. Duplicates may be present, but options is a Set.
Expand Down

0 comments on commit 94fa2eb

Please sign in to comment.