Skip to content
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 @@ -46,23 +46,36 @@ public int traversalTimeSeconds (EdgeStore.Edge currentEdge, StreetMode streetMo
* TODO pull this out into an interface to allow generalization to data from generalized cost tags
*/
@Override
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode) {
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode, CongestionLevel congestionLevel) {
if (streetMode == StreetMode.CAR) {
double angle = calculateNewTurnAngle(fromEdge, toEdge);
if (angle < 27)
return STRAIGHT_ON;
return straightOnDelay(fromEdge, congestionLevel);
else if (angle < 153)
return driveOnRight ? LEFT_TURN : RIGHT_TURN;
else if (angle < 207)
return U_TURN;
else if (angle < 333)
return driveOnRight ? RIGHT_TURN : LEFT_TURN;
else
return STRAIGHT_ON;
return straightOnDelay(fromEdge, congestionLevel);
}
return 0;
}

/**
* Get the delay for driving straight over a crossing, depending on fromEdge’s StreetClass, and the time of the day
* Based on Jaakkonen (2013)
*/
private int straightOnDelay(int fromEdge, CongestionLevel congestionLevel){
EdgeStore.Edge e = layer.edgeStore.getCursor(fromEdge);
Byte streetClassCode = e.getStreetClassCode();
return CrossingPenalty.getDelay(
congestionLevel,
JaakkonenStreetClass.fromR5StreetClassCode(streetClassCode)
);
}

/**
* Gets in/out angles from edges and calculates angle between them
* @return angle in degrees from 0-360
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/com/conveyal/r5/streets/CongestionLevel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.conveyal.r5.streets;

import com.conveyal.r5.profile.ProfileRequest;

import java.time.DayOfWeek;

/**
* These congestion levels relate to Jaakkonen (2013)’s assessment of crossing penalties in the Helsinki metropolitan area
* See: <a href="http://urn.fi/URN:NBN:fi-fe2017112252365">...</a>, table 28 on page 61,
*/
public enum CongestionLevel {
RUSH_HOUR,
OFF_PEAK,
AVERAGE;

public static CongestionLevel fromFromTime(int secondsSinceMidnight) {
/*
* based on https://www.tomtom.com/traffic-index/helsinki-traffic/
*/
if (secondsSinceMidnight < 25_200) // 7:00
return CongestionLevel.OFF_PEAK;
else if (secondsSinceMidnight < 36_000) // 10:00
return CongestionLevel.RUSH_HOUR;
else if (secondsSinceMidnight < 50_400) // 14:00
return CongestionLevel.AVERAGE;
else if (secondsSinceMidnight < 64800) // 18:00
return CongestionLevel.RUSH_HOUR;
else
return CongestionLevel.OFF_PEAK;
}

public static CongestionLevel fromProfileRequest(ProfileRequest profileRequest) {
// set the congestion level (rush hour/off peak) depending on week day and time of day
if (profileRequest.date != null) {
DayOfWeek dayOfWeek = profileRequest.date.getDayOfWeek();
if (dayOfWeek.equals(DayOfWeek.SUNDAY) || dayOfWeek.equals(DayOfWeek.SATURDAY))
return CongestionLevel.OFF_PEAK;
else
return CongestionLevel.fromFromTime(profileRequest.fromTime);
} else
return CongestionLevel.AVERAGE;
}

}

48 changes: 48 additions & 0 deletions src/main/java/com/conveyal/r5/streets/CrossingPenalty.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.conveyal.r5.streets;

import java.util.HashMap;

/**
* Crossing penalties according to Jaakkonen (2013)’s analysis for the Helsinki metropolitan area
* See: <a href="http://urn.fi/URN:NBN:fi-fe2017112252365">...</a>, table 28 on page 61,
*
* Note that delays are in integer seconds, as com.conveyal.r5.streets.BasicTraversalTimeCalculator.turnTimeSeconds
* returns full seconds, only (probably a question of performance?)
*/
public class CrossingPenalty {
public static final HashMap<CongestionLevel, HashMap<JaakkonenStreetClass, Integer>> CROSSING_PENALTIES = new HashMap<>();

static {
CROSSING_PENALTIES.put(
CongestionLevel.AVERAGE, new HashMap<>() {
{
put(JaakkonenStreetClass.CLASS_1_2, 11 /*11.311*/);
put(JaakkonenStreetClass.CLASS_3, 9 /*9.439*/);
put(JaakkonenStreetClass.CLASS_4_5_6, 9 /*9.362*/);
}
}
);
CROSSING_PENALTIES.put(
CongestionLevel.OFF_PEAK, new HashMap<>() {
{
put(JaakkonenStreetClass.CLASS_1_2, 10 /*9.979*/);
put(JaakkonenStreetClass.CLASS_3, 7 /*6.650*/);
put(JaakkonenStreetClass.CLASS_4_5_6, 8 /*7.752*/);
}
}
);
CROSSING_PENALTIES.put(
CongestionLevel.RUSH_HOUR, new HashMap<>() {
{
put(JaakkonenStreetClass.CLASS_1_2, 12 /*12.195*/);
put(JaakkonenStreetClass.CLASS_3, 11 /*11.199*/);
put(JaakkonenStreetClass.CLASS_4_5_6, 11 /*10.633*/);
}
}
);
}

public static int getDelay(CongestionLevel congestionLevel, JaakkonenStreetClass jaakkonenStreetClass){
return CROSSING_PENALTIES.get(congestionLevel).get(jaakkonenStreetClass);
}
}
6 changes: 4 additions & 2 deletions src/main/java/com/conveyal/r5/streets/EdgeStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -729,13 +729,15 @@ public StreetRouter.State traverse (
// This was rounding up, now truncating ... maybe change back for consistency?
// int roundedTime = (int) Math.ceil(time);

CongestionLevel congestionLevel = CongestionLevel.fromProfileRequest(req);

int turnTimeSeconds = 0;
// Negative backEdge means this state is not the result of traversing an edge (it's the start of a search).
if (s0.backEdge >= 0) {
if (req.reverseSearch) {
turnTimeSeconds = timeCalculator.turnTimeSeconds(getEdgeIndex(), s0.backEdge, streetMode);
turnTimeSeconds = timeCalculator.turnTimeSeconds(getEdgeIndex(), s0.backEdge, streetMode, congestionLevel);
} else {
turnTimeSeconds = timeCalculator.turnTimeSeconds(s0.backEdge, getEdgeIndex(), streetMode);
turnTimeSeconds = timeCalculator.turnTimeSeconds(s0.backEdge, getEdgeIndex(), streetMode, congestionLevel);
}
}
// TODO add checks for negative increment values to these functions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public int traversalTimeSeconds (EdgeStore.Edge currentEdge, StreetMode streetMo
}

@Override
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode) {
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode, CongestionLevel congestionLevel) {
if (streetMode == StreetMode.WALK) {
return walkTraversalTimes.turnTimeSeconds(fromEdge, toEdge);
} else if (streetMode == StreetMode.BICYCLE) {
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/com/conveyal/r5/streets/JaakkonenStreetClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.conveyal.r5.streets;

import com.conveyal.r5.labeling.StreetClass;

/**
* Translates the com.conveyal.r5.labeling.StreetClass (OSM tags) into Jaakkonen (2013)’s street classes,
* which are based on ‘functional classes’ in the DigiRoad’s road classification,
* see <a href="https://ava.vaylapilvi.fi/ava/Tie/Digiroad/Aineistojulkaisut/latest/Julkaisudokumentit">...</a>
* and
*/
public enum JaakkonenStreetClass {
CLASS_1_2,
CLASS_3,
CLASS_4_5_6;

public static JaakkonenStreetClass fromR5StreetClassCode(Byte streetClassCode) {
if(streetClassCode.equals(StreetClass.MOTORWAY.code) || streetClassCode.equals(StreetClass.PRIMARY.code)){
return JaakkonenStreetClass.CLASS_1_2;
} else if (streetClassCode.equals(StreetClass.SECONDARY.code)){
return JaakkonenStreetClass.CLASS_3;
} else if (streetClassCode.equals(StreetClass.TERTIARY.code) || streetClassCode.equals(StreetClass.OTHER.code)){
return JaakkonenStreetClass.CLASS_4_5_6;
} else { // catch-all, not really necessary here?
return JaakkonenStreetClass.CLASS_4_5_6;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public int traversalTimeSeconds (EdgeStore.Edge currentEdge, StreetMode streetMo
}

@Override
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode) {
return base.turnTimeSeconds(fromEdge, toEdge, streetMode);
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode, CongestionLevel congestionLevel) {
return base.turnTimeSeconds(fromEdge, toEdge, streetMode, congestionLevel);
}

}
6 changes: 4 additions & 2 deletions src/main/java/com/conveyal/r5/streets/StreetRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,8 @@ public State getState (Split split) {
// Start on the forward edge of the pair that was split
EdgeStore.Edge e = streetLayer.edgeStore.getCursor(split.edge);

CongestionLevel congestionLevel = CongestionLevel.fromProfileRequest(profileRequest);

TIntList edgeList;
if (profileRequest.reverseSearch) {
edgeList = streetLayer.outgoingEdges.get(split.vertex1);
Expand All @@ -769,7 +771,7 @@ public State getState (Split split) {
ret.streetMode = s.streetMode;

// figure out the turn cost
int turnCost = this.timeCalculator.turnTimeSeconds(s.backEdge, split.edge, s.streetMode);
int turnCost = this.timeCalculator.turnTimeSeconds(s.backEdge, split.edge, s.streetMode, congestionLevel);
int traversalCost = (int) Math.round(split.distance0_mm / 1000d / e.calculateSpeed(profileRequest, s.streetMode));

// TODO length of perpendicular
Expand Down Expand Up @@ -798,7 +800,7 @@ public State getState (Split split) {
}
State ret = new State(-1, split.edge + 1, state);
ret.streetMode = state.streetMode;
int turnCost = this.timeCalculator.turnTimeSeconds(state.backEdge, split.edge + 1, state.streetMode);
int turnCost = this.timeCalculator.turnTimeSeconds(state.backEdge, split.edge + 1, state.streetMode, congestionLevel);
int traversalCost = (int) Math.round(split.distance1_mm / 1000d / e.calculateSpeed(profileRequest, state.streetMode));
ret.distance += split.distance1_mm;
// TODO length of perpendicular
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public interface TraversalTimeCalculator extends Serializable {
*
* @return the expected value of the time in seconds to make the turn.
*/
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode);
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode, CongestionLevel congestionLevel);


}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void testAngleSouthernHemisphere() throws Exception {
public void testCost () throws Exception {
setUp(false);
BasicTraversalTimeCalculator calculator = new BasicTraversalTimeCalculator(streetLayer, true);
assertEquals(calculator.LEFT_TURN, calculator.turnTimeSeconds(ee + 1, es, StreetMode.CAR));
assertEquals(calculator.LEFT_TURN, calculator.turnTimeSeconds(ee + 1, es, StreetMode.CAR, CongestionLevel.OFF_PEAK));
}

/**
Expand All @@ -58,4 +58,4 @@ public void testJtsAngle () {
double a1 = Angle.angle(new Coordinate(10, 10), new Coordinate(9, 9));
assertTrue(a1 < a0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public int traversalTimeSeconds (EdgeStore.Edge currentEdge, StreetMode streetMo
}

@Override
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode) {
public int turnTimeSeconds (int fromEdge, int toEdge, StreetMode streetMode, CongestionLevel congestionLevel) {
return 0;
}
};
Expand Down