20 Commits

Author SHA1 Message Date
93acfad184 Bump version to 1.1.1 2018-02-20 19:40:24 +01:00
daa84f61c5 Minor dependency updates 2018-02-20 19:32:38 +01:00
0304f6e0f4 Update copyright notice to 2018 2018-01-01 17:28:24 +01:00
56d482b723 Test framework migrated to Mockito/Powermock 2
This simple migration with the bare minimum of changes necessary solves
compatibility issues with Java 9 build environments and presumably makes
JaCoCo offline analysis superfluous.
2017-11-25 18:56:34 +01:00
afffaa4a6f Bundled error messages for model creation in interface 2017-08-29 20:13:28 +02:00
e12775b6ae Refactored empty-checks on result lists 2017-08-29 20:13:27 +02:00
6b0481a5e1 Encapsulated errors in IllegalStateException
getList() and getStops() no longer silently ignore IOExceptions and also
do not print StackTraces any more.
2017-08-29 20:13:20 +02:00
ca9578a4d3 JaCoCo offline analysis
As PowerMock instrumented classes (used to mock API requests here)
messes up with JaCoCo coverage analysis, Maven build is changed to
offline instrumentation.
2017-08-19 17:34:39 +02:00
9ecb3763af Add SonarCloud to Travis 2017-08-18 14:08:35 +02:00
a8fc93fb41 Model classes and input parameters declared final
As model classes are not designed for inheritance and about all method
parameters are already effectively final, they have been explicitly
declared final.
2017-08-04 10:12:12 +02:00
1f4bd9f411 Code style
Various code style improvements:
lines trimmed to 120 characters,
complete JavaDoc for public methods,
braces on multiline if-else,
fixed numbers defined as constants
2017-08-04 10:06:44 +02:00
ee1d8bd2c8 Minor ReadMe adjustments 2017-08-02 12:36:46 +02:00
4b3a255e18 Bump Jackson to 2.9.0 2017-08-02 12:31:38 +02:00
6fab3ab6fe POM and changelog updates 2017-05-14 10:37:32 +02:00
35728fa9b1 Badges in ReadMe 2017-01-07 19:09:00 +01:00
5a93a54c64 Preparations for 1.1.0 2017-01-07 18:49:27 +01:00
d7f6c34fc0 Queries for destination, towards and GPS coordinates 2017-01-07 18:33:29 +01:00
746696405c API V1 as default; Tests against ASEAG and TFL 2017-01-07 17:51:42 +01:00
b208a732c7 Package correction 2017-01-02 15:46:36 +01:00
03e6fc0338 Javadoc corrections 2017-01-02 15:46:35 +01:00
32 changed files with 1045 additions and 277 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
target/
.idea/
*.iml

View File

@ -1,3 +1,15 @@
language: java
jdk:
- oraclejdk8
- oraclejdk8
install: true
addons:
sonarcloud:
organization: "stklcode-github"
token:
secure: "FkEe/+MKpF4pSX3ZYOgu7oeIKf0460Q3XVLUhIX9bk2dyY8hoab74oCo4FtD7jim0+ZC13JVHGDX7iOQMUtS5EZ+x+pA0qpppzCK5zV8afN/l46HJ07kJldvr+EH0klbDVMFZQ5dT7r/w6CoDzjtENHzKQAJLcheUVDNpkcuBdaplTqIAVf3lQpKtOuVjQJ5qZDwwS5wsHNqPcYbcEGrPmcKDVnp3mD3bfI6dT1bbRt845QcD73rPYkQKxen8eIwJxFf5MZStgvbj7yphPxPGwoLAsoLP6LpThTDYcrg+vgUnSs+l9GckL3IbhPAmecixLbKVnphBZzRTvpdMTt5KeOoAJ2nM6RLs5cRCqiEgLEioWkVaSH5WxoBj38Z1h4fTsDV3dTcCuQWX8GFxdeeTelu+XbatdRWMnUgiF7oax+uNvR62fasTbAc7dWPJbARiD7ZbkWH4nHEY07xKKx87xzUz36ZeEHGoBXgqnLmv/FCwqMrEpOoIT41fc0WYtdIA4wjRoAyG0u+wNBMbVlf4PK72seM4b/bmU+TtmaaVla/SvNOiz+A3DHxtJEUScPcL3QGjviddglMf+wyD6VXVViq9VuYRKZFyjpuoNpb5lwEbwmRnmLabBx8jBgyPinjpmqHYlIntcPAwuyLRaqTHFcmCrbeeZEf7KaPRYKx+Cs="
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'
script:
- mvn -P jacoco clean package sonar:sonar

12
CHANGELOG.md Normal file
View File

@ -0,0 +1,12 @@
## 1.1.1 [2018-02-20]
* [improvement] On connection or parsing errors, the `IOException` is no longer ignored, but encapsulated in `RuntimeException` (no StackTraces printed)
* [internal] Code cleanup and minor improvements
* [dependeny] Minor dependency updates
## 1.1.0 [2017-01-07]
* [feature] Filter stops by coordinates and radius
* [feature] Filter trips by destination and and towards fields
* [test] Test coverage 100% (line); tested against ASEAG and TFL APIs
## 1.0.0 [2017-01-02]
* Initial release

View File

