Australia: Add AustraliaProvider.

Navitia provides an "au" coverage which aggregates GTFS feeds from
most states public transport authorities. At time of writing, it supports:

 * Melbourne
 * Sydney
 * Canberra
 * Brisbane
 * Darwin
 * Alice Springs
 * Perth
 * Adelaide
 * Hobart
 * Launceston
 * Bernie

However of these, Darwin, Alice Springs, and Darwin are out of date.

By default, only does a semi-comprehensive test of Melbourne (the PTV
network). This is to avoid running into Navitia's API limits if run
under CI, or run continuously during development.

For each other supported network (which is in date), it also provides
tests. The sum of all these tests is that each network should have each
different type of transport (e.g. train/tram/ferry/etc) tested.
This commit is contained in:
Peter Serwylo 2017-05-16 17:24:22 +10:00 committed by Andreas Schildbach
parent af0bcdc17c
commit 973a9d5668
3 changed files with 471 additions and 1 deletions

View file

@ -0,0 +1,165 @@
/*
* Copyright 2017 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.schildbach.pte;
import java.util.HashMap;
import java.util.Map;
import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.Style;
import okhttp3.HttpUrl;
public class AustraliaProvider extends AbstractNavitiaProvider {
public static final String NETWORK_PTV = "PTV - Public Transport Victoria";
public static final String NETWORK_TAS = "Metro Tasmania";
public static final String NETWORK_QLD = "TransLink";
public static final String NETWORK_SA = "Adelaide Metro";
public static final String NETWORK_WA = "Transperth";
private static final Map<String, Style> STYLES = new HashMap<>();
static {
// Melbourne train networks.
// https://static.ptv.vic.gov.au/Maps/1482457134/PTV_Train-Network-Map_2017.pdf
STYLES.put(NETWORK_PTV + "|SBelgrave", new Style(Style.Shape.RECT, Style.parseColor("#094B8D"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SLilydale", new Style(Style.Shape.RECT, Style.parseColor("#094B8D"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SAlamein", new Style(Style.Shape.RECT, Style.parseColor("#094B8D"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SGlen Waverly",
new Style(Style.Shape.RECT, Style.parseColor("#094B8D"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SSunbury", new Style(Style.Shape.RECT, Style.parseColor("#FFB531"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|SCraigieburn",
new Style(Style.Shape.RECT, Style.parseColor("#FFB531"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|SUpfield", new Style(Style.Shape.RECT, Style.parseColor("#FFB531"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|SSouth Morang",
new Style(Style.Shape.RECT, Style.parseColor("#E42B23"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SHurstbridge",
new Style(Style.Shape.RECT, Style.parseColor("#E42B23"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SPakenham", new Style(Style.Shape.RECT, Style.parseColor("#16B4E8"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SCranbourne", new Style(Style.Shape.RECT, Style.parseColor("#16B4E8"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SFrankston", new Style(Style.Shape.RECT, Style.parseColor("#149943"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SWerribee", new Style(Style.Shape.RECT, Style.parseColor("#149943"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|SWilliamstown",
new Style(Style.Shape.RECT, Style.parseColor("#149943"), Style.WHITE));
// Truncated version of "Sandringham". Not sure if this is a bug/limitation in either Navitia or the
// GTFS feed from PTV.
STYLES.put(NETWORK_PTV + "|SSandringha", new Style(Style.Shape.RECT, Style.parseColor("#FC7EBB"), Style.BLACK));
// Difficult to test this, because the line is open only for select periods of the year (e.g. for the
// Melbourne Show in September) and at time of writing, the GTFS feed did not have data up until then.
STYLES.put(NETWORK_PTV + "|SFlemington Racecourse",
new Style(Style.Shape.RECT, Style.parseColor("#9A9B9F"), Style.BLACK));
// Melbourne Trams.
// https://static.ptv.vic.gov.au/Maps/1493356745/PTV_Tram-Network-Map_2017.pdf
STYLES.put(NETWORK_PTV + "|T1", new Style(Style.Shape.RECT, Style.parseColor("#B8C53A"), Style.BLACK));
// The GTFS feed seems to combine 3 + 3a like this, but for completeness we include 3 and 3a
// separately too.
STYLES.put(NETWORK_PTV + "|T3", new Style(Style.Shape.RECT, Style.parseColor("#87D9F2"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T3a", new Style(Style.Shape.RECT, Style.parseColor("#87D9F2"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T3/3a", new Style(Style.Shape.RECT, Style.parseColor("#87D9F2"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T5", new Style(Style.Shape.RECT, Style.parseColor("#F44131"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T6", new Style(Style.Shape.RECT, Style.parseColor("#004969"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T11", new Style(Style.Shape.RECT, Style.parseColor("#7ECBA4"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T12", new Style(Style.Shape.RECT, Style.parseColor("#008A99"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T16", new Style(Style.Shape.RECT, Style.parseColor("#FFD86C"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T19", new Style(Style.Shape.RECT, Style.parseColor("#87457A"), Style.WHITE)); // Night
STYLES.put(NETWORK_PTV + "|T30", new Style(Style.Shape.RECT, Style.parseColor("#3343A3"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T35", new Style(Style.Shape.RECT, Style.parseColor("#6E351C"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T48", new Style(Style.Shape.RECT, Style.parseColor("#45474C"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T57", new Style(Style.Shape.RECT, Style.parseColor("#45C6CE"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T58", new Style(Style.Shape.RECT, Style.parseColor("#878E94"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T59", new Style(Style.Shape.RECT, Style.parseColor("#438459"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T64", new Style(Style.Shape.RECT, Style.parseColor("#2EB070"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T67", new Style(Style.Shape.RECT, Style.parseColor("#B47962"), Style.WHITE)); // Night
STYLES.put(NETWORK_PTV + "|T70", new Style(Style.Shape.RECT, Style.parseColor("#FC8BC1"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T72", new Style(Style.Shape.RECT, Style.parseColor("#97BAA6"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T75", new Style(Style.Shape.RECT, Style.parseColor("#00A8DF"), Style.WHITE)); // Night
STYLES.put(NETWORK_PTV + "|T78", new Style(Style.Shape.RECT, Style.parseColor("#7B7EC0"), Style.WHITE));
STYLES.put(NETWORK_PTV + "|T82", new Style(Style.Shape.RECT, Style.parseColor("#BCD649"), Style.BLACK));
STYLES.put(NETWORK_PTV + "|T86", new Style(Style.Shape.RECT, Style.parseColor("#FFB730"), Style.BLACK)); // Night
STYLES.put(NETWORK_PTV + "|T96", new Style(Style.Shape.RECT, Style.parseColor("#F2428F"), Style.WHITE)); // Night
STYLES.put(NETWORK_PTV + "|T109", new Style(Style.Shape.RECT, Style.parseColor("#FF7B24"), Style.WHITE)); // Night
STYLES.put(NETWORK_PTV + "|B", new Style(Style.Shape.RECT, Style.parseColor("#EA8D1E"), Style.WHITE));
// NOTE: This is a work around for poor GTFS data. We should instead say "All REGIONAL_TRAINs are
// purple", but the GTFS feed from Navitia instead returns rural trains as SUBURBAN_TRAINs. Given we
// have already provided colours for all suburban trains above, this more general statement about
// suburban trains results in all regional trains being coloured purple, as intented.
STYLES.put(NETWORK_PTV + "|S", new Style(Style.Shape.RECT, Style.parseColor("#782F9A"), Style.WHITE));
// Sydney train networks.
// http://www.sydneytrains.info/stations/pdf/suburban_map.pdf
// Navitia is not returning enough info in "display_informations" to colourise correctly.
// Specifically, they are not returning "code" which usually is the display name of the line.
// At any rate, the EFA provider for Sydney is likely going to be better than this Navitia provider
// anyway.
// Adelaide train/tram networks.
// These are already colourised correctly by the GTFS data given to Navitia.
// But for reference, the map is available at https://www.adelaidemetro.com.au/Timetables-Maps/Maps.
// Brisbane train/tram/bus/ferry networks.
// These are already colourised correctly by the GTFS data given to Navitia.
// But for reference, the maps are available at https://translink.com.au/plan-your-journey/maps.
// Perth train/bus/ferry networks.
// These are already colourised correctly by the GTFS data given to Navitia.
// The styles do not include "display_informations" with a proper "code" though, so the names are not
// displayed. But for reference, the maps are available at
// http://www.transperth.wa.gov.au/Journey-Planner/Network-Maps.
// Tasmania bus networks.
// Somewhat colourised in Navitia (e.g. Launceston has green buses), but it is incorrect (e.g.
// Launceston should have all sorts of different coloured buses). Maps are available at
// https://www.metrotas.com.au/timetables/.
}
public AustraliaProvider(final HttpUrl apiBase, final String authorization) {
super(NetworkId.AUSTRALIA, apiBase, authorization);
setTimeZone("Australia/Melbourne");
setStyles(STYLES);
}
public AustraliaProvider(final String authorization) {
super(NetworkId.AUSTRALIA, authorization);
setTimeZone("Australia/Melbourne");
setStyles(STYLES);
}
@Override
public String region() {
return "au";
}
@Override
protected Style getLineStyle(String network, Product product, String code, String backgroundColor,
String foregroundColor) {
final Style overridenStyle = lineStyle(network, product, code);
if (overridenStyle != Standard.STYLES.get(product))
return overridenStyle;
else
return super.getLineStyle(network, product, code, backgroundColor, foregroundColor);
}
}

View file

@ -79,5 +79,5 @@ public enum NetworkId {
ONTARIO, QUEBEC,
// Australia
SYDNEY
SYDNEY, AUSTRALIA
}

View file

@ -0,0 +1,305 @@
/*
* Copyright 2017 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.schildbach.pte.live;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import java.io.IOException;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Test;
import de.schildbach.pte.AustraliaProvider;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult;
import de.schildbach.pte.dto.Trip;
/**
* Test basic from/to directions for each mode of transport, in each state of Australia supported by Navitia.
* This is mainly to test whether or not the coverage is still in date or not. For example, at time of writing
* Cambera, Alice Springs, and Darwin were all present on Navitia, but out of date so were unable to provide
* journey planning.
*
* These tests work by taking the next Monday at 08:45 (a random peak hour time where you'd expect there to be
* a lot of public transport available). If they are unable to find a route for a specific mode of transport,
* then you should further investigate to see if the data is out of date or not in Navitia.
*
* Note that by default, only Melbourne is tested comprehensively to prevent running into API limits
* ({@see #RUN_EXPENSIVE_TESTS}).
*/
public class AustraliaProviderLiveTest extends AbstractNavitiaProviderLiveTest {
/**
* If enabled, the entire set of tests will run, resulting in over 30 API calls to Navitia. Given this
* test may or may not be run under, e.g. continuous integration, or run frequently while working on the
* Australian API, it could end up using up your API limit unneccesarily. Thus, the default value is to
* only perform a proper test of Melbourne, and the rest of the coverage is disabled until this flag is
* true.
*/
private static final boolean RUN_EXPENSIVE_TESTS = false;
public AustraliaProviderLiveTest() {
super(new AustraliaProvider(secretProperty("navitia.authorization")));
}
/**
* Ensures that each of the suburban/rural trains, trams, and buses are represented in the journey
* planning and location suggestion API. Based on travelling around the Camberwell area:
* http://www.openstreetmap.org/#map=15/-37.8195/145.0586&layers=T
*/
@Test
public void melbourne() throws IOException {
final Location suburbanTrainStation = assertAndGetLocation("Camberwell Railway Station (Camberwell)");
final Location ruralTrainStation = assertAndGetLocation("Geelong Railway Station (Geelong)");
assertJourneyExists(AustraliaProvider.NETWORK_PTV, new String[] { "Lilydale", "Belgrave", "Alamein" },
suburbanTrainStation, ruralTrainStation);
final Location tramStop = assertAndGetLocation("70-Cotham Rd/Burke Rd (Kew)");
assertJourneyExists(AustraliaProvider.NETWORK_PTV, "72", suburbanTrainStation, tramStop);
final Location busStop = assertAndGetLocation("Lawrence St/Burke Rd (Kew East)");
assertJourneyExists(AustraliaProvider.NETWORK_PTV, "548", tramStop, busStop);
}
@Test
public void adelaideRail() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location railwayStation = assertAndGetLocation("Woodville Park Railway Station");
final Location railwayStation2 = assertAndGetLocation("Unley Park Railway Station");
assertJourneyExists(AustraliaProvider.NETWORK_SA, new String[] { "GRNG", "BEL", "OUTHA" }, railwayStation,
railwayStation2);
}
@Test
public void adelaideBus() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location busStation = assertAndGetLocation("Stop 137 Portrush Rd - East side");
final Location busStation2 = assertAndGetLocation("Stop 144 Portrush Rd - East side");
assertJourneyExists(AustraliaProvider.NETWORK_SA, "300", busStation, busStation2);
}
@Test
public void adelaideTram() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location tramStation = assertAndGetLocation("Stop 15 Dunbar Tce - Brighton Rd");
final Location tramStation2 = assertAndGetLocation("Stop 5 Black Forest");
assertJourneyExists(AustraliaProvider.NETWORK_SA, "Tram", tramStation, tramStation2);
}
@Test
public void perthTrain() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location railwayStation = assertAndGetLocation("Kenwick Stn");
final Location railwayStation2 = assertAndGetLocation("Warwick Stn");
assertJourneyExists(AustraliaProvider.NETWORK_WA, "", railwayStation, railwayStation2);
}
@Test
public void perthBus() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location bus = assertAndGetLocation("Curtin University");
final Location bus2 = assertAndGetLocation("Murdoch Stn");
assertJourneyExists(AustraliaProvider.NETWORK_WA, "", bus, bus2);
}
@Test
public void perthFerry() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location ferry = assertAndGetLocation("Elizabeth Quay Stn");
final Location ferry2 = assertAndGetLocation("Ferry Route Mends St Jetty");
assertJourneyExists(AustraliaProvider.NETWORK_WA, "", ferry, ferry2);
}
@Test
public void brisbaneRail() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location railwayStation = assertAndGetLocation("Beenleigh station");
final Location railwayStation2 = assertAndGetLocation("Ipswich station");
assertJourneyExists(AustraliaProvider.NETWORK_QLD, "BNFG", railwayStation, railwayStation2);
}
@Test
public void brisbaneFerry() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location ferry = assertAndGetLocation("Broadbeach South");
final Location ferry2 = assertAndGetLocation("Southport");
assertJourneyExists(AustraliaProvider.NETWORK_QLD, "GLKN", ferry, ferry2);
}
@Test
public void brisbaneTram() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location tram = assertAndGetLocation("South Bank 2 ferry terminal");
final Location tram2 = assertAndGetLocation("Guyatt Park ferry terminal");
assertJourneyExists(AustraliaProvider.NETWORK_QLD, "UQSL", tram, tram2);
}
@Test
public void hobartBus() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location bus = assertAndGetLocation("Stop 15, No.237 New Town Rd");
final Location bus2 = assertAndGetLocation("Stop 2, No.131 Elizabeth St");
assertJourneyExists(AustraliaProvider.NETWORK_TAS, "504", bus, bus2);
}
@Test
public void launcestonBus() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location bus = assertAndGetLocation("Riverside Dr / Rannoch Ave");
final Location bus2 = assertAndGetLocation("Trevallyn Shops");
assertJourneyExists(AustraliaProvider.NETWORK_TAS, "90", bus, bus2);
}
@Test
public void bernieBus() throws IOException {
assumeTrue(RUN_EXPENSIVE_TESTS);
final Location bus = assertAndGetLocation("Stop 31, 197 Mount St");
final Location bus2 = assertAndGetLocation("Burnie Park opposite 55 West Park Gr");
assertJourneyExists(AustraliaProvider.NETWORK_TAS, "12", bus, bus2);
}
// Although Navitia has a GTFS feed for ACT, Darwin, and Alice Springs, they were out of date at time of
// writing.
@Test
@Ignore
public void act() {
}
@Test
@Ignore
public void darwin() {
}
@Test
@Ignore
public void aliceSprings() {
}
/**
* Suggests locations similar to {@param locationName}, but then ensures that one matches exactly and then
* returns it. Try not to use an ambiguous name such as "Central Station", because it may exist in several
* datasets on Navitia.
*/
private Location assertAndGetLocation(String locationName) throws IOException {
SuggestLocationsResult locations = suggestLocations(locationName);
assertEquals(SuggestLocationsResult.Status.OK, locations.status);
assertTrue(locations.getLocations().size() > 0);
StringBuilder nonMatching = new StringBuilder();
for (Location location : locations.getLocations()) {
if (locationName.equals(location.name)) {
return location;
}
nonMatching.append('[').append(location.name).append("] ");
}
throw new AssertionError(
"suggestLocations() did not find \"" + locationName + "\". Options were: " + nonMatching);
}
/**
* @see #assertJourneyExists(String, String[], Location, Location)
*/
private void assertJourneyExists(String network, String eligibleLine, Location from, Location to)
throws IOException {
assertJourneyExists(network, new String[] { eligibleLine }, from, to);
}
private Date getNextMondayMorning() {
Calendar date = Calendar.getInstance();
date.setTime(new Date());
date.set(Calendar.HOUR_OF_DAY, 8);
date.set(Calendar.MINUTE, 45);
while (date.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
date.add(Calendar.DATE, 1);
}
return date.getTime();
}
private void assertJourneyExists(String network, String[] eligibleLines, Location from, Location to)
throws IOException {
QueryTripsResult trips = queryTrips(from, null, to, getNextMondayMorning(), true, null, null, null);
assertNull(trips.ambiguousFrom);
assertNull(trips.ambiguousTo);
assertEquals(QueryTripsResult.Status.OK, trips.status);
assertNotNull(trips.trips);
assertTrue(trips.trips.size() > 0);
Set<String> eligibleLineSet = new HashSet<>();
Collections.addAll(eligibleLineSet, eligibleLines);
for (Trip trip : trips.trips) {
boolean hasPublicTransport = false;
boolean matchesCode = false;
for (Trip.Leg leg : trip.legs) {
if (leg instanceof Trip.Public) {
hasPublicTransport = true;
Trip.Public publicLeg = (Trip.Public) leg;
assertEquals(network, publicLeg.line.network);
if (eligibleLineSet.contains(publicLeg.line.label)) {
matchesCode = true;
}
}
}
if (hasPublicTransport && matchesCode) {
return;
}
}
StringBuilder sb = new StringBuilder();
for (Trip trip : trips.trips) {
sb.append("\n ");
for (Trip.Leg leg : trip.legs) {
String via = leg instanceof Trip.Public ? " (via " + ((Trip.Public) leg).line.label + ") " : " -> ";
sb.append('[').append(leg.arrival.name).append(']').append(via).append('[').append(leg.departure.name)
.append(']').append(" ... ");
}
}
fail("No public trip found between [" + from.name + "] and [" + to.name
+ "] using appropriate line. Found trips:" + sb);
}
}