/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.graphhopper;
import com.graphhopper.reader.gtfs.GraphHopperGtfs;
import com.graphhopper.reader.gtfs.GtfsStorage;
import com.graphhopper.reader.gtfs.PtFlagEncoder;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.storage.GHDirectory;
import com.graphhopper.storage.GraphHopperStorage;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.util.Helper;
import com.graphhopper.util.Instruction;
import com.graphhopper.util.Parameters;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.graphhopper.reader.gtfs.GtfsHelper.time;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
public class GraphHopperGtfsIT {
private static final String GRAPH_LOC = "target/GraphHopperGtfsIT";
private static GraphHopperGtfs graphHopper;
private static final ZoneId zoneId = ZoneId.of("America/Los_Angeles");
private static GraphHopperStorage graphHopperStorage;
private static LocationIndex locationIndex;
@BeforeClass
public static void init() {
Helper.removeDir(new File(GRAPH_LOC));
final PtFlagEncoder ptFlagEncoder = new PtFlagEncoder();
EncodingManager encodingManager = new EncodingManager(Arrays.asList(ptFlagEncoder), 8);
GHDirectory directory = GraphHopperGtfs.createGHDirectory(GRAPH_LOC);
GtfsStorage gtfsStorage = GraphHopperGtfs.createGtfsStorage();
graphHopperStorage = GraphHopperGtfs.createOrLoad(directory, encodingManager, ptFlagEncoder, gtfsStorage, false, Collections.singleton("files/sample-feed.zip"), Collections.emptyList());
locationIndex = GraphHopperGtfs.createOrLoadIndex(directory, graphHopperStorage);
graphHopper = GraphHopperGtfs.createFactory(ptFlagEncoder, GraphHopperGtfs.createTranslationMap(), graphHopperStorage, locationIndex, gtfsStorage)
.createWithoutRealtimeFeed();
}
@AfterClass
public static void close() {
graphHopperStorage.close();
locationIndex.close();
}
@Test
public void testRoute1() {
final double FROM_LAT = 36.914893, FROM_LON = -116.76821; // NADAV stop
final double TO_LAT = 36.914944, TO_LON = -116.761472; // NANAA stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,0,0,0).atZone(zoneId).toInstant());
GHResponse route = graphHopper.route(ghRequest);
assertFalse(route.hasErrors());
assertEquals(1, route.getAll().size());
assertEquals("Expected travel time == scheduled arrival time", time(6, 49), route.getBest().getTime(), 0.1);
}
@Test
public void testRoute1DoesNotGoAt654() {
final double FROM_LAT = 36.914893, FROM_LON = -116.76821; // NADAV stop
final double TO_LAT = 36.914944, TO_LON = -116.761472; // NANAA stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,6,54).atZone(zoneId).toInstant());
GHResponse route = graphHopper.route(ghRequest);
assertFalse(route.hasErrors());
assertEquals(1, route.getAll().size());
assertEquals("Expected travel time == scheduled arrival time", time(0, 25), route.getBest().getTime(), 0.1);
}
@Test
public void testRoute1GoesAt744() {
final double FROM_LAT = 36.914893, FROM_LON = -116.76821; // NADAV stop
final double TO_LAT = 36.914944, TO_LON = -116.761472; // NANAA stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,7,44).atZone(zoneId).toInstant());
ghRequest.getHints().put(Parameters.PT.IGNORE_TRANSFERS, "true");
GHResponse response = graphHopper.route(ghRequest);
assertEquals(1, response.getAll().size());
assertEquals("Expected travel time == scheduled arrival time", time(0, 5), response.getBest().getTime(), 0.1);
}
@Test
public void testRoute1ArriveBy() {
final double FROM_LAT = 36.914893, FROM_LON = -116.76821; // NADAV stop
final double TO_LAT = 36.914944, TO_LON = -116.761472; // NANAA stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,6, 49).atZone(zoneId).toInstant());
ghRequest.getHints().put(Parameters.PT.ARRIVE_BY, true);
GHResponse route = graphHopper.route(ghRequest);
assertFalse(route.hasErrors());
assertEquals(1, route.getAll().size());
assertEquals("Expected travel time == scheduled travel time", time(0, 5), route.getBest().getTime(), 0.1);
}
@Test
public void testRoute1ProfileEarliestArrival() {
final double FROM_LAT = 36.914893, FROM_LON = -116.76821; // NADAV stop
final double TO_LAT = 36.914944, TO_LON = -116.761472; // NANAA stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,0,0).atZone(zoneId).toInstant());
ghRequest.getHints().put(Parameters.PT.RANGE_QUERY_END_TIME, LocalDateTime.of(2007,1,1,13,0).atZone(zoneId).toInstant());
ghRequest.getHints().put(Parameters.PT.IGNORE_TRANSFERS, "true");
GHResponse response = graphHopper.route(ghRequest);
List<LocalTime> actualDepartureTimes = response.getAll().stream()
.map(path -> LocalTime.from(((Trip.PtLeg) path.getLegs().get(0)).departureTime.toInstant().atZone(zoneId)))
.collect(Collectors.toList());
List<LocalTime> expectedDepartureTimes = Stream.of(
"06:44", "07:14", "07:44", "08:14", "08:44", "08:54", "09:04", "09:14", "09:24", "09:34", "09:44", "09:54",
"10:04", "10:14", "10:24", "10:34", "10:44", "11:14", "11:44", "12:14", "12:44")
.map(LocalTime::parse)
.collect(Collectors.toList());
assertEquals(expectedDepartureTimes, actualDepartureTimes);
}
@Test
public void testRoute1ProfileLatestDeparture() {
final double FROM_LAT = 36.914893, FROM_LON = -116.76821; // NADAV stop
final double TO_LAT = 36.914944, TO_LON = -116.761472; // NANAA stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,2,13,0).atZone(zoneId).toInstant());
ghRequest.getHints().put(Parameters.PT.ARRIVE_BY, "true");
ghRequest.getHints().put(Parameters.PT.RANGE_QUERY_END_TIME, LocalDateTime.of(2007,1,2,11,0).atZone(zoneId).toInstant());
// TODO: Find the problem with 1.1.2007
ghRequest.getHints().put(Parameters.PT.IGNORE_TRANSFERS, "true");
GHResponse response = graphHopper.route(ghRequest);
List<LocalTime> actualDepartureTimes = response.getAll().stream()
.map(path -> LocalTime.from(((Trip.PtLeg) path.getLegs().get(0)).departureTime.toInstant().atZone(zoneId)))
.collect(Collectors.toList());
List<LocalTime> expectedDepartureTimes = Stream.of(
"12:44", "12:14", "11:44", "11:14", "10:44")
.map(LocalTime::parse)
.collect(Collectors.toList());
assertEquals(expectedDepartureTimes, actualDepartureTimes);
}
@Test
public void testRoute2() {
final double FROM_LAT = 36.914894, FROM_LON = -116.76821; // NADAV stop
final double TO_LAT = 36.909489, TO_LON = -116.768242; // DADAN stop
assertTravelTimeIs(graphHopper, FROM_LAT, FROM_LON, TO_LAT, TO_LON, time(6, 19));
}
@Test
public void testRoute3() {
final double FROM_LAT = 36.915682, FROM_LON = -116.751677; // STAGECOACH stop
final double TO_LAT = 36.914944, TO_LON = -116.761472; // NANAA stop
assertTravelTimeIs(graphHopper, FROM_LAT, FROM_LON, TO_LAT, TO_LON, time(6, 5));
}
@Test
public void testRoute4() {
final double FROM_LAT = 36.915682, FROM_LON = -116.751677; // STAGECOACH stop
final double TO_LAT = 36.914894, TO_LON = -116.76821; // NADAV stop
assertTravelTimeIs(graphHopper, FROM_LAT, FROM_LON, TO_LAT, TO_LON, time(6, 12));
}
@Test
public void testRoute5() {
final double FROM_LAT = 36.915682, FROM_LON = -116.751677; // STAGECOACH stop
final double TO_LAT = 36.88108, TO_LON = -116.81797; // BULLFROG stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,0,0).atZone(zoneId).toInstant());
GHResponse route = graphHopper.route(ghRequest);
assertFalse(route.hasErrors());
assertFalse(route.getAll().isEmpty());
assertEquals("Expected travel time == scheduled travel time", time(8, 10), route.getBest().getTime(), 0.1);
assertEquals("Using expected route", "STBA", (((Trip.PtLeg) route.getBest().getLegs().get(0)).tripId));
assertEquals("Using expected route", "AB1", (((Trip.PtLeg) route.getBest().getLegs().get(1)).tripId));
assertEquals("Paid expected fare", 250, route.getBest().getFare().multiply(BigDecimal.valueOf(100)).intValue()); // Two legs, no transfers allowed. Need two 'p' tickets costing 125 cents each.
}
@Test
public void testRoute6() {
final double FROM_LAT = 36.7, FROM_LON = -116.5; // HASNOROUTES stop
final double TO_LAT = 36.914894, TO_LON = -116.76821; // NADAV stop
assertNoRoute(graphHopper, FROM_LAT, FROM_LON, TO_LAT, TO_LON);
}
@Test
public void testRouteWithLaterDepartureTime() {
final double FROM_LAT = 36.915682, FROM_LON = -116.751677; // STAGECOACH stop
final double TO_LAT = 36.914894, TO_LON = -116.76821; // NADAV stop
// Missed the bus at 10 by one minute, will have to use the 10:30 one.
assertTravelTimeIs(graphHopper, FROM_LAT, FROM_LON, LocalDateTime.of(2007,1,1,10, 1).atZone(zoneId).toInstant(), TO_LAT, TO_LON, time(0, 41));
}
@Test
public void testWeekendRouteWorksOnlyOnWeekend() {
final double FROM_LAT = 36.868446, FROM_LON = -116.784582; // BEATTY_AIRPORT stop
final double TO_LAT = 36.641496, TO_LON = -116.40094; // AMV stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,0,0).atZone(zoneId).toInstant()); // Monday morning
GHResponse route = graphHopper.route(ghRequest);
Assert.assertTrue(route.getAll().isEmpty()); // No service on monday morning, and we cannot spend the night at stations yet
GHRequest ghRequest1 = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest1.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,6,0,0).atZone(zoneId).toInstant());
GHResponse route1 = graphHopper.route(ghRequest1);
assertFalse(route1.hasErrors());
assertFalse(route1.getAll().isEmpty());
assertEquals("Expected travel time == scheduled travel time", time(9, 0), route1.getBest().getTime());
assertEquals("Using expected trip", "AAMV1", (((Trip.PtLeg) route1.getBest().getLegs().get(0)).tripId));
assertEquals("Paid expected fare", 525, route1.getBest().getFare().multiply(BigDecimal.valueOf(100)).intValue());
}
@Test
public void testBlockTrips() {
final double FROM_LAT = 36.868446, FROM_LON = -116.784582; // BEATTY_AIRPORT stop
final double TO_LAT = 36.425288, TO_LON = -117.133162; // FUR_CREEK_RES stop
GHRequest ghRequest = new GHRequest(
FROM_LAT, FROM_LON,
TO_LAT, TO_LON
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,8,0).atZone(zoneId).toInstant());
GHResponse response = graphHopper.route(ghRequest);
assertEquals("Only find one solution. If blocks wouldn't work, there would be two. (There is a slower alternative without transfer.)", 1, response.getAll().size());
assertEquals("Expected travel time == scheduled travel time", time(1,20), response.getBest().getTime());
assertEquals("Two legs: pt, pt, but the two pt legs are in one vehicle, so...", 2, response.getBest().getLegs().size());
assertEquals("...one boarding instruction", 1, response.getBest().getInstructions().stream().filter(i -> i.getSign() == Instruction.PT_START_TRIP).count());
assertEquals("...and one alighting instruction", 1, response.getBest().getInstructions().stream().filter(i -> i.getSign() == Instruction.PT_END_TRIP).count());
}
@Test
public void testTransferRules() {
final double FROM_LAT = 36.915682, FROM_LON = -116.751677; // STAGECOACH stop
final double TO1_LAT = 36.641496, TO1_LON = -116.40094; // AMV stop
final double TO2_LAT = 36.88108, TO2_LON = -116.81797; // BULLFROG stop
GHRequest request = new GHRequest(
FROM_LAT, FROM_LON,
TO1_LAT, TO1_LON
);
request.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,6,7,30).atZone(zoneId).toInstant());
GHResponse response = graphHopper.route(request);
assertEquals("Ignoring transfer rules (free walking): Will be there at 9.", time(1, 30), response.getBest().getTime());
request = new GHRequest(
FROM_LAT, FROM_LON,
TO1_LAT, TO1_LON
);
request.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,6,7,30).atZone(zoneId).toInstant());
request.getHints().put(Parameters.PT.MAX_TRANSFER_DISTANCE_PER_LEG, 0.0);
response = graphHopper.route(request);
assertEquals("Transfer rule: 11 minutes. Will miss connection, and be there at 14.", time(6, 30), response.getBest().getTime());
request = new GHRequest(
FROM_LAT, FROM_LON,
TO2_LAT, TO2_LON
);
request.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,6,7,30).atZone(zoneId).toInstant());
response = graphHopper.route(request);
assertEquals("Ignoring transfer rules (free walking): Will be there at 8:10.", time(0, 40), response.getBest().getTime());
request = new GHRequest(
FROM_LAT, FROM_LON,
TO2_LAT, TO2_LON
);
request.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,6,7,30).atZone(zoneId).toInstant());
request.getHints().put(Parameters.PT.MAX_TRANSFER_DISTANCE_PER_LEG, 0.0);
response = graphHopper.route(request);
assertEquals("Will still be there at 8:10 because there is a route-specific exception for this route.", time(0, 40), response.getBest().getTime());
request = new GHRequest(
TO2_LAT, TO2_LON,
FROM_LAT, FROM_LON
);
request.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,6,12,5).atZone(zoneId).toInstant());
request.getHints().put(Parameters.PT.MAX_TRANSFER_DISTANCE_PER_LEG, 0.0);
response = graphHopper.route(request);
assertEquals("Will take 1:15 because of a 'from route' exception with a longer transfer time.", time(1, 15), response.getBest().getTime());
}
private void assertTravelTimeIs(GraphHopperGtfs graphHopper, double from_lat, double from_lon, Instant earliestDepartureTime, double to_lat, double to_lon, int expectedTravelTime) {
GHRequest ghRequest = new GHRequest(
from_lat, from_lon,
to_lat, to_lon
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, earliestDepartureTime);
ghRequest.getHints().put(Parameters.PT.MAX_WALK_DISTANCE_PER_LEG, 30);
GHResponse route = graphHopper.route(ghRequest);
assertFalse(route.hasErrors());
assertFalse(route.getAll().isEmpty());
assertEquals("Expected travel time == scheduled travel time", expectedTravelTime, route.getBest().getTime(), 0.1);
}
private void assertTravelTimeIs(GraphHopperGtfs graphHopper, double FROM_LAT, double FROM_LON, double TO_LAT, double TO_LON, int expectedWeight) {
assertTravelTimeIs(graphHopper, FROM_LAT, FROM_LON, LocalDateTime.of(2007,1,1,0,0).atZone(zoneId).toInstant(), TO_LAT, TO_LON, expectedWeight);
}
private void assertNoRoute(GraphHopperGtfs graphHopper, double from_lat, double from_lon, double to_lat, double to_lon) {
GHRequest ghRequest = new GHRequest(
from_lat, from_lon,
to_lat, to_lon
);
ghRequest.getHints().put(Parameters.PT.EARLIEST_DEPARTURE_TIME, LocalDateTime.of(2007,1,1,0,0).atZone(zoneId).toInstant());
ghRequest.getHints().put(Parameters.PT.MAX_WALK_DISTANCE_PER_LEG, 30);
GHResponse route = graphHopper.route(ghRequest);
Assert.assertTrue(route.getAll().isEmpty());
}
}