@ -1,46 +1,68 @@
jURAclient [![Build status](https://travis-ci.org/stklcode/juraclient.svg?branch=master)](https://travis-ci.org/stklcode/juraclient)
==========
# jURAclient
[![Build status](https://travis-ci.org/stklcode/juraclient.svg?branch=master)](https://travis-ci.org/stklcode/juraclient)
[![Quality Gate](https://sonarcloud.io/api/badges/gate?key=de.stklcode.pubtrans%3Ajuraclient)](https://sonarcloud.io/dashboard?id=de.stklcode.pubtrans%3Ajuraclient)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/stklcode/juraclient/blob/master/LICENSE.txt)
[![Maven Central](https://img.shields.io/maven-central/v/de.stklcode.pubtrans/juraclient.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.stklcode.pubtrans%22%20AND%20a%3A%22juraclient%22)
Java client for URA based public transport APIs.
This client allows to simply connect any Java application to the public transport API to implement a monitor for the
local bus station or any other custom queries.
local bus station or any other custom queries. API versions 1.x and 2.x are supported.
**Usage Example**
## Usage Examples
### Initialization
```java
// Instantiate the client (e.g. using the ASEAG API)
UraClient ura = new UraClient("http://ivu.aseag.de");
// Instantiate the client (e.g. using the TFL API)
UraClient ura = new UraClient("http://countdown.api.tfl.gov.uk");
// Initiailize with non-standard endpoints
// Initiailize the API with non-standard endpoints (e.g. ASEAG with API V2)
UraClient ura = new UraClient("http://ivu.aseag.de",
"interfaces/ura/instant_V2",
"interfaces/ura/stream_V2");
// List available stops
List<Stop> stops = ura.listStops();
// Get next 10 trips for given stops and lines (all filters optional)
List<Trip> trips = ura.forStop("100000")
.forLines("25", "35")
.getTrips(10);
```
**Maven Artifact**
### List Stops
```java
// List ALL available stops
List<Stop> stops = ura.getStops();
// List available stops in a 200m radius around given coordinates
List<Stop> stops = ura.forPosition(51.51009, -0.1345734, 200)
.getStops();
```
### Get Trips
```java
// Get next 10 trips for given stops and lines in a single direction (all filters optional)
List<Trip> trips = ura.forStop("100000")
.forLines("25", "35")
.forDirection(1)
.getTrips(10);
// Get trips from given stop towards your destination
List<Trip> trips = ura.forStopByName("Piccadilly Circus")
.towards("Marble Arch")
.getTrips();
```
## Maven Artifact
```
<dependency>
<groupId>de.stklcode.pubtrans</groupId>
<artifactId>juraclient</artifactId>
<version>1.0.0</version>
<version>1.1.1</version>
</dependency>
```
**Planned features:**
## Planned Features
* More refined query parameters
* Stream API with asynchronous consumer
**License**
## License
The project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
The project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).

106
pom.xml
View File

@ -5,23 +5,57 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.stklcode.pubtrans</groupId>
<artifactId>juraclien</artifactId>
<version>1.0.0</version>
<artifactId>juraclient</artifactId>
<version>1.1.1</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<packaging>jar</packaging>
<name>jURAclient</name>
<description>Java client for URA based public transport API</description>
<url>https://github.com/stklcode/juraclient</url>
<inceptionYear>2016</inceptionYear>
<licenses>
<license>
<name>Apache License 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Stefan Kalscheuer</name>
<email>stefan@stklcode.de</email>
<timezone>+1</timezone>
</developer>
</developers>
<scm>
<connection>scm:git:git://github.com/stklcode/juraclient.git</connection>
<developerConnection>scm:git:git@github.com:stklcode/juraclient.git</developerConnection>
<url>https://github.com/stklcode/juraclient</url>
</scm>
<issueManagement>
<system>github</system>
<url>https://github.com/stklcode/juraclient/issues</url>
</issueManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.5</version>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.5</version>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
@ -38,13 +72,13 @@
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.6</version>
<version>2.0.0-beta.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.6</version>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.0-beta.5</version>
<scope>test</scope>
</dependency>
</dependencies>
@ -54,7 +88,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@ -62,4 +96,60 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jacoco</id>
<dependencies>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<version>0.8.0</version>
<classifier>runtime</classifier>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<id>default-instrument</id>
<goals>
<goal>instrument</goal>
</goals>
</execution>
<execution>
<id>default-restore-instrumented-classes</id>
<goals>
<goal>restore-instrumented-classes</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${project.build.directory}/coverage.exec</dataFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
<configuration>
<systemPropertyVariables>
<jacoco-agent.destfile>${project.build.directory}/jacoco.exec</jacoco-agent.destfile>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,11 +31,11 @@ import java.util.List;
/**
* Client for URA based public transport API.
*
* @author Stefan Kalscheuer <stefan@stklcode.de>
* @author Stefan Kalscheuer
*/
public class UraClient {
private static final String DEFAULT_INSTANT_URL = "/interfaces/ura/instant_V2";
private static final String DEFAULT_STREAM_URL = "/interfaces/ura/stream_V2";
private static final String DEFAULT_INSTANT_URL = "/interfaces/ura/instant_V1";
private static final String DEFAULT_STREAM_URL = "/interfaces/ura/stream_V1";
private static final String PAR_STOP_ID = "StopID";
private static final String PAR_STOP_NAME = "StopPointName";
@ -51,10 +51,12 @@ public class UraClient {
private static final String PAR_VEHICLE_ID = "VehicleID";
private static final String PAR_TRIP_ID = "TripID";
private static final String PAR_ESTTIME = "EstimatedTime";
private static final String PAR_TOWARDS = "Towards";
private static final String PAR_CIRCLE = "Circle";
private static final Integer RES_TYPE_STOP = 0;
private static final Integer RES_TYPE_PREDICTION = 1;
private static final Integer RES_TYPE_URA_VERSION = 4;
private static final String[] REQUEST_STOP = {PAR_STOP_NAME, PAR_STOP_ID, PAR_STOP_INDICATOR, PAR_STOP_STATE, PAR_GEOLOCATION};
private static final String[] REQUEST_TRIP = {PAR_STOP_NAME, PAR_STOP_ID, PAR_STOP_INDICATOR, PAR_STOP_STATE, PAR_GEOLOCATION,
@ -68,20 +70,20 @@ public class UraClient {
/**
* Constructor with base URL and default API paths.
*
* @param baseURL the base URL (with protocol, without trailing slash)
* @param baseURL The base URL (with protocol, without trailing slash).
*/
public UraClient(String baseURL) {
public UraClient(final String baseURL) {
this(baseURL, DEFAULT_INSTANT_URL, DEFAULT_STREAM_URL);
}
/**
* Constructor with base URL and custom API paths
* Constructor with base URL and custom API paths.
*
* @param baseURL the base URL (including protocol)
* @param instantURL the path for instant requests
* @param streamURL the path for stream requests
* @param baseURL The base URL (including protocol).
* @param instantURL The path for instant requests.
* @param streamURL The path for stream requests.
*/
public UraClient(String baseURL, String instantURL, String streamURL) {
public UraClient(final String baseURL, final String instantURL, final String streamURL) {
this.baseURL = baseURL;
this.instantURL = instantURL;
this.streamURL = streamURL;
@ -94,7 +96,7 @@ public class UraClient {
* @param stops Stop IDs
* @return the request
*/
public Query forStops(final String...stops) {
public final Query forStops(final String... stops) {
return new Query().forStops(stops);
}
@ -104,45 +106,80 @@ public class UraClient {
* @param stopNames Stop Point Names
* @return the request
*/
public Query forStopsByName(final String...stopNames) {
public final Query forStopsByName(final String... stopNames) {
return new Query().forStopsByName(stopNames);
}
/**
* Builder pattern to request given line IDs.
*
* @param lines line IDs
* @return the request
* @param lines Line IDs.
* @return The request.
*/
public Query forLines(final String...lines) {
public final Query forLines(final String... lines) {
return new Query().forLines(lines);
}
/**
* Builder pattern to request given line names.
*
* @param lineNames line names
* @return the request
* @param lineNames Line names.
* @return The request.
*/
public Query forLinesByName(final String...lineNames) {
public final Query forLinesByName(final String... lineNames) {
return new Query().forLinesByName(lineNames);
}
/**
* Builder pattern to request given direction.
*
* @param direction the direction ID
* @return the request
* @param direction The direction ID.
* @return The request.
*/
public Query forDirection(final Integer direction) {
public final Query forDirection(final Integer direction) {
return new Query().forDirection(direction);
}
/**
* Builder pattern to request given destination names.
*
* @param destinationNames Destination names.
* @return The request.
* @since 1.1.0
*/
public final Query forDestinationNames(final String... destinationNames) {
return new Query().forDestinationNames(destinationNames);
}
/**
* Builder pattern to request given direction defined by stop point name.
*
* @param towards Towards stop point names.
* @return The request.
* @since 1.1.0
*/
public final Query towards(final String... towards) {
return new Query().towards(towards);
}
/**
* Builder pattern to request given destination names.
*
* @param latitude Latitude (WGS84).
* @param longitude Longitude (WGS84).
* @param radius Search radius (meters).
* @return The request.
* @since 1.1.0
*/
public final Query forPosition(final Double latitude, final Double longitude, final Integer radius) {
return new Query().forPosition(latitude, longitude, radius);
}
/**
* Get list of trips.
* If forStops() and/or forLines() has been called, those will be used as filter.
*
* @return list of trips
* @return List of trips.
*/
public List<Trip> getTrips() {
return getTrips(new Query(), null);
@ -152,7 +189,8 @@ public class UraClient {
* Get list of trips with limit.
* If forStops() and/or forLines() has been called, those will be used as filter.
*
* @return list of trips
* @param limit Maximum number of results.
* @return List of trips.
*/
public List<Trip> getTrips(final Integer limit) {
return getTrips(new Query(), limit);
@ -162,32 +200,40 @@ public class UraClient {
* Get list of trips.
* If forStops() and/or forLines() has been called, those will be used as filter.
*
* @return list of trips
* @param query The query.
* @return List of trips.
*/
public List<Trip> getTrips(Query query) {
public List<Trip> getTrips(final Query query) {
return getTrips(query, null);
}
/**
* Get list of trips for given stopIDs and lineIDs with result limit.
*
* @param query the request
* @param limit maximum number of results
* @return list of trips
* @param query The query.
* @param limit Maximum number of results.
* @return List of trips.
*/
public List<Trip> getTrips(final Query query, final Integer limit) {
List<Trip> trips = new ArrayList<>();
try (InputStream is = requestInstant(REQUEST_TRIP, query.stopIDs, query.stopNames, query.lineIDs, query.lineNames, query.direction);
try (InputStream is = requestInstant(REQUEST_TRIP, query);
BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String line;
while ((line = br.readLine()) != null && (limit == null || trips.size() < limit)) {
String version = null;
String line = br.readLine();
while (line != null && (limit == null || trips.size() < limit)) {
List l = mapper.readValue(line, List.class);
/* Check if result exists and has correct response type */
if (l != null && l.size() > 0 && l.get(0).equals(RES_TYPE_PREDICTION))
trips.add(new Trip(l));
if (l != null && !l.isEmpty()) {
if (l.get(0).equals(RES_TYPE_URA_VERSION)) {
version = l.get(1).toString();
} else if (l.get(0).equals(RES_TYPE_PREDICTION)) {
trips.add(new Trip(l, version));
}
}
line = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
throw new IllegalStateException("Failed to read from API", e);
}
return trips;
}
@ -195,7 +241,7 @@ public class UraClient {
/**
* Get list of stops without filters.
*
* @return the list
* @return Lhe list.
*/
public List<Stop> getStops() {
return getStops(new Query());
@ -205,21 +251,23 @@ public class UraClient {
* List available stopIDs.
* If forStops() and/or forLines() has been called, those will be used as filter.
*
* @return the list
* @param query The query.
* @return The list.
*/
public List<Stop> getStops(Query query) {
public List<Stop> getStops(final Query query) {
List<Stop> stops = new ArrayList<>();
try (InputStream is = requestInstant(REQUEST_STOP, query.stopIDs, query.stopNames, query.lineIDs, query.lineNames, query.direction);
try (InputStream is = requestInstant(REQUEST_STOP, query);
BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String line;
while ((line = br.readLine()) != null) {
List l = mapper.readValue(line, List.class);
/* Check if result exists and has correct response type */
if (l != null && l.size() > 0 && l.get(0).equals(RES_TYPE_STOP))
if (l != null && !l.isEmpty() && l.get(0).equals(RES_TYPE_STOP)) {
stops.add(new Stop(l));
}
}
} catch (IOException e) {
e.printStackTrace();
throw new IllegalStateException("Failed to read from API", e);
}
return stops;
}
@ -227,43 +275,64 @@ public class UraClient {
/**
* Issue request to instant endpoint and return input stream.
*
* @param returnList fields to fetch
* @param returnList Fields to fetch.
* @param query The query.
* @return Input stream of the URL
* @throws IOException on errors
*/
private InputStream requestInstant(String[] returnList, String[] stopIDs, String[] stopNames, String[] lineIDs, String[] lineNames, Integer direction) throws IOException {
private InputStream requestInstant(final String[] returnList, final Query query) throws IOException {
String urlStr = baseURL + instantURL + "?ReturnList=" + String.join(",", returnList);
if (stopIDs != null && stopIDs.length > 0)
urlStr += "&" + PAR_STOP_ID + "=" + String.join(",", stopIDs);
if (stopNames != null && stopNames.length > 0)
urlStr += "&" + PAR_STOP_NAME + "=" + String.join(",", stopNames);
if (lineIDs != null && lineIDs.length > 0)
urlStr += "&" + PAR_LINE_ID + "=" + String.join(",", lineIDs);
if (lineNames != null && lineNames.length > 0)
urlStr += "&" + PAR_LINE_NAME + "=" + String.join(",", lineNames);
if (direction != null)
urlStr += "&" + PAR_DIR_ID + "=" + direction;
if (query.stopIDs != null && query.stopIDs.length > 0) {
urlStr += "&" + PAR_STOP_ID + "=" + String.join(",", query.stopIDs);
}
if (query.stopNames != null && query.stopNames.length > 0) {
urlStr += "&" + PAR_STOP_NAME + "=" + String.join(",", query.stopNames);
}
if (query.lineIDs != null && query.lineIDs.length > 0) {
urlStr += "&" + PAR_LINE_ID + "=" + String.join(",", query.lineIDs);
}
if (query.lineNames != null && query.lineNames.length > 0) {
urlStr += "&" + PAR_LINE_NAME + "=" + String.join(",", query.lineNames);
}
if (query.direction != null) {
urlStr += "&" + PAR_DIR_ID + "=" + query.direction;
}
if (query.destinationNames != null) {
urlStr += "&" + PAR_DEST_NAME + "=" + String.join(",", query.destinationNames);
}
if (query.towards != null) {
urlStr += "&" + PAR_TOWARDS + "=" + String.join(",", query.towards);
}
if (query.circle != null) {
urlStr += "&" + PAR_CIRCLE + "=" + String.join(",", query.circle);
}
URL url = new URL(urlStr);
return url.openStream();
}
/**
* Request meta object.
*/
public class Query {
public final class Query {
private String[] stopIDs;
private String[] stopNames;
private String[] lineIDs;
private String[] lineNames;
private Integer direction;
private String[] destinationNames;
private String[] towards;
private String circle;
/**
* Builder pattern to request given line IDs.
*
* @param lineIDs line IDs
* @return the query
* @param lineIDs Line IDs.
* @return The query.
*/
public Query forLines(final String...lineIDs) {
public Query forLines(final String... lineIDs) {
this.lineIDs = lineIDs;
return this;
}
@ -271,10 +340,10 @@ public class UraClient {
/**
* Builder pattern to request given line names.
*
* @param lineNames line names
* @return the query
* @param lineNames Line names.
* @return The query.
*/
public Query forLinesByName(final String...lineNames) {
public Query forLinesByName(final String... lineNames) {
this.lineNames = lineNames;
return this;
}
@ -282,10 +351,10 @@ public class UraClient {
/**
* Builder pattern to request given stop IDs.
*
* @param stopIDs stop IDs
* @return the query
* @param stopIDs Stop IDs.
* @return The query.
*/
public Query forStops(final String...stopIDs) {
public Query forStops(final String... stopIDs) {
this.stopIDs = stopIDs;
return this;
}
@ -293,10 +362,10 @@ public class UraClient {
/**
* Builder pattern to request given stop names.
*
* @param stopNames line names
* @return the query
* @param stopNames Line names.
* @return The query.
*/
public Query forStopsByName(final String...stopNames) {
public Query forStopsByName(final String... stopNames) {
this.stopNames = stopNames;
return this;
}
@ -304,18 +373,56 @@ public class UraClient {
/**
* Builder pattern to request given direction.
*
* @param direction the direction
* @return the query
* @param direction The direction.
* @return The query.
*/
public Query forDirection(final Integer direction) {
this.direction = direction;
return this;
}
/**
* Builder pattern to request given destination names.
*
* @param destinationNames Names of destinations.
* @return The query.
* @since 1.1.0
*/
public Query forDestinationNames(final String... destinationNames) {
this.destinationNames = destinationNames;
return this;
}
/**
* Builder pattern to request given direction defined by stop point name.
*
* @param towards Towards stop point names.
* @return The request.
* @since 1.1.0
*/
public Query towards(final String... towards) {
this.towards = towards;
return this;
}
/**
* Builder pattern to request given position and radius.
*
* @param latitude Latitude (WGS84).
* @param longitude Longitude (WGS84).
* @param radius Search radius (meters).
* @return The query.
* @since 1.1.0
*/
public Query forPosition(final Double latitude, final Double longitude, final Integer radius) {
this.circle = latitude.toString() + "," + longitude.toString() + "," + radius.toString();
return this;
}
/**
* Get stops for set filters.
*
* @return List of matching trips
* @return List of matching trips.
*/
public List<Stop> getStops() {
return UraClient.this.getStops(this);
@ -324,7 +431,7 @@ public class UraClient {
/**
* Get trips for set filters.
*
* @return List of matching trips
* @return List of matching trips.
*/
public List<Trip> getTrips() {
return UraClient.this.getTrips(this);

View File

@ -0,0 +1,49 @@
/*
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed 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 de.stklcode.pubtrans.ura.model;
import java.io.IOException;
/**
* @author Stefan Kalscheuer
* @since 1.1.1
*/
interface Model {
/**
* Generate exception for unmatched type when String is expected.
*
* @param field Field number.
* @param actual Actual class.
* @return The Exception.
*/
static IOException typeErrorString(int field, Class actual) {
return typeError(field, actual, "String");
}
/**
* Generate exception for unmatched type.
*
* @param field Field number.
* @param actual Actual class.
* @param expected Expected type.
* @return The Exception.
*/
static IOException typeError(int field, Class actual, String expected) {
return new IOException(String.format("Field %d not of expected type %s, found %s",
field, expected, actual.getSimpleName()));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,9 +22,17 @@ import java.util.List;
/**
* Entity for a single stop.
*
* @author Stefan Kalscheuer <stefan@stklcode.de>
* @author Stefan Kalscheuer
*/
public class Stop {
public final class Stop implements Model {
private static final int F_STOP_NAME = 1;
private static final int F_STOP_ID = 2;
private static final int F_INDICATOR = 3;
private static final int F_STATE = 4;
private static final int F_LATITUDE = 5;
private static final int F_LONGITUDE = 6;
private static final int F_NUM_OF_FIELDS = 7;
private final String id;
private final String name;
private final String indicator;
@ -32,7 +40,22 @@ public class Stop {
private final Double latitude;
private final Double longitude;
public Stop(String id, String name, String indicator, Integer state, Double latitude, Double longitude) {
/**
* Construct Stop object.
*
* @param id Stop ID.
* @param name Stop name.
* @param indicator Stop indicator.
* @param state Stop state.
* @param latitude Stop geolocation latitude.
* @param longitude Stop geolocation longitude.
*/
public Stop(final String id,
final String name,
final String indicator,
final Integer state,
final Double latitude,
final Double longitude) {
this.id = id;
this.name = name;
this.indicator = indicator;
@ -41,56 +64,94 @@ public class Stop {
this.longitude = longitude;
}
public Stop(List raw) throws IOException {
if (raw == null || raw.size() < 7)
/**
* Construct Stop object from raw list of attributes parsed from JSON.
*
* @param raw List of attributes from JSON line
* @throws IOException Thrown on invalid line format.
*/
public Stop(final List raw) throws IOException {
if (raw == null || raw.size() < F_NUM_OF_FIELDS) {
throw new IOException("Invalid number of fields");
}
if (raw.get(1) instanceof String)
name = (String)raw.get(1);
else
throw new IOException("Field 1 not of expected type String, found " + raw.get(1).getClass().getSimpleName());
if (raw.get(2) instanceof String)
id = (String)raw.get(2);
else
throw new IOException("Field 2 not of expected type String, found " + raw.get(2).getClass().getSimpleName());
if (raw.get(3) instanceof String)
indicator = (String)raw.get(3);
else
throw new IOException("Field 3 not of expected type String, found " + raw.get(3).getClass().getSimpleName());
if (raw.get(4) instanceof Integer)
state = (Integer)raw.get(4);
else
throw new IOException("Field 4 not of expected type Integer, found " + raw.get(4).getClass().getSimpleName());
if (raw.get(5) instanceof Double)
latitude = (Double)raw.get(5);
else
throw new IOException("Field 5 not of expected type Double, found " + raw.get(5).getClass().getSimpleName());
if (raw.get(6) instanceof Double)
longitude = (Double)raw.get(6);
else
throw new IOException("Field 6 not of expected type Double, found " + raw.get(6).getClass().getSimpleName());
if (raw.get(1) instanceof String) {
name = (String) raw.get(F_STOP_NAME);
} else {
throw Model.typeErrorString(F_STOP_NAME, raw.get(F_STOP_NAME).getClass());
}
if (raw.get(F_STOP_ID) instanceof String) {
id = (String) raw.get(F_STOP_ID);
} else {
throw Model.typeErrorString(F_STOP_ID, raw.get(F_STOP_ID).getClass());
}
if (raw.get(F_INDICATOR) instanceof String) {
indicator = (String) raw.get(F_INDICATOR);
} else if (raw.get(F_INDICATOR) == null) {
indicator = null;
} else {
throw Model.typeErrorString(F_INDICATOR, raw.get(F_INDICATOR).getClass());
}
if (raw.get(F_STATE) instanceof Integer) {
state = (Integer) raw.get(F_STATE);
} else {
throw Model.typeError(F_STATE, raw.get(F_STATE).getClass(), "Integer");
}
if (raw.get(F_LATITUDE) instanceof Double) {
latitude = (Double) raw.get(F_LATITUDE);
} else {
throw Model.typeError(F_LATITUDE, raw.get(F_LATITUDE).getClass(), "Double");
}
if (raw.get(F_LONGITUDE) instanceof Double) {
longitude = (Double) raw.get(F_LONGITUDE);
} else {
throw Model.typeError(F_LONGITUDE, raw.get(F_LONGITUDE).getClass(), "Double");
}
}
/**
* @return The stop ID.
*/
public String getId() {
return id;
}
/**
* @return The stop name.
*/
public String getName() {
return name;
}
/**
* @return The stop indicator.
*/
public String getIndicator() {
return indicator;
}
/**
* @return The stop indicator.
*/
public Integer getState() {
return state;
}
/**
* @return The stop geoloaction latitude.
*/
public Double getLatitude() {
return latitude;
}
/**
* @return The stop geolocation longitude.
*/
public Double getLongitude() {
return longitude;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,9 +22,20 @@ import java.util.List;
/**
* Entity for a single trip.
*
* @author Stefan Kalscheuer <stefan@stklcode.de>
* @author Stefan Kalscheuer
*/
public class Trip {
public final class Trip implements Model {
private static final int VISIT_ID = 7;
private static final int LINE_ID = 8;
private static final int LINE_NAME = 9;
private static final int DIRECTION_ID = 10;
private static final int DESTINATION_NAME = 11;
private static final int DESTINATION_TEXT = 12;
private static final int VEHICLE_ID = 13;
private static final int TRIP_ID = 14;
private static final int ESTIMATED_TIME = 15;
private static final int NUM_OF_FIELDS = 16;
private final Stop stop;
private final String id;
private final Integer visitID;
@ -36,13 +47,81 @@ public class Trip {
private final Long estimatedTime;
private final String vehicleID;
public Trip(String stopID, String stopName, String stopIndicator, Integer stopState, Double stopLatitude, Double stopLongitude,
Integer visitID, String lineID, String lineName, Integer directionID, String destinationName, String destinationText, String vehicleID, String tripID, Long estimatedTime) {
this(new Stop(stopID, stopName, stopIndicator, stopState, stopLatitude, stopLongitude),
visitID, lineID, lineName, directionID, destinationName, destinationText, vehicleID, tripID, estimatedTime);
/**
* Construct Trip object from complete set of data.
*
* @param stopID Stop ID.
* @param stopName Stop name.
* @param stopIndicator Stop Indicator.
* @param stopState Stop state.
* @param stopLatitude Stop geolocation latitude.
* @param stopLongitude Stop geolocation latitude.
* @param visitID Visit ID.
* @param lineID Line ID.
* @param lineName Line name.
* @param directionID Direction ID.
* @param destinationName Destination name.
* @param destinationText Destination text.
* @param vehicleID Vehicle ID.
* @param tripID Trip ID.
* @param estimatedTime Estimated time.
*/
public Trip(final String stopID,
final String stopName,
final String stopIndicator,
final Integer stopState,
final Double stopLatitude,
final Double stopLongitude,
final Integer visitID,
final String lineID,
final String lineName,
final Integer directionID,
final String destinationName,
final String destinationText,
final String vehicleID,
final String tripID,
final Long estimatedTime) {
this(new Stop(stopID,
stopName,
stopIndicator,
stopState,
stopLatitude,
stopLongitude),
visitID,
lineID,
lineName,
directionID,
destinationName,
destinationText,
vehicleID,
tripID,
estimatedTime);
}
public Trip(Stop stop, Integer visitID, String lineID, String lineName, Integer directionID, String destinationName, String destinationText, String vehicleID, String tripID, Long estimatedTime) {
/**
* Construct Trip object from Stop model and set of additional data.
*
* @param stop Stop model
* @param visitID Visit ID
* @param lineID Line ID
* @param lineName Line name
* @param directionID Direction ID
* @param destinationName Destination name
* @param destinationText Destination text
* @param vehicleID Vehicle ID
* @param tripID Trip ID
* @param estimatedTime Estimated time
*/
public Trip(final Stop stop,
final Integer visitID,
final String lineID,
final String lineName,
final Integer directionID,
final String destinationName,
final String destinationText,
final String vehicleID,
final String tripID,
final Long estimatedTime) {
this.stop = stop;
this.visitID = visitID;
this.lineID = lineID;
@ -55,86 +134,159 @@ public class Trip {
this.estimatedTime = estimatedTime;
}
public Trip(List raw) throws IOException {
if (raw == null || raw.size() < 16)
/**
* Construct Trip object from raw list of attributes parsed from JSON.
*
* @param raw List of attributes from JSON line
* @throws IOException Thrown on invalid line format.
*/
public Trip(final List raw) throws IOException {
this(raw, null);
}
/**
* Construct Stop object from raw list of attributes parsed from JSON with explicitly specified version.
*
* @param raw List of attributes from JSON line
* @param version API version
* @throws IOException Thrown on invalid line format.
*/
public Trip(final List raw, final String version) throws IOException {
if (raw == null || raw.size() < NUM_OF_FIELDS) {
throw new IOException("Invalid number of fields");
}
stop = new Stop(raw);
if (raw.get(7) instanceof Integer)
visitID = (Integer) raw.get(7);
else
throw new IOException("Field 7 not of expected type Integer, found " + raw.get(7).getClass().getSimpleName());
if (raw.get(8) instanceof String)
lineID = (String)raw.get(8);
else
throw new IOException("Field 8 not of expected type String, found " + raw.get(8).getClass().getSimpleName());
if (raw.get(9) instanceof String)
lineName = (String)raw.get(9);
else
throw new IOException("Field 9 not of expected type String, found " + raw.get(9).getClass().getSimpleName());
if (raw.get(10) instanceof Integer)
directionID = (Integer)raw.get(10);
else
throw new IOException("Field 10 not of expected type Integer, found " + raw.get(10).getClass().getSimpleName());
if (raw.get(11) instanceof String)
destinationName = (String)raw.get(11);
else
throw new IOException("Field 11 not of expected type String, found " + raw.get(11).getClass().getSimpleName());
if (raw.get(12) instanceof String)
destinationText = (String)raw.get(12);
else
throw new IOException("Field 12 not of expected type String, found " + raw.get(12).getClass().getSimpleName());
if (raw.get(13) instanceof String)
vehicleID = (String)raw.get(13);
else
throw new IOException("Field 13 not of expected type String, found " + raw.get(13).getClass().getSimpleName());
if (raw.get(14) instanceof String)
id = (String)raw.get(14);
else
throw new IOException("Field 14 not of expected type String, found " + raw.get(14).getClass().getSimpleName());
if (raw.get(15) instanceof Long)
estimatedTime = (Long)raw.get(15);
else
throw new IOException("Field 15 not of expected type Long, found " + raw.get(15).getClass().getSimpleName());
if (raw.get(VISIT_ID) instanceof Integer) {
visitID = (Integer) raw.get(VISIT_ID);
} else {
throw Model.typeError(VISIT_ID, raw.get(VISIT_ID).getClass(), "Integer");
}
if (raw.get(LINE_ID) instanceof String) {
lineID = (String) raw.get(LINE_ID);
} else {
throw Model.typeErrorString(LINE_ID, raw.get(LINE_ID).getClass());
}
if (raw.get(LINE_NAME) instanceof String) {
lineName = (String) raw.get(LINE_NAME);
} else {
throw Model.typeErrorString(LINE_NAME, raw.get(LINE_NAME).getClass());
}
if (raw.get(DIRECTION_ID) instanceof Integer) {
directionID = (Integer) raw.get(DIRECTION_ID);
if (directionID < 0 || directionID > 2) {
throw new IOException("Direction out of range. Expected 1 or 2, found " + directionID);
}
} else {
throw Model.typeError(DIRECTION_ID, raw.get(DIRECTION_ID).getClass(), "Integer");
}
if (raw.get(DESTINATION_NAME) instanceof String) {
destinationName = (String) raw.get(DESTINATION_NAME);
} else {
throw Model.typeErrorString(DESTINATION_NAME, raw.get(DESTINATION_NAME).getClass());
}
if (raw.get(DESTINATION_TEXT) instanceof String) {
destinationText = (String) raw.get(DESTINATION_TEXT);
} else {
throw Model.typeErrorString(DESTINATION_TEXT, raw.get(DESTINATION_TEXT).getClass());
}
/* TFL and ASEAG deliver different types with the same API version, so this field is a little more tolerant */
if (raw.get(VEHICLE_ID) instanceof String
|| raw.get(VEHICLE_ID) instanceof Integer
|| raw.get(VEHICLE_ID) instanceof Long) {
vehicleID = raw.get(VEHICLE_ID).toString();
} else {
throw Model.typeError(VEHICLE_ID, raw.get(VEHICLE_ID).getClass(), "String/Integer/Long");
}
if (raw.get(TRIP_ID) instanceof String
|| raw.get(TRIP_ID) instanceof Integer
|| raw.get(TRIP_ID) instanceof Long) {
id = raw.get(TRIP_ID).toString();
} else {
throw Model.typeError(TRIP_ID, raw.get(TRIP_ID).getClass(), "String/Integer/Long");
}
if (raw.get(ESTIMATED_TIME) instanceof Long) {
estimatedTime = (Long) raw.get(ESTIMATED_TIME);
} else {
throw Model.typeError(ESTIMATED_TIME, raw.get(ESTIMATED_TIME).getClass(), "Long");
}
}
/**
* @return The (starting) stop.
*/
public Stop getStop() {
return stop;
}
/**
* @return The trip ID.
*/
public String getId() {
return id;
}
/**
* @return The visit ID.
*/
public Integer getVisitID() {
return visitID;
}
/**
* @return The line ID.
*/
public String getLineID() {
return lineID;
}
/**
* @return The line name.
*/
public String getLineName() {
return lineName;
}
/**
* @return The direction ID.
*/
public Integer getDirectionID() {
return directionID;
}
/**
* @return The destination name.
*/
public String getDestinationName() {
return destinationName;
}
/**
* @return The destination text.
*/
public String getDestinationText() {
return destinationText;
}
/**
* @return The estimated departure time.
*/
public Long getEstimatedTime() {
return estimatedTime;
}
/**
* @return The vehicle ID.
*/
public String getVehicleID() {
return vehicleID;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 Stefan Kalscheuer
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,12 +32,16 @@ import java.util.Optional;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
/**
* Unit test for the URA Client.
* Tests run against mocked data collected from hte ASEAG API (http://ivu.aseag.de/)
* Tests run against mocked data collected from ASEAG API (http://ivu.aseag.de) and
* TFL API (http://http://countdown.api.tfl.gov.uk)
*
* @author Stefan Kalscheuer
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest({ UraClient.class, URL.class })
@ -47,7 +51,8 @@ public class UraClientTest {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_stops.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V2_stops.txt"));
/* List stops and verify some values */
List<Stop> stops = new UraClient("mocked").getStops();
@ -65,9 +70,13 @@ public class UraClientTest {
throw new IOException("Provoked exception 1.");
}
});
assertThat(new UraClient("mocked").getStops(), hasSize(0));
PowerMockito.when(mockURL.openStream()).thenThrow(new IOException("Provoked exception 2."));
assertThat(new UraClient("mocked").getStops(), hasSize(0));
try {
new UraClient("mocked").getStops();
} catch (RuntimeException e) {
assertThat(e, is(instanceOf(IllegalStateException.class)));
assertThat(e.getCause(), is(instanceOf(IOException.class)));
assertThat(e.getCause().getMessage(), is("Provoked exception 1."));
}
}
@Test
@ -75,7 +84,8 @@ public class UraClientTest {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_stops_line.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V2_stops_line.txt"));
/* List stops and verify some values */
List<Stop> stops = new UraClient("mocked").forLines("33").getStops();
@ -88,12 +98,89 @@ public class UraClientTest {
assertThat(stops.get(5).getLongitude(), is(6.2314072));
}
@Test
public void getStopsForPositionTest() throws Exception {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_stops_circle.txt"));
/* List stops and verify some values */
List<Stop> stops = new UraClient("mocked")
.forPosition(51.51009, -0.1345734, 200)
.getStops();
assertThat(stops, hasSize(13));
assertThat(stops.get(0).getId(), is("156"));
assertThat(stops.get(1).getName(), is("Piccadilly Circus"));
assertThat(stops.get(2).getState(), is(0));;
assertThat(stops.get(3).getLatitude(), is(51.509154));
assertThat(stops.get(4).getLongitude(), is(-0.134172));
assertThat(stops.get(5).getIndicator(), is(nullValue()));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_stops_circle_name.txt"));
stops = new UraClient("mocked")
.forStopsByName("Piccadilly Circus")
.forPosition(51.51009, -0.1345734, 200)
.getStops();
assertThat(stops, hasSize(7));
assertThat(stops.stream().filter(t -> !t.getName().equals("Piccadilly Circus")).findAny(), is(Optional.empty()));
}
@Test
public void getTripsForDestinationNamesTest() throws Exception {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_destination.txt"));
/* List stops and verify some values */
List<Trip> trips = new UraClient("mocked").forDestinationNames("Piccadilly Circus").getTrips();
assertThat(trips, hasSize(9));
assertThat(trips.stream().filter(t -> !t.getDestinationName().equals("Piccadilly Cir")).findAny(),
is(Optional.empty()));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_stop_destination.txt"));
trips = new UraClient("mocked")
.forStops("156")
.forDestinationNames("Marble Arch")
.getTrips();
assertThat(trips, hasSize(5));
assertThat(trips.stream().filter(t -> !t.getStop().getId().equals("156")).findAny(),
is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getDestinationName().equals("Marble Arch")).findAny(),
is(Optional.empty()));
}
@Test
public void getTripsTowardsTest() throws Exception {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_towards.txt"));
/* List stops and verify some values */
List<Trip> trips = new UraClient("mocked").towards("Marble Arch").getTrips();
assertThat(trips, hasSize(10));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_stop_towards.txt"));
trips = new UraClient("mocked").forStops("156").towards("Marble Arch").getTrips();
assertThat(trips, hasSize(17));
assertThat(trips.stream().filter(t -> !t.getStop().getId().equals("156")).findAny(), is(Optional.empty()));
}
@Test
public void getTripsTest() throws Exception {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_all.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_all.txt"));
/* Get trips without filters and verify some values */
List<Trip> trips = new UraClient("mocked").getTrips();
@ -109,8 +196,26 @@ public class UraClientTest {
assertThat(trips.get(8).getVisitID(), is(30));
assertThat(trips.get(9).getStop().getId(), is("100002"));
/* Repeat test for API V2 */
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V2_trips_all.txt"));
/* Get trips without filters and verify some values */
trips = new UraClient("mocked").getTrips();
assertThat(trips, hasSize(10));
assertThat(trips.get(0).getId(), is("27000165015001"));
assertThat(trips.get(1).getLineID(), is("55"));
assertThat(trips.get(2).getLineName(), is("28"));;
assertThat(trips.get(3).getDirectionID(), is(1));
assertThat(trips.get(4).getDestinationName(), is("Verlautenheide Endstr."));
assertThat(trips.get(5).getDestinationText(), is("Aachen Bushof"));
assertThat(trips.get(6).getVehicleID(), is("247"));
assertThat(trips.get(7).getEstimatedTime(), is(1482854580000L));
assertThat(trips.get(8).getVisitID(), is(30));
assertThat(trips.get(9).getStop().getId(), is("100002"));
/* Get limited number of trips */
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_all.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_all.txt"));
trips = new UraClient("mocked").getTrips(5);
assertThat(trips, hasSize(5));
@ -121,9 +226,13 @@ public class UraClientTest {
throw new IOException("Provoked exception 1.");
}
});
assertThat(new UraClient("mocked").getTrips(), hasSize(0));
PowerMockito.when(mockURL.openStream()).thenThrow(new IOException("Provoked exception 2."));
assertThat(new UraClient("mocked").getTrips(), hasSize(0));
try {
new UraClient("mocked").getTrips();
} catch (RuntimeException e) {
assertThat(e, is(instanceOf(IllegalStateException.class)));
assertThat(e.getCause(), is(instanceOf(IOException.class)));
assertThat(e.getCause().getMessage(), is("Provoked exception 1."));
}
}
@Test
@ -131,7 +240,8 @@ public class UraClientTest {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_stop.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_stop.txt"));
/* Get trips for stop ID 100000 (Aachen Bushof) and verify some values */
List<Trip> trips = new UraClient("mocked")
@ -145,12 +255,14 @@ public class UraClientTest {
assertThat(trips.get(3).getStop().getIndicator(), is("H.15"));
/* Get trips for stop name "Uniklinik" and verify some values */
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_stop_name.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_stop_name.txt"));
trips = new UraClient("mocked")
.forStopsByName("Uniklinik")
.getTrips();
assertThat(trips, hasSize(10));
assertThat(trips.stream().filter(t -> !t.getStop().getName().equals("Uniklinik")).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getStop().getName().equals("Uniklinik")).findAny(),
is(Optional.empty()));
assertThat(trips.get(0).getId(), is("92000043013001"));
assertThat(trips.get(1).getLineID(), is("5"));
assertThat(trips.get(2).getVehicleID(), is("317"));;
@ -162,7 +274,8 @@ public class UraClientTest {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_line.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_line.txt"));
/* Get trips for line ID 3 and verify some values */
List<Trip> trips = new UraClient("mocked")
@ -176,7 +289,8 @@ public class UraClientTest {
assertThat(trips.get(3).getStop().getIndicator(), is("H.4 (Pontwall)"));
/* Get trips for line name "3.A" and verify some values */
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_line_name.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_line_name.txt"));
trips = new UraClient("mocked")
.forLinesByName("3.A")
.getTrips();
@ -188,24 +302,26 @@ public class UraClientTest {
assertThat(trips.get(3).getStop().getName(), is("Aachen Gartenstraße"));
/* Get trips for line 3 with direction 1 and verify some values */
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_line_direction.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_line_direction.txt"));
trips = new UraClient("mocked")
.forLines("3")
.forDirection(1)
.forLines("412")
.forDirection(2)
.getTrips();
assertThat(trips, hasSize(10));
assertThat(trips.stream().filter(t -> !t.getLineID().equals("3")).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getDirectionID().equals(1)).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getLineID().equals("412")).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getDirectionID().equals(2)).findAny(), is(Optional.empty()));
/* Test lineID and direction in different order */
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_line_direction.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_line_direction.txt"));
trips = new UraClient("mocked")
.forDirection(1)
.forLines("3")
.forDirection(2)
.forLines("412")
.getTrips();
assertThat(trips, hasSize(10));
assertThat(trips.stream().filter(t -> !t.getLineID().equals("3")).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getDirectionID().equals(1)).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getLineID().equals("412")).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getDirectionID().equals(2)).findAny(), is(Optional.empty()));
}
@Test
@ -213,7 +329,8 @@ public class UraClientTest {
/* Mock the HTTP call */
URL mockURL = PowerMockito.mock(URL.class);
PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockURL);
PowerMockito.when(mockURL.openStream()).thenReturn(getClass().getResourceAsStream("instant_trips_stop_line.txt"));
PowerMockito.when(mockURL.openStream())
.thenReturn(getClass().getResourceAsStream("instant_V1_trips_stop_line.txt"));
/* Get trips for line ID 25 and 25 at stop 100000 and verify some values */
List<Trip> trips = new UraClient("mocked")
@ -221,7 +338,8 @@ public class UraClientTest {
.forStops("100000")
.getTrips();
assertThat(trips, hasSize(10));
assertThat(trips.stream().filter(t -> !t.getLineID().equals("25") && !t.getLineID().equals("35")).findAny(), is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getLineID().equals("25") && !t.getLineID().equals("35")).findAny(),
is(Optional.empty()));
assertThat(trips.stream().filter(t -> !t.getStop().getId().equals("100000")).findAny(), is(Optional.empty()));
assertThat(trips.get(0).getId(), is("27000078014001"));
assertThat(trips.get(1).getLineID(), is("25"));

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed 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 de.stklcode.pubtrans.ura.model;
import org.junit.Test;
@ -15,7 +31,7 @@ import static org.junit.Assert.fail;
/**
* Unit test for the Stop metamodel.
*
* @author Stefan Kalscheuer <stefan@stklcode.de>
* @author Stefan Kalscheuer
*/
public class StopTest {
@Test

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016-2018 Stefan Kalscheuer
*
* Licensed 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 de.stklcode.pubtrans.ura.model;
import org.junit.Test;
@ -15,13 +31,26 @@ import static org.junit.Assert.fail;
/**
* Unit test for the Trip metamodel.
*
* @author Stefan Kalscheuer <stefan@stklcode.de>
* @author Stefan Kalscheuer
*/
public class TripTest {
@Test
public void basicConstructorTest() {
Trip trip = new Trip("sid", "name", "indicator", 1, 2.345, 6.789,
123, "lineid", "linename", 0, "destination name", "destination text", "vehicle", "id", 123456789123456789L);
Trip trip = new Trip("sid",
"name",
"indicator",
1,
2.345,
6.789,
123,
"lineid",
"linename",
0,
"destination name",
"destination text",
"vehicle",
"id",
123456789123456789L);
assertThat(trip.getStop().getId(), is("sid"));
assertThat(trip.getStop().getName(), is("name"));
assertThat(trip.getStop().getIndicator(), is("indicator"));
@ -57,7 +86,7 @@ public class TripTest {
raw.add("destination name");
raw.add("destination text");
raw.add("vehicle");
raw.add("id");
raw.add(9876543210L);
raw.add(123456789123456789L);
try {
@ -75,12 +104,22 @@ public class TripTest {
assertThat(trip.getDestinationName(), is("destination name"));
assertThat(trip.getDestinationText(), is("destination text"));
assertThat(trip.getVehicleID(), is("vehicle"));
assertThat(trip.getId(), is("id"));
assertThat(trip.getId(), is("9876543210"));
assertThat(trip.getEstimatedTime(), is(123456789123456789L));
} catch (IOException e) {
fail("Creation of Trip from valid list failed: " + e.getMessage());
}
/* Test with V2 style list */
raw.set(14, "id");
try {
Trip trip = new Trip(raw, "2.0");
assertThat(trip.getId(), is("id"));
} catch (IOException e) {
fail("Creation of Trip from valid list failed: " + e.getMessage());
}
raw.set(14, 9876543210L);
/* Excess elements should be ignored */
raw.add("foo");
try {
@ -164,7 +203,7 @@ public class TripTest {
invalid = new ArrayList<>(raw);
invalid.remove(14);
invalid.add(14, 123);
invalid.add(14, 1.2);
try {
new Trip(invalid);
fail("Creation of Trip with invalid id field successfull");
@ -190,5 +229,14 @@ public class TripTest {
} catch (Exception e) {
assertThat(e, is(instanceOf(IOException.class)));
}
invalid = new ArrayList<>(raw);
invalid.set(10, 3);
try {
new Trip(invalid);
fail("Creation of Trip with direction ID 3 successfull");
} catch (Exception e) {
assertThat(e, is(instanceOf(IOException.class)));
}
}
}

View File

@ -0,0 +1,14 @@
[4,"1.0",1483808583971]
[0,"Piccadilly Circus","156","D",0,51.509822,-0.136967]
[0,"Piccadilly Circus","149","G",0,51.51006,-0.135545]
[0,"Piccadilly Circus","155","C",0,51.50986,-0.135971]
[0,"Piccadilly Circus","34716","S",0,51.509154,-0.136245]
[0,"Trocadero / Haymarket","161","H",0,51.510515,-0.134172]
[0,"Piccadilly Circus, Haymarket","PCIRHM S",null,0,51.509709,-0.13272]
[0,"Regent Street / St James's","17683","Z",0,51.508732,-0.134043]
[0,"Regent Street / St James's","154","Y",0,51.508942,-0.134207]
[0,"Piccadilly Circus","BP5840","E",0,51.509882,-0.137325]
[0,"Piccadilly Circus","150","F",0,51.509894,-0.136402]
[0,"Trocadero / Haymarket","37229","K",0,51.511194,-0.133366]
[0,"Piccadilly Circus","37122","B",0,51.509306,-0.13621]
[0,"Haymarket / Jermyn Street","29867","R",0,51.509691,-0.132706]

View File

@ -0,0 +1,8 @@
[4,"1.0",1483808692732]
[0,"Piccadilly Circus","156","D",0,51.509822,-0.136967]
[0,"Piccadilly Circus","149","G",0,51.51006,-0.135545]
[0,"Piccadilly Circus","155","C",0,51.50986,-0.135971]
[0,"Piccadilly Circus","34716","S",0,51.509154,-0.136245]
[0,"Piccadilly Circus","BP5840","E",0,51.509882,-0.137325]
[0,"Piccadilly Circus","150","F",0,51.509894,-0.136402]
[0,"Piccadilly Circus","37122","B",0,51.509306,-0.13621]

View File

@ -0,0 +1,10 @@
[4,"1.0",1483809193233]
[1,"New Bond Street","829","OE",0,51.514814,-0.146117,1,"94","94",1,"Piccadilly Cir","Piccadilly Circus",15925,443778,1483810113000]
[1,"New Bond Street","829","OE",0,51.514814,-0.146117,1,"94","94",1,"Piccadilly Cir","Piccadilly Circus",15209,443756,1483810614000]
[1,"Hobury Street / Worlds End","11296","QH",0,51.482463,-0.179129,1,"22","22",1,"Piccadilly Cir","Piccadilly Circus",8923,390782,1483810037000]
[1,"Abinger Road","11333",null,0,51.496971,-0.24965,1,"94","94",1,"Piccadilly Cir","Piccadilly Circus",15554,444015,1483809664000]
[1,"Knightsbridge Station","210","KJ",0,51.500899,-0.160183,1,"22","22",1,"Piccadilly Cir","Piccadilly Circus",7988,392720,1483810456000]
[1,"Piccadilly Circus","37122","B",0,51.509306,-0.13621,1,"22","22",1,"Piccadilly Cir","Piccadilly Circus",8027,502319,1483809941000]
[1,"Edith Grove / Worlds End","37444",null,0,51.481603,-0.182217,1,"22","22",1,"Piccadilly Cir","Piccadilly Circus",8015,392809,1483810291000]
[1,"Selfridges","153","BB",0,51.514067,-0.152921,1,"94","94",1,"Piccadilly Cir","Piccadilly Circus",15105,444021,1483809200000]
[1,"Edith Grove / Worlds End","37444",null,0,51.481603,-0.182217,1,"22","22",1,"Piccadilly Cir","Piccadilly Circus",7988,392720,1483809409000]

View File

@ -1,11 +1,11 @@
[4,"2.0",1482854839773]
[1,"Seffenter Weg","100643","",0,50.7820069,6.0644119,19,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","317","27000154004001",1482855824000]
[1,"Normaluhr","100009","H.5 (Wilhelmstr.)",0,50.7680616,6.094883,9,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","226","27000134003001",1482855960000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,5,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","226","27000134002001",1482854820000]
[1,"Ponttor","100005","H.4 (Pontwall)",0,50.7808611,6.0779722,15,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","226","27000134003001",1482856500000]
[1,"Stiewistraße","100630","",0,50.78029,6.05058,25,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","226","27000134004001",1482859800000]
[1,"Misereor","100010","",0,50.7685583,6.0833027,7,"3","3.A",1,"Hbf.-Ponttor-Uniklinik","Hbf.-Ponttor-Uniklinik","0","27000134005001",1482861060000]
[1,"Wendlingweg","100646","",0,50.7773216,6.0504405,26,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","338","27000128004001",1482858060000]
[1,"Hörn Brücke","100627","",0,50.7871194,6.051875,22,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0","27000141015001",1482861420000]
[1,"Forckenbeckstraße","100628","",0,50.78534,6.05054,23,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","338","27000128003001",1482855127000]
[1,"Hörn Brücke","100627","",0,50.7871194,6.051875,22,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","338","27000128003001",1482855067000]
[1,"Seffenter Weg","100643","",0,50.7820069,6.0644119,19,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","317",27000154004001,1482855824000]
[1,"Normaluhr","100009","H.5 (Wilhelmstr.)",0,50.7680616,6.094883,9,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","226",27000134003001,1482855960000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,5,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","226",27000134002001,1482854820000]
[1,"Ponttor","100005","H.4 (Pontwall)",0,50.7808611,6.0779722,15,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","226",27000134003001,1482856500000]
[1,"Stiewistraße","100630","",0,50.78029,6.05058,25,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","226",27000134004001,1482859800000]
[1,"Misereor","100010","",0,50.7685583,6.0833027,7,"3","3.A",1,"Hbf.-Ponttor-Uniklinik","Hbf.-Ponttor-Uniklinik","0",27000134005001,1482861060000]
[1,"Wendlingweg","100646","",0,50.7773216,6.0504405,26,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","338",27000128004001,1482858060000]
[1,"Hörn Brücke","100627","",0,50.7871194,6.051875,22,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0",27000141015001,1482861420000]
[1,"Forckenbeckstraße","100628","",0,50.78534,6.05054,23,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","338",27000128003001,1482855127000]
[1,"Hörn Brücke","100627","",0,50.7871194,6.051875,22,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","338",27000128003001,1482855067000]

View File

@ -0,0 +1,11 @@
[4,"1.0",1483806258560]
[1,"Lower Barn Road","16637",null,0,51.330106,-0.093099,1,"412","412",2,"Purley","Purley",3469,653171,1483807114000]
[1,"Sanderstead Plantation","23493",null,0,51.339851,-0.069809,1,"412","412",2,"Purley","Purley",2161,653155,1483807649000]
[1,"Christchurch Road / Purley Hospital","10825","E",0,51.340668,-0.113337,1,"412","412",2,"Purley","Purley",3146,653175,1483806540000]
[1,"Ridge Langley","20396",null,0,51.346848,-0.072013,1,"412","412",2,"Purley","Purley",2161,653155,1483807260000]
[1,"Purley / Downlands Precinct","4396","D",0,51.339165,-0.116083,1,"412","412",2,"Purley","Purley",3469,653171,1483807453000]
[1,"Coombe Road","10829","W",0,51.368209,-0.099175,1,"412","412",2,"Purley","Purley",2161,653155,1483806733000]
[1,"Ruskin Parade / South Croydon Station","4415","F",0,51.363068,-0.097132,1,"412","412",2,"Purley","Purley",1554,653151,1483807590000]
[1,"Sussex Road","18810","GP",0,51.359344,-0.093811,1,"412","412",2,"Purley","Purley",1554,653151,1483807686000]
[1,"Farley Road","26185","Z",0,51.342569,-0.063937,1,"412","412",2,"Purley","Purley",1554,653151,1483808161000]
[1,"Sanderstead Plantation","23493",null,0,51.339851,-0.069809,1,"412","412",2,"Purley","Purley",3469,653171,1483806820000]

View File

@ -0,0 +1,11 @@
[4,"2.0",1483362794677]
[1,"Westbahnhof","100641","H.2",0,50.7805372,6.0719672,17,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0",92000288014001,1483367880000]
[1,"Aachen Gartenstraße","100601","",0,50.7704169,6.0695841,4,"3","3.A",1,"Schanz-Hbf.-Ponttor","Schanz-Hbf.-Ponttor","0",92000058014001,1483368360000]
[1,"Schanz","100012","H.2 (Boxgraben)",0,50.76891,6.074498,5,"3","3.A",1,"Hbf.-Ponttor-Uniklinik","Hbf.-Ponttor-Uniklinik","0",92000288015001,1483369380000]
[1,"Aachen Gartenstraße","100601","",0,50.7704169,6.0695841,4,"3","3.A",1,"Schanz-Hbf.-Ponttor","Schanz-Hbf.-Ponttor","327",92000289012001,1483363045000]
[1,"Kastanienweg","100631","",0,50.7854908,6.0547602,21,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0",92000058014001,1483369860000]
[1,"Eurogress","100007","",0,50.7807852,6.0900738,13,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","327",92000289012001,1483363994000]
[1,"Augustastraße","100008","",0,50.7731333,6.0959027,10,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","0",92000288014001,1483367280000]
[1,"Hörn Brücke","100627","",0,50.7871194,6.051875,22,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0",92000279013001,1483369020000]
[1,"Audimax","100029","",0,50.7802655,6.0752138,16,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0",92000058014001,1483369560000]
[1,"Aachen Hauptbahnhof","100004","H.1",0,50.7687027,6.0906277,8,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","0",92000288014001,1483367040000]

View File

@ -0,0 +1,11 @@
[4,"2.0",1482854457011]
[1,"Aachen Bushof","100000","H.13",0,50.7775936,6.0908191,20,"1","1",1,"Schevenhütte","Schevenhütte","565",27000158010001,1482854446000]
[1,"Aachen Bushof","100000","H.2",0,50.7775936,6.0908191,8,"7","7",1,"Aachen Bushof","Aachen Bushof","514",27000210017001,1482854400000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","0",27000171010001,1482854460000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,17,"47","47",1,"Aachen Bushof","Aachen Bushof","258",27000139003001,1482854460000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,1,"7","7",1,"Aachen Diepenbenden","Aachen Diepenbenden","257",27000089026001,1482854454000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,17,"2","2",1,"Eilendorf Schubertstr.","Eilendorf Schubertstr.","221",27000039021001,1482854445000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,28,"12","12",1,"Campus Melaten","Campus Melaten","298",27000062010001,1482854451000]
[1,"Aachen Bushof","100000","H.1",0,50.7775936,6.0908191,27,"51","51",1,"Aachen Bushof","Aachen Bushof","603",27000230015001,1482854801000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,1,"43","43",1,"Hüls Schulz+Elleter F.","Hüls Schulz+Elleter F.","258",27000139004001,1482854520000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,25,"33","33",1,"Aachen Fuchserde","Aachen Fuchserde","286",27000033017001,1482854459000]

View File

@ -0,0 +1,6 @@
[4,"1.0",1483809267076]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16588,309783,1483809502000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16589,239190,1483809796000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16211,310616,1483810325000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16549,238574,1483810733000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16213,216852,1483810840000]

View File

@ -1,11 +1,11 @@
[4,"2.0",1482855134910]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,32,"35","35",1,"Vaals Grenze","Vaals Grenze","346","27000078014001",1482855350000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,15,"25","25",1,"Stolberg Mühlener Bf.","Stolberg Mühlener Bf.","334","27000268014001",1482856194000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","242","27000270010001",1482856248000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,14,"35","35",1,"Breinig Entengasse","Breinig Entengasse","248","27000074012001",1482856740000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,32,"35","35",1,"Vaals Grenze","Vaals Grenze","294","27000076010001",1482857507000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,15,"25","25",1,"Stolberg Mühlener Bf.","Stolberg Mühlener Bf.","0","27000171011001",1482857640000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","191","27000032012001",1482858057000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,14,"35","35",1,"Breinig Entengasse","Breinig Entengasse","346","27000078015001",1482858540000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,15,"25","25",1,"Stolberg Mühlener Bf.","Stolberg Mühlener Bf.","242","27000270011001",1482859440000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","187","27000060013001",1482859860000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,32,"35","35",1,"Vaals Grenze","Vaals Grenze","346",27000078014001,1482855350000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,15,"25","25",1,"Stolberg Mühlener Bf.","Stolberg Mühlener Bf.","334",27000268014001,1482856194000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","242",27000270010001,1482856248000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,14,"35","35",1,"Breinig Entengasse","Breinig Entengasse","248",27000074012001,1482856740000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,32,"35","35",1,"Vaals Grenze","Vaals Grenze","294",27000076010001,1482857507000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,15,"25","25",1,"Stolberg Mühlener Bf.","Stolberg Mühlener Bf.","0",27000171011001,1482857640000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","191",27000032012001,1482858057000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,14,"35","35",1,"Breinig Entengasse","Breinig Entengasse","346",27000078015001,1482858540000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,15,"25","25",1,"Stolberg Mühlener Bf.","Stolberg Mühlener Bf.","242",27000270011001,1482859440000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","187",27000060013001,1482859860000]

View File

@ -0,0 +1,11 @@
[4,"2.0",1483362959788]
[1,"Uniklinik","100600","H.2",0,50.7756388,6.04425,11,"33","33",1,"Aachen Fuchserde","Aachen Fuchserde","318",92000043013001,1483362935000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,1,"5","5",1,"Driescher Hof-Brand","Driescher Hof-Brand","312",92000282009001,1483362936000]
[1,"Uniklinik","100600","H.4",0,50.7756388,6.04425,33,"45","45",1,"Uniklinik","Uniklinik","317",92000285009001,1483363294000]
[1,"Uniklinik","100600","H.3",0,50.7756388,6.04425,28,"10","3.B",1,"Uniklinik-Ponttor-Hbf.","Uniklinik-Ponttor-Hbf.","347",92000053015001,1483363039000]
[1,"Uniklinik","100600","H.3",0,50.7756388,6.04425,29,"33","33",1,"Uniklinik","Uniklinik","529",92000209014001,1483363288000]
[1,"Uniklinik","100600","H.2",0,50.7756388,6.04425,1,"73","73",1,"Aachen Bf.Rothe Erde","Aachen Bf.Rothe Erde","315",92000291016001,1483363080000]
[1,"Uniklinik","100600","H.3",0,50.7756388,6.04425,29,"10","3.B",1,"Uniklinik-Ponttor-Hbf.","Uniklinik-Ponttor-Hbf.","347",92000053015001,1483363099000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,28,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","325",92000288012001,1483363080000]
[1,"Uniklinik","100600","H.2",0,50.7756388,6.04425,6,"70","70",1,"Aachen Adenauerallee","Aachen Adenauerallee","588",92000225009001,1483363346000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,1,"3","3.A",1,"Schanz-Hbf.-Ponttor","Schanz-Hbf.-Ponttor","325",92000288013001,1483363380000]

View File

@ -0,0 +1,18 @@
[4,"1.0",1483809773487]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"94","94",2,"Acton Green","Acton Green",15294,445809,1483809898000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16589,239190,1483809806000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"6","6",2,"Willesden Gar","Willesden, Bus Garage",10600,216033,1483810030000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"23","23",2,"Westbourne Park","Westbourne Park",20276,685207,1483810231000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"94","94",2,"Acton Green","Acton Green",15296,445944,1483810322000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"6","6",2,"Willesden Gar","Willesden, Bus Garage",10603,216027,1483810421000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16211,310616,1483810135000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"94","94",2,"Acton Green","Acton Green",15105,445989,1483810449000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"23","23",2,"Westbourne Park","Westbourne Park",20322,690792,1483810642000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"94","94",2,"Acton Green","Acton Green",15575,445855,1483810750000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16213,216852,1483810840000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"6","6",2,"Willesden Gar","Willesden, Bus Garage",10602,216822,1483810854000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"23","23",2,"Westbourne Park","Westbourne Park",20337,689508,1483811038000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16549,238574,1483810791000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"94","94",2,"Acton Green","Acton Green",15925,445840,1483811317000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"6","6",2,"Willesden Gar","Willesden, Bus Garage",10596,216025,1483811331000]
[1,"Piccadilly Circus","156","D",0,51.509822,-0.136967,1,"159","159",1,"Marble Arch","Marble Arch",16713,310640,1483811409000]

View File

@ -0,0 +1,11 @@
[4,"1.0",1483810124081]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"36","36",1,"New Cross Gate","New Cross Gate",8864,595212,1483810191000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"7","7",1,"Oxford Circus","Oxford Circus",9932,109224,1483810173000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"36","36",1,"New Cross Gate","New Cross Gate",8862,597163,1483810551000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"23","23",1,"Liverpool St","Liverpool Street",20333,690728,1483810367000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"7","7",1,"Oxford Circus","Oxford Circus",9861,106419,1483810702000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"36","36",1,"New Cross Gate","New Cross Gate",9407,599527,1483810830000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"23","23",1,"Liverpool St","Liverpool Street",20335,683419,1483810677000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"36","36",1,"New Cross Gate","New Cross Gate",9379,599540,1483811196000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"7","7",1,"Oxford Circus","Oxford Circus",10262,109245,1483811093000]
[1,"Porchester Terrace North","16972","L",0,51.516451,-0.186379,1,"23","23",1,"Liverpool St","Liverpool Street",20099,684947,1483811250000]

View File

@ -0,0 +1,11 @@
[4,"2.0",1482850556146]
[1,"Fischbachstraße","215812","",0,50.73893,6.2666311,6,"8","8",1,"Eschweiler Bushof","Eschweiler Bushof","0",27000165015001,1482856620000]
[1,"Wolferskaul","100322","",0,50.7439086,6.1594241,19,"55","55",1,"Aachen Elisenbrunnen","Aachen Elisenbrunnen","619",27000229015001,1482852180000]
[1,"Lindenallee","213227","",0,50.8271524,6.3194636,8,"28","28",1,"Alsdorf Annapark","Alsdorf Annapark","519",27000190012001,1482853140000]
[1,"Karlsgraben","100024","",0,50.7728605,6.0766513,6,"5","5",1,"Driescher Hof-Brand","Driescher Hof-Brand","0",27000283012001,1482855960000]
[1,"Eilendorf Bahnhof","100246","",0,50.7855477,6.1521783,61,"57","57",1,"Verlautenheide Endstr.","Verlautenheide Endstr.","528",27000200015001,1482855908000]
[1,"Elisenbrunnen","100001","H.1",0,50.7747372,6.0879925,24,"34","34",1,"Aachen Bushof","Aachen Bushof","670",27000237013001,1482854520000]
[1,"Schneidmühle","215602","",0,50.7847038,6.2190627,42,"22","22",1,"Stolberg Mühlener Bf.","Stolberg Mühlener Bf.","247",27000118005001,1482855180000]
[1,"Dechant-Brock-Str.","215824","",0,50.7592927,6.2735355,22,"1","1",1,"Lintert Friedhof","Lintert Friedhof","268",27000102007001,1482854580000]
[1,"Am Tiergarten","100382","",0,50.755833,6.1689597,30,"34","34",1,"Brand","Brand","0",27000224012001,1482854040000]
[1,"Hansemannplatz","100002","H.1",0,50.7784794,6.0959816,26,"51","51",1,"Aachen Bushof","Aachen Bushof","603",27000230015001,1482854400000]

View File

@ -1,11 +0,0 @@
[4,"2.0",1483363349461]
[1,"Westbahnhof","100641","H.2",0,50.7805372,6.0719672,17,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","325","92000288014001",1483367880000]
[1,"Aachen Gartenstraße","100601","",0,50.7704169,6.0695841,4,"3","3.A",1,"Schanz-Hbf.-Ponttor","Schanz-Hbf.-Ponttor","229","92000058014001",1483368360000]
[1,"Schanz","100012","H.2 (Boxgraben)",0,50.76891,6.074498,5,"3","3.A",1,"Hbf.-Ponttor-Uniklinik","Hbf.-Ponttor-Uniklinik","0","92000288015001",1483369380000]
[1,"Kastanienweg","100631","",0,50.7854908,6.0547602,21,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","229","92000058014001",1483369860000]
[1,"Eurogress","100007","",0,50.7807852,6.0900738,13,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","327","92000289012001",1483363996000]
[1,"Augustastraße","100008","",0,50.7731333,6.0959027,10,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","325","92000288014001",1483367280000]
[1,"Hörn Brücke","100627","",0,50.7871194,6.051875,22,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0","92000279013001",1483369020000]
[1,"Audimax","100029","",0,50.7802655,6.0752138,16,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0","92000288015001",1483370460000]
[1,"Misereor","100010","",0,50.7685583,6.0833027,7,"3","3.A",1,"Hbf.-Ponttor-Uniklinik","Hbf.-Ponttor-Uniklinik","0","92000154003001",1483370460000]
[1,"Audimax","100029","",0,50.7802655,6.0752138,16,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","229","92000058014001",1483369560000]

View File

@ -1,11 +0,0 @@
[4,"2.0",1483362794677]
[1,"Westbahnhof","100641","H.2",0,50.7805372,6.0719672,17,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0","92000288014001",1483367880000]
[1,"Aachen Gartenstraße","100601","",0,50.7704169,6.0695841,4,"3","3.A",1,"Schanz-Hbf.-Ponttor","Schanz-Hbf.-Ponttor","0","92000058014001",1483368360000]
[1,"Schanz","100012","H.2 (Boxgraben)",0,50.76891,6.074498,5,"3","3.A",1,"Hbf.-Ponttor-Uniklinik","Hbf.-Ponttor-Uniklinik","0","92000288015001",1483369380000]
[1,"Aachen Gartenstraße","100601","",0,50.7704169,6.0695841,4,"3","3.A",1,"Schanz-Hbf.-Ponttor","Schanz-Hbf.-Ponttor","327","92000289012001",1483363045000]
[1,"Kastanienweg","100631","",0,50.7854908,6.0547602,21,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0","92000058014001",1483369860000]
[1,"Eurogress","100007","",0,50.7807852,6.0900738,13,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","327","92000289012001",1483363994000]
[1,"Augustastraße","100008","",0,50.7731333,6.0959027,10,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","0","92000288014001",1483367280000]
[1,"Hörn Brücke","100627","",0,50.7871194,6.051875,22,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0","92000279013001",1483369020000]
[1,"Audimax","100029","",0,50.7802655,6.0752138,16,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","0","92000058014001",1483369560000]
[1,"Aachen Hauptbahnhof","100004","H.1",0,50.7687027,6.0906277,8,"3","3.A",1,"Ponttor-Unikl.-Schanz","Ponttor-Unikl.-Schanz","0","92000288014001",1483367040000]

View File

@ -1,11 +0,0 @@
[4,"2.0",1482854457011]
[1,"Aachen Bushof","100000","H.13",0,50.7775936,6.0908191,20,"1","1",1,"Schevenhütte","Schevenhütte","565","27000158010001",1482854446000]
[1,"Aachen Bushof","100000","H.2",0,50.7775936,6.0908191,8,"7","7",1,"Aachen Bushof","Aachen Bushof","514","27000210017001",1482854400000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,30,"25","25",1,"Vaals Busstation","Vaals Busstation","0","27000171010001",1482854460000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,17,"47","47",1,"Aachen Bushof","Aachen Bushof","258","27000139003001",1482854460000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,1,"7","7",1,"Aachen Diepenbenden","Aachen Diepenbenden","257","27000089026001",1482854454000]
[1,"Aachen Bushof","100000","H.12",0,50.7775936,6.0908191,17,"2","2",1,"Eilendorf Schubertstr.","Eilendorf Schubertstr.","221","27000039021001",1482854445000]
[1,"Aachen Bushof","100000","H.11",0,50.7775936,6.0908191,28,"12","12",1,"Campus Melaten","Campus Melaten","298","27000062010001",1482854451000]
[1,"Aachen Bushof","100000","H.1",0,50.7775936,6.0908191,27,"51","51",1,"Aachen Bushof","Aachen Bushof","603","27000230015001",1482854801000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,1,"43","43",1,"Hüls Schulz+Elleter F.","Hüls Schulz+Elleter F.","258","27000139004001",1482854520000]
[1,"Aachen Bushof","100000","H.15",0,50.7775936,6.0908191,25,"33","33",1,"Aachen Fuchserde","Aachen Fuchserde","286","27000033017001",1482854459000]

View File

@ -1,11 +0,0 @@
[4,"2.0",1483362959788]
[1,"Uniklinik","100600","H.2",0,50.7756388,6.04425,11,"33","33",1,"Aachen Fuchserde","Aachen Fuchserde","318","92000043013001",1483362935000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,1,"5","5",1,"Driescher Hof-Brand","Driescher Hof-Brand","312","92000282009001",1483362936000]
[1,"Uniklinik","100600","H.4",0,50.7756388,6.04425,33,"45","45",1,"Uniklinik","Uniklinik","317","92000285009001",1483363294000]
[1,"Uniklinik","100600","H.3",0,50.7756388,6.04425,28,"10","3.B",1,"Uniklinik-Ponttor-Hbf.","Uniklinik-Ponttor-Hbf.","347","92000053015001",1483363039000]
[1,"Uniklinik","100600","H.3",0,50.7756388,6.04425,29,"33","33",1,"Uniklinik","Uniklinik","529","92000209014001",1483363288000]
[1,"Uniklinik","100600","H.2",0,50.7756388,6.04425,1,"73","73",1,"Aachen Bf.Rothe Erde","Aachen Bf.Rothe Erde","315","92000291016001",1483363080000]
[1,"Uniklinik","100600","H.3",0,50.7756388,6.04425,29,"10","3.B",1,"Uniklinik-Ponttor-Hbf.","Uniklinik-Ponttor-Hbf.","347","92000053015001",1483363099000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,28,"3","3.A",1,"Uniklinik-Schanz-Hbf.","Uniklinik-Schanz-Hbf.","325","92000288012001",1483363080000]
[1,"Uniklinik","100600","H.2",0,50.7756388,6.04425,6,"70","70",1,"Aachen Adenauerallee","Aachen Adenauerallee","588","92000225009001",1483363346000]
[1,"Uniklinik","100600","H.1",0,50.7756388,6.04425,1,"3","3.A",1,"Schanz-Hbf.-Ponttor","Schanz-Hbf.-Ponttor","325","92000288013001",1483363380000]