From f6211c91f8477ca76286d7fa88206488203130ca Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Mon, 3 Dec 2018 16:36:29 +0100
Subject: [PATCH 01/20] Hydra parser fix for De Lijn connections
---
src/demo.ts | 23 +++++++++++++++----
.../connections/ld-fetch/HydraPageParser.ts | 15 +++++++++++-
src/inversify.config.ts | 11 +++++++--
3 files changed, 42 insertions(+), 7 deletions(-)
diff --git a/src/demo.ts b/src/demo.ts
index 3d770876..4dbd74f7 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -21,10 +21,11 @@ export default async (logResults) => {
const publicTransportResult = await planner.query({
publicTransportOnly: true,
- from: "http://irail.be/stations/NMBS/008896925", // Ingelmunster
- to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
+ from: "https://data.delijn.be/stops/201657", // Ingelmunster
+ to: "https://data.delijn.be/stops/205910", // Ghent-Sint-Pieters
minimumDepartureTime: new Date(),
- maximumTransferDuration: Units.fromHours(.5),
+ maximumTransfers: 2,
+ maximumTransferDuration: Units.fromHours(.05),
});
console.timeEnd("Public transport planner");
@@ -35,7 +36,21 @@ export default async (logResults) => {
let path = publicTransportResult.read();
while (path && i < 5) {
- console.log(i++, path);
+ // console.log(i++, path);
+
+ console.log(i++);
+
+ path.steps.forEach((step) => {
+ console.log(step.startTime);
+ console.log(step.startLocation.name);
+ console.log(step.travelMode);
+ console.log(step.stopTime);
+ console.log(step.stopLocation.name);
+ console.log("");
+ });
+
+ console.log("");
+ console.log("");
path = publicTransportResult.read();
}
diff --git a/src/fetcher/connections/ld-fetch/HydraPageParser.ts b/src/fetcher/connections/ld-fetch/HydraPageParser.ts
index 7a0adcf0..e28779a3 100644
--- a/src/fetcher/connections/ld-fetch/HydraPageParser.ts
+++ b/src/fetcher/connections/ld-fetch/HydraPageParser.ts
@@ -63,7 +63,10 @@ export default class HydraPageParser {
}
private getDocumentIri(): string {
- const typeTriple = this.triples.find(
+ // Type can be either http://www.w3.org/ns/hydra/core#PartialCollectionView
+ // or http://www.w3.org/ns/hydra/core#PagedCollection
+
+ let typeTriple = this.triples.find(
Rdf.matchesTriple(
null,
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
@@ -71,6 +74,16 @@ export default class HydraPageParser {
),
);
+ if (!typeTriple) {
+ typeTriple = this.triples.find(
+ Rdf.matchesTriple(
+ null,
+ "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
+ "http://www.w3.org/ns/hydra/core#PagedCollection",
+ ),
+ );
+ }
+
if (!typeTriple) {
throw new Error("Hydra page doesn`t have type triple");
}
diff --git a/src/inversify.config.ts b/src/inversify.config.ts
index ad05a770..e790b646 100644
--- a/src/inversify.config.ts
+++ b/src/inversify.config.ts
@@ -82,9 +82,16 @@ container.bind>(TYPES.StopsFetcherFactory)
);
// Init catalog
+// const catalog = new Catalog();
+// catalog.addStopsFetcher("http://irail.be/stations/NMBS/", "https://irail.be/stations/NMBS");
+// catalog.addConnectionsFetcher("https://graph.irail.be/sncb/connections", TravelMode.Train);
+
const catalog = new Catalog();
-catalog.addStopsFetcher("http://irail.be/stations/NMBS/", "https://irail.be/stations/NMBS");
-catalog.addConnectionsFetcher("https://graph.irail.be/sncb/connections", TravelMode.Train);
+catalog.addStopsFetcher(
+ "https://data.delijn.be/stops/",
+ "https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/stops",
+);
+catalog.addConnectionsFetcher("https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/connections", TravelMode.Bus);
container.bind(TYPES.Catalog).toConstantValue(catalog);
From 1121d89d0a04c4155edcbae757de9d40dc055082 Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Tue, 4 Dec 2018 12:00:55 +0100
Subject: [PATCH 02/20] Implement MergeIterator
---
src/demo.ts | 3 +-
...erator.ts => FilterUniquePathsIterator.ts} | 2 +-
.../exponential/QueryRunnerExponential.ts | 6 +-
src/util/MergeIterator.test.ts | 70 ++++++++++
src/util/MergeIterator.ts | 125 ++++++++++++++++++
5 files changed, 200 insertions(+), 6 deletions(-)
rename src/query-runner/exponential/{FilterUniqueIterator.ts => FilterUniquePathsIterator.ts} (84%)
create mode 100644 src/util/MergeIterator.test.ts
create mode 100644 src/util/MergeIterator.ts
diff --git a/src/demo.ts b/src/demo.ts
index 4dbd74f7..1cf6e61d 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -24,8 +24,7 @@ export default async (logResults) => {
from: "https://data.delijn.be/stops/201657", // Ingelmunster
to: "https://data.delijn.be/stops/205910", // Ghent-Sint-Pieters
minimumDepartureTime: new Date(),
- maximumTransfers: 2,
- maximumTransferDuration: Units.fromHours(.05),
+ maximumTransferDuration: Units.fromHours(.01),
});
console.timeEnd("Public transport planner");
diff --git a/src/query-runner/exponential/FilterUniqueIterator.ts b/src/query-runner/exponential/FilterUniquePathsIterator.ts
similarity index 84%
rename from src/query-runner/exponential/FilterUniqueIterator.ts
rename to src/query-runner/exponential/FilterUniquePathsIterator.ts
index d937d256..6478b97d 100644
--- a/src/query-runner/exponential/FilterUniqueIterator.ts
+++ b/src/query-runner/exponential/FilterUniquePathsIterator.ts
@@ -2,7 +2,7 @@ import { AsyncIterator, SimpleTransformIterator } from "asynciterator";
import IPath from "../../interfaces/IPath";
import Path from "../../planner/Path";
-export default class FilterUniqueIterator extends SimpleTransformIterator {
+export default class FilterUniquePathsIterator extends SimpleTransformIterator {
private store: Path[];
diff --git a/src/query-runner/exponential/QueryRunnerExponential.ts b/src/query-runner/exponential/QueryRunnerExponential.ts
index d5e2f3f2..2397234f 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.ts
@@ -11,7 +11,7 @@ import ILocationResolver from "../ILocationResolver";
import IQueryRunner from "../IQueryRunner";
import IResolvedQuery from "../IResolvedQuery";
import ExponentialQueryIterator from "./ExponentialQueryIterator";
-import FilterUniqueIterator from "./FilterUniqueIterator";
+import FilterUniquePathsIterator from "./FilterUniquePathsIterator";
import SubqueryIterator from "./SubqueryIterator";
@injectable()
@@ -40,7 +40,7 @@ export default class QueryRunnerExponential implements IQueryRunner {
const queryIterator = new ExponentialQueryIterator(baseQuery, 15 * 60 * 1000);
const subqueryIterator = new SubqueryIterator(queryIterator, this.runSubquery.bind(this));
- return new FilterUniqueIterator(subqueryIterator);
+ return new FilterUniquePathsIterator(subqueryIterator);
} else {
return Promise.reject("Query not supported");
@@ -48,7 +48,7 @@ export default class QueryRunnerExponential implements IQueryRunner {
}
private async runSubquery(query: IResolvedQuery): Promise> {
- // TODO investigate publicTransportPlanner
+ // TODO investigate if publicTransportPlanner can be reused or reuse some of its aggregated data
const planner = this.context.getContainer().get(TYPES.PublicTransportPlanner);
return planner.plan(query);
diff --git a/src/util/MergeIterator.test.ts b/src/util/MergeIterator.test.ts
new file mode 100644
index 00000000..6e84a808
--- /dev/null
+++ b/src/util/MergeIterator.test.ts
@@ -0,0 +1,70 @@
+import { ArrayIterator } from "asynciterator";
+import "jest";
+import MergeIterator from "./MergeIterator";
+
+const createSources = () => {
+ const firstIterator = new ArrayIterator([1, 8, 10, 13]);
+ const secondIterator = new ArrayIterator([4, 11]);
+ const thirdIterator = new ArrayIterator([3, 5, 6, 12, 18]);
+
+ return [firstIterator, secondIterator, thirdIterator];
+};
+
+describe("[MergeIterator]", () => {
+
+ it("uncondensed", (done) => {
+
+ const mergeIterator = new MergeIterator(createSources(), (numbers: number[]) => {
+
+ let smallestIndex = -1;
+
+ for (let i = 0; i < numbers.length; i++) {
+ if (smallestIndex < 0 && numbers[i] !== undefined) {
+ smallestIndex = i;
+ continue;
+ }
+
+ if (numbers[i] !== undefined && numbers[i] < numbers[smallestIndex]) {
+ smallestIndex = i;
+ }
+ }
+
+ return smallestIndex;
+ });
+
+ let current = 0;
+ const expected = [1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 18];
+
+ mergeIterator.each((str) => {
+ expect(expected[current++]).toBe(str);
+ });
+
+ mergeIterator.on("end", () => done());
+ });
+
+ it("condensed", (done) => {
+
+ const mergeIterator = new MergeIterator(createSources(), (numbers: number[]) => {
+
+ let smallestIndex = 0;
+
+ for (let i = 1; i < numbers.length; i++) {
+ if (numbers[i] !== undefined && numbers[i] < numbers[smallestIndex]) {
+ smallestIndex = i;
+ }
+ }
+
+ return smallestIndex;
+ }, true);
+
+ let current = 0;
+ const expected = [1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 18];
+
+ mergeIterator.each((str) => {
+ expect(expected[current++]).toBe(str);
+ });
+
+ mergeIterator.on("end", () => done());
+ });
+
+});
diff --git a/src/util/MergeIterator.ts b/src/util/MergeIterator.ts
new file mode 100644
index 00000000..9b4d9674
--- /dev/null
+++ b/src/util/MergeIterator.ts
@@ -0,0 +1,125 @@
+import { AsyncIterator, BufferedIterator } from "asynciterator";
+
+type MergeIteratorSelector = (values: T[]) => number;
+
+export default class MergeIterator extends BufferedIterator {
+ private readonly sourceIterators: Array>;
+ private readonly selector: MergeIteratorSelector;
+ private readonly condensed: boolean;
+
+ private values: T[];
+ private lastPushedIndex: number;
+ private endedSources: number;
+ private shouldClose: boolean;
+
+ constructor(sourceIterators: Array>, selector: MergeIteratorSelector, condensed?: boolean) {
+ super();
+
+ this.sourceIterators = sourceIterators;
+ this.selector = selector;
+ this.condensed = condensed;
+
+ this.endedSources = 0;
+
+ this.addListeners();
+ }
+
+ public _read(count: number, done: () => void): void {
+ if (!this.values) {
+ this.fillFirstValues(done);
+ return;
+ }
+
+ this.fillValue(this.lastPushedIndex, () => {
+ this.doPush(done);
+ });
+ }
+
+ private fillFirstValues(done) {
+ this.values = Array(this.sourceIterators.length).fill(undefined);
+ let filledValues = 0;
+
+ const filled = () => {
+ filledValues++;
+
+ if (filledValues === this.sourceIterators.length) {
+ this.doPush(done);
+ }
+ };
+
+ for (let i = 0; i < this.sourceIterators.length; i++) {
+ this.fillValue(i, filled);
+ }
+ }
+
+ private fillValue(sourceIndex: number, filled: () => void) {
+ const iterator = this.sourceIterators[sourceIndex];
+
+ if (iterator.ended) {
+ filled();
+ return;
+ }
+
+ const value = iterator.read();
+
+ if (value) {
+ this.values[sourceIndex] = value;
+ filled();
+
+ } else {
+ iterator.once("readable", () => {
+ this.values[sourceIndex] = iterator.read();
+ filled();
+ });
+ }
+ }
+
+ private doPush(done: () => void) {
+ if (this.condensed) {
+ const { values, indexMap } = this.getCondensedValues();
+
+ this.lastPushedIndex = indexMap[this.selector(values)];
+
+ } else {
+ this.lastPushedIndex = this.selector(this.values);
+ }
+
+ this._push(this.values[this.lastPushedIndex]);
+ this.values[this.lastPushedIndex] = undefined;
+
+ done();
+
+ if (this.shouldClose) {
+ this.close();
+ }
+ }
+
+ private getCondensedValues() {
+ const values = [];
+ const indexMap = [];
+
+ this.values
+ .forEach((value: T, originalIndex: number) => {
+ if (value !== undefined) {
+ values.push(value);
+ indexMap.push(originalIndex);
+ }
+ }, {});
+
+ return { values, indexMap };
+ }
+
+ private addListeners() {
+ const self = this;
+
+ for (const iterator of this.sourceIterators) {
+ iterator.on("end", () => {
+ self.endedSources++;
+
+ if (self.endedSources === self.sourceIterators.length) {
+ self.shouldClose = true;
+ }
+ });
+ }
+ }
+}
From 06f5e53e862cd03fcf3cb7af8a845bae8b962c82 Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Tue, 4 Dec 2018 14:26:05 +0100
Subject: [PATCH 03/20] Implement ConnectionsProviderMerge
---
src/catalog.delijn.ts | 11 +++
src/catalog.nmbs.ts | 8 ++
src/demo.ts | 6 +-
.../merge/ConnectionsIteratorMerge.ts | 79 -------------------
.../merge/ConnectionsProviderMerge.ts | 67 ++++++++++++++--
src/inversify.config.ts | 18 +----
6 files changed, 88 insertions(+), 101 deletions(-)
create mode 100644 src/catalog.delijn.ts
create mode 100644 src/catalog.nmbs.ts
delete mode 100644 src/fetcher/connections/merge/ConnectionsIteratorMerge.ts
diff --git a/src/catalog.delijn.ts b/src/catalog.delijn.ts
new file mode 100644
index 00000000..6f6dbaa0
--- /dev/null
+++ b/src/catalog.delijn.ts
@@ -0,0 +1,11 @@
+import Catalog from "./Catalog";
+import TravelMode from "./TravelMode";
+
+const catalog = new Catalog();
+catalog.addStopsFetcher(
+ "https://data.delijn.be/stops/",
+ "https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/stops",
+);
+catalog.addConnectionsFetcher("https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/connections", TravelMode.Bus);
+
+export default catalog;
diff --git a/src/catalog.nmbs.ts b/src/catalog.nmbs.ts
new file mode 100644
index 00000000..33204c55
--- /dev/null
+++ b/src/catalog.nmbs.ts
@@ -0,0 +1,8 @@
+import Catalog from "./Catalog";
+import TravelMode from "./TravelMode";
+
+const catalog = new Catalog();
+catalog.addStopsFetcher("http://irail.be/stations/NMBS/", "https://irail.be/stations/NMBS");
+catalog.addConnectionsFetcher("https://graph.irail.be/sncb/connections", TravelMode.Train);
+
+export default catalog;
diff --git a/src/demo.ts b/src/demo.ts
index 1cf6e61d..bb1b5ca0 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -21,8 +21,10 @@ export default async (logResults) => {
const publicTransportResult = await planner.query({
publicTransportOnly: true,
- from: "https://data.delijn.be/stops/201657", // Ingelmunster
- to: "https://data.delijn.be/stops/205910", // Ghent-Sint-Pieters
+ from: "https://data.delijn.be/stops/201657",
+ to: "https://data.delijn.be/stops/205910",
+ // from: "http://irail.be/stations/NMBS/008896008", // Kortrijk
+ // to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
minimumDepartureTime: new Date(),
maximumTransferDuration: Units.fromHours(.01),
});
diff --git a/src/fetcher/connections/merge/ConnectionsIteratorMerge.ts b/src/fetcher/connections/merge/ConnectionsIteratorMerge.ts
deleted file mode 100644
index 3eea26ab..00000000
--- a/src/fetcher/connections/merge/ConnectionsIteratorMerge.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import IConnection from "../IConnection";
-import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig";
-
-export default class ConnectionsIteratorMerge implements AsyncIterator {
-
- private iterators: Array>;
- private config: IConnectionsFetcherConfig;
-
- private nextConnections: IConnection[];
-
- constructor(iterators: Array>, config: IConnectionsFetcherConfig) {
- this.iterators = iterators;
- this.config = config;
-
- this.nextConnections = Array(iterators.length);
- }
-
- public async next(): Promise> {
- await this.replenishNextConnections();
-
- // console.log("Next connections", this.nextConnections.map((c) => `${c.departureTime} ${c["@id"]}`));
-
- if (!this.config.backward) {
- // Get next connection with earliest departure time
-
- const earliestConnection = this.findNextConnection(this.earliestPredicate);
- return {value: earliestConnection, done: false};
-
- } else {
- // Get next connection with latest departure time
-
- const latestConnection = this.findNextConnection(this.latestPredicate);
- return {value: latestConnection, done: false};
- }
- }
-
- public return(value?: any): Promise> {
- return undefined;
- }
-
- public throw(e?: any): Promise> {
- return undefined;
- }
-
- private findNextConnection(predicate: (matching: IConnection, current: IConnection) => boolean): IConnection {
- let matchingConnectionIndex = 0;
- let matchingConnection = this.nextConnections[matchingConnectionIndex];
-
- for (let i = 1; i < this.nextConnections.length; i++) {
- const currentConnection = this.nextConnections[i];
-
- if (predicate(matchingConnection, currentConnection)) {
- matchingConnectionIndex = i;
- matchingConnection = currentConnection;
- }
- }
-
- this.nextConnections[matchingConnectionIndex] = null;
- return matchingConnection;
- }
-
- private earliestPredicate(matching: IConnection, current: IConnection): boolean {
- return matching.departureTime.valueOf() > current.departureTime.valueOf();
- }
-
- private latestPredicate(matching: IConnection, current: IConnection): boolean {
- return matching.departureTime.valueOf() < current.departureTime.valueOf();
- }
-
- private async replenishNextConnections() {
- for (let i = 0; i < this.nextConnections.length; i++) {
- if (!this.nextConnections[i]) {
- const result = await this.iterators[i].next();
- this.nextConnections[i] = result.value;
- }
- }
- }
-
-}
diff --git a/src/fetcher/connections/merge/ConnectionsProviderMerge.ts b/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
index ae8b53e1..f6fdd021 100644
--- a/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
+++ b/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
@@ -1,23 +1,78 @@
import { AsyncIterator } from "asynciterator";
-import { injectable, multiInject, tagged } from "inversify";
-import TYPES from "../../../types";
+import { inject, injectable, multiInject } from "inversify";
+import Catalog from "../../../Catalog";
+import TYPES, { ConnectionsFetcherFactory } from "../../../types";
+import MergeIterator from "../../../util/MergeIterator";
import IConnection from "../IConnection";
import IConnectionsFetcher from "../IConnectionsFetcher";
import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig";
-import ConnectionsIteratorMerge from "./ConnectionsIteratorMerge";
@injectable()
export default class ConnectionsProviderMerge implements IConnectionsFetcher {
- public createIterator: () => AsyncIterator;
+ private static forwardsConnectionSelector(connections: IConnection[]): number {
+ if (connections.length === 1) {
+ return 0;
+ }
+
+ let earliestIndex = 0;
+ const earliest = connections[earliestIndex];
+
+ for (let i = 1; i < connections.length; i++) {
+ const connection = connections[i];
+
+ if (connection.departureTime < earliest.departureTime) {
+ earliestIndex = i;
+ }
+ }
+
+ return earliestIndex;
+ }
+
+ private static backwardsConnectionsSelector(connections: IConnection[]): number {
+ if (connections.length === 1) {
+ return 0;
+ }
+
+ let latestIndex = 0;
+ const latest = connections[latestIndex];
+
+ for (let i = 1; i < connections.length; i++) {
+ const connection = connections[i];
+
+ if (connection.departureTime > latest.departureTime) {
+ latestIndex = i;
+ }
+ }
+
+ return latestIndex;
+ }
private config: IConnectionsFetcherConfig;
private connectionsFetchers: IConnectionsFetcher[];
constructor(
- @multiInject(TYPES.ConnectionsFetcher) @tagged("type", "source") connectionsFetchers: IConnectionsFetcher[],
+ @inject(TYPES.ConnectionsFetcherFactory) connectionsFetcherFactory: ConnectionsFetcherFactory,
+ @inject(TYPES.Catalog) catalog: Catalog,
) {
- this.connectionsFetchers = connectionsFetchers;
+ this.connectionsFetchers = [];
+
+ for (const { accessUrl, travelMode } of catalog.connectionsFetcherConfigs) {
+ this.connectionsFetchers.push(connectionsFetcherFactory(accessUrl, travelMode));
+ }
+ }
+
+ public createIterator(): AsyncIterator {
+
+ const iterators = this.connectionsFetchers
+ .map((fetcher) => fetcher.createIterator());
+
+ const selector = this.config.backward ?
+ ConnectionsProviderMerge.backwardsConnectionsSelector
+ :
+ ConnectionsProviderMerge.forwardsConnectionSelector;
+
+ return new MergeIterator(iterators, selector, true);
}
public setConfig(config: IConnectionsFetcherConfig): void {
diff --git a/src/inversify.config.ts b/src/inversify.config.ts
index e790b646..18cc6bce 100644
--- a/src/inversify.config.ts
+++ b/src/inversify.config.ts
@@ -1,11 +1,12 @@
import { Container, interfaces } from "inversify";
import LDFetch from "ldfetch";
import Catalog from "./Catalog";
+import catalog from "./catalog.delijn";
import Context from "./Context";
-import ConnectionsProviderPassthrough from "./fetcher/connections/ConnectionsProviderPassthrough";
import IConnectionsFetcher from "./fetcher/connections/IConnectionsFetcher";
import IConnectionsProvider from "./fetcher/connections/IConnectionsProvider";
import ConnectionsFetcherLazy from "./fetcher/connections/ld-fetch/ConnectionsFetcherLazy";
+import ConnectionsProviderMerge from "./fetcher/connections/merge/ConnectionsProviderMerge";
import IStopsFetcher from "./fetcher/stops/IStopsFetcher";
import IStopsProvider from "./fetcher/stops/IStopsProvider";
import StopsFetcherLDFetch from "./fetcher/stops/ld-fetch/StopsFetcherLDFetch";
@@ -51,7 +52,7 @@ container.bind(TYPES.ReachableStopsFinder)
container.bind(TYPES.ReachableStopsFinder)
.to(ReachableStopsFinderBirdsEyeCached).whenTargetTagged("phase", ReachableStopsSearchPhase.Final);
-container.bind(TYPES.ConnectionsProvider).to(ConnectionsProviderPassthrough).inSingletonScope();
+container.bind(TYPES.ConnectionsProvider).to(ConnectionsProviderMerge).inSingletonScope();
container.bind(TYPES.ConnectionsFetcher).to(ConnectionsFetcherLazy);
container.bind>(TYPES.ConnectionsFetcherFactory)
.toFactory(
@@ -81,18 +82,7 @@ container.bind>(TYPES.StopsFetcherFactory)
},
);
-// Init catalog
-// const catalog = new Catalog();
-// catalog.addStopsFetcher("http://irail.be/stations/NMBS/", "https://irail.be/stations/NMBS");
-// catalog.addConnectionsFetcher("https://graph.irail.be/sncb/connections", TravelMode.Train);
-
-const catalog = new Catalog();
-catalog.addStopsFetcher(
- "https://data.delijn.be/stops/",
- "https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/stops",
-);
-catalog.addConnectionsFetcher("https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/connections", TravelMode.Bus);
-
+// Bind catalog
container.bind(TYPES.Catalog).toConstantValue(catalog);
// Init LDFetch
From 067d02f7fa5ef72343a0444af12519f56f8ad0c2 Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Tue, 4 Dec 2018 16:42:36 +0100
Subject: [PATCH 04/20] Remove prefixes from StopsFetchers
---
src/Catalog.ts | 4 ++--
src/catalog.delijn.ts | 9 ++++----
src/catalog.nmbs.ts | 2 +-
src/demo.ts | 8 +++++--
src/fetcher/stops/IStopsFetcher.ts | 2 --
src/fetcher/stops/StopsProviderDefault.ts | 21 +++++--------------
.../ld-fetch/StopsFetcherLDFetch.test.ts | 2 --
.../stops/ld-fetch/StopsFetcherLDFetch.ts | 5 -----
src/inversify.config.ts | 5 +----
.../PublicTransportPlannerCSAProfile.test.ts | 2 --
src/planner/road/RoadPlannerBirdsEye.test.ts | 1 -
.../ReachableStopsFinderBirdsEye.test.ts | 1 -
.../ReachableStopsFinderRoadPlanner.test.ts | 1 -
.../LocationResolverDefault.test.ts | 1 -
.../QueryRunnerExponential.test.ts | 1 -
src/types.ts | 2 +-
16 files changed, 21 insertions(+), 46 deletions(-)
diff --git a/src/Catalog.ts b/src/Catalog.ts
index 6bfebad8..4a18dee8 100644
--- a/src/Catalog.ts
+++ b/src/Catalog.ts
@@ -4,8 +4,8 @@ export default class Catalog {
public stopsFetcherConfigs = [];
public connectionsFetcherConfigs = [];
- public addStopsFetcher(prefix: string, accessUrl: string) {
- this.stopsFetcherConfigs.push({prefix, accessUrl});
+ public addStopsFetcher(accessUrl: string) {
+ this.stopsFetcherConfigs.push({accessUrl});
}
public addConnectionsFetcher(accessUrl: string, travelMode: TravelMode) {
diff --git a/src/catalog.delijn.ts b/src/catalog.delijn.ts
index 6f6dbaa0..d33e64d2 100644
--- a/src/catalog.delijn.ts
+++ b/src/catalog.delijn.ts
@@ -2,10 +2,11 @@ import Catalog from "./Catalog";
import TravelMode from "./TravelMode";
const catalog = new Catalog();
-catalog.addStopsFetcher(
- "https://data.delijn.be/stops/",
- "https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/stops",
-);
+catalog.addStopsFetcher("https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/stops");
+catalog.addStopsFetcher("https://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/stops");
+catalog.addStopsFetcher("https://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/stops");
+
catalog.addConnectionsFetcher("https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/connections", TravelMode.Bus);
+catalog.addConnectionsFetcher("https://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/connections", TravelMode.Bus);
export default catalog;
diff --git a/src/catalog.nmbs.ts b/src/catalog.nmbs.ts
index 33204c55..f9edf2e8 100644
--- a/src/catalog.nmbs.ts
+++ b/src/catalog.nmbs.ts
@@ -2,7 +2,7 @@ import Catalog from "./Catalog";
import TravelMode from "./TravelMode";
const catalog = new Catalog();
-catalog.addStopsFetcher("http://irail.be/stations/NMBS/", "https://irail.be/stations/NMBS");
+catalog.addStopsFetcher("https://irail.be/stations/NMBS");
catalog.addConnectionsFetcher("https://graph.irail.be/sncb/connections", TravelMode.Train);
export default catalog;
diff --git a/src/demo.ts b/src/demo.ts
index bb1b5ca0..70b4fff2 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -21,8 +21,12 @@ export default async (logResults) => {
const publicTransportResult = await planner.query({
publicTransportOnly: true,
- from: "https://data.delijn.be/stops/201657",
- to: "https://data.delijn.be/stops/205910",
+ // from: "https://data.delijn.be/stops/201657",
+ // to: "https://data.delijn.be/stops/205910",
+ // from: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456
+ // to: "https://data.delijn.be/stops/502481", // Tielt Metaalconstructie Goossens
+ from: "https://data.delijn.be/stops/509927", // Tield Rameplein perron 1
+ to: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456
// from: "http://irail.be/stations/NMBS/008896008", // Kortrijk
// to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
minimumDepartureTime: new Date(),
diff --git a/src/fetcher/stops/IStopsFetcher.ts b/src/fetcher/stops/IStopsFetcher.ts
index d2c9d3a1..098ac036 100644
--- a/src/fetcher/stops/IStopsFetcher.ts
+++ b/src/fetcher/stops/IStopsFetcher.ts
@@ -2,9 +2,7 @@ import IStopsProvider from "./IStopsProvider";
/**
* Represents one data source of stops, e.g. De Lijn or NMBS
- * Has a prefix for potential use by a [[IStopsProvider]]
* Extends [[IStopsProvider]] because it should implement the same methods
*/
export default interface IStopsFetcher extends IStopsProvider {
- prefix: string;
}
diff --git a/src/fetcher/stops/StopsProviderDefault.ts b/src/fetcher/stops/StopsProviderDefault.ts
index 4398c39d..ffe9b65a 100644
--- a/src/fetcher/stops/StopsProviderDefault.ts
+++ b/src/fetcher/stops/StopsProviderDefault.ts
@@ -16,17 +16,15 @@ export default class StopsProviderDefault implements IStopsProvider {
) {
this.stopsFetchers = [];
- for (const {prefix, accessUrl} of catalog.stopsFetcherConfigs) {
- this.stopsFetchers.push(stopsFetcherFactory(prefix, accessUrl));
+ for (const { accessUrl } of catalog.stopsFetcherConfigs) {
+ this.stopsFetchers.push(stopsFetcherFactory(accessUrl));
}
}
public async getStopById(stopId: string): Promise {
- const fetcher = this.determineStopFetcher(stopId);
-
- if (fetcher) {
- return fetcher.getStopById(stopId);
- }
+ return Promise.all(this.stopsFetchers
+ .map((stopsFetcher: IStopsFetcher) => stopsFetcher.getStopById(stopId)),
+ ).then((results: IStop[]) => results.find((stop) => stop !== undefined));
}
public async getAllStops(): Promise {
@@ -34,13 +32,4 @@ export default class StopsProviderDefault implements IStopsProvider {
.map((stopsFetcher: IStopsFetcher) => stopsFetcher.getAllStops()),
).then((results: IStop[][]) => [].concat(...results));
}
-
- private determineStopFetcher(stopId: string): IStopsFetcher {
- if (!this.stopsFetchers || !this.stopsFetchers.length) {
- return null;
- }
-
- return this.stopsFetchers
- .find((fetcher) => stopId.indexOf(fetcher.prefix) === 0);
- }
}
diff --git a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.test.ts b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.test.ts
index da6b6fda..14e10cf9 100644
--- a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.test.ts
+++ b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.test.ts
@@ -14,11 +14,9 @@ const DE_LIJN_STOPS_URLS = [
const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
const deLijnFetcher = new StopsFetcherLDFetch(ldFetch);
-deLijnFetcher.setPrefix("https://data.delijn.be/stops/");
deLijnFetcher.setAccessUrl(DE_LIJN_STOPS_URLS[2]);
const nmbsFetcher = new StopsFetcherLDFetch(ldFetch);
-nmbsFetcher.setPrefix("http://irail.be/stations/NMBS/");
nmbsFetcher.setAccessUrl("https://irail.be/stations/NMBS");
test("[StopsFetcherLDFetch] De Lijn first stop", async () => {
diff --git a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts
index d47fdb63..d7ef11b9 100644
--- a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts
+++ b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts
@@ -17,7 +17,6 @@ interface IStopMap {
@injectable()
export default class StopsFetcherLDFetch implements IStopsFetcher {
- public prefix: string;
private accessUrl: string;
private ldFetch: LDFetch;
@@ -31,10 +30,6 @@ export default class StopsFetcherLDFetch implements IStopsFetcher {
this.loadStops();
}
- public setPrefix(prefix: string) {
- this.prefix = prefix;
- }
-
public setAccessUrl(accessUrl: string) {
this.accessUrl = accessUrl;
}
diff --git a/src/inversify.config.ts b/src/inversify.config.ts
index 18cc6bce..fae0acfb 100644
--- a/src/inversify.config.ts
+++ b/src/inversify.config.ts
@@ -72,12 +72,9 @@ container.bind(TYPES.StopsFetcher).to(StopsFetcherLDFetch);
container.bind>(TYPES.StopsFetcherFactory)
.toFactory(
(context: interfaces.Context) =>
- (prefix: string, accessUrl: string) => {
+ (accessUrl: string) => {
const fetcher = context.container.get(TYPES.StopsFetcher);
-
- fetcher.setPrefix(prefix);
fetcher.setAccessUrl(accessUrl);
-
return fetcher;
},
);
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
index 44255b4a..f86de78a 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
@@ -30,7 +30,6 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
connectionFetcher.setConfig({ backward: true });
const stopsFetcher = new StopsFetcherLDFetch(ldFetch);
- stopsFetcher.setPrefix("http://irail.be/stations/NMBS/");
stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS");
const locationResolver = new LocationResolverDefault(stopsFetcher);
@@ -160,7 +159,6 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
connectionFetcher.setAccessUrl("https://graph.irail.be/sncb/connections");
const stopsFetcher = new StopsFetcherLDFetch(ldFetch);
- stopsFetcher.setPrefix("http://irail.be/stations/NMBS/");
stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS");
const locationResolver = new LocationResolverDefault(stopsFetcher);
diff --git a/src/planner/road/RoadPlannerBirdsEye.test.ts b/src/planner/road/RoadPlannerBirdsEye.test.ts
index 7f6201a2..b15577ca 100644
--- a/src/planner/road/RoadPlannerBirdsEye.test.ts
+++ b/src/planner/road/RoadPlannerBirdsEye.test.ts
@@ -11,7 +11,6 @@ const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
const planner: IRoadPlanner = new RoadPlannerBirdsEye();
const stopsFetcher = new StopsFetcherLDFetch(ldFetch);
-stopsFetcher.setPrefix("http://irail.be/stations/NMBS/");
stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS");
const locationResolver = new LocationResolverDefault(stopsFetcher);
diff --git a/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts b/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts
index c503f602..86523581 100644
--- a/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts
+++ b/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts
@@ -16,7 +16,6 @@ const DE_LIJN_STOPS_URLS = [
const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
const stopsFetcher = new StopsFetcherLDFetch(ldFetch);
-stopsFetcher.setPrefix("https://data.delijn.be/stops/");
stopsFetcher.setAccessUrl(DE_LIJN_STOPS_URLS[2]);
const reachableStopsFinder = new ReachableStopsFinderBirdsEye(stopsFetcher);
diff --git a/src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts b/src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts
index 9013d2e5..e567b4a9 100644
--- a/src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts
+++ b/src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts
@@ -9,7 +9,6 @@ import ReachableStopsFinderRoadPlanner from "./ReachableStopsFinderRoadPlanner";
const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
const stopsFetcher = new StopsFetcherLDFetch(ldFetch);
-stopsFetcher.setPrefix("http://irail.be/stations/NMBS/");
stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS");
const roadPlanner = new RoadPlannerBirdsEye();
diff --git a/src/query-runner/LocationResolverDefault.test.ts b/src/query-runner/LocationResolverDefault.test.ts
index 5f3cd9a8..1ec1a03d 100644
--- a/src/query-runner/LocationResolverDefault.test.ts
+++ b/src/query-runner/LocationResolverDefault.test.ts
@@ -6,7 +6,6 @@ import LocationResolverDefault from "./LocationResolverDefault";
const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
const stopsFetcher = new StopsFetcherLDFetch(ldFetch);
-stopsFetcher.setPrefix("http://irail.be/stations/NMBS/");
stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS");
const locationResolver = new LocationResolverDefault(stopsFetcher);
diff --git a/src/query-runner/exponential/QueryRunnerExponential.test.ts b/src/query-runner/exponential/QueryRunnerExponential.test.ts
index 8adf0aba..5b247881 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.test.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.test.ts
@@ -46,7 +46,6 @@ describe("[QueryRunnerExponential]", () => {
connectionFetcher.setAccessUrl("https://graph.irail.be/sncb/connections");
const stopsFetcher = new StopsFetcherLDFetch(ldFetch);
- stopsFetcher.setPrefix("http://irail.be/stations/NMBS/");
stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS");
const locationResolver = new LocationResolverDefault(stopsFetcher);
diff --git a/src/types.ts b/src/types.ts
index 1874889d..157ebd76 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -25,5 +25,5 @@ const TYPES = {
export default TYPES;
-export type StopsFetcherFactory = (prefix: string, accessUrl: string) => IStopsFetcher;
+export type StopsFetcherFactory = (accessUrl: string) => IStopsFetcher;
export type ConnectionsFetcherFactory = (accessUrl: string, travelMode: TravelMode) => IConnectionsFetcher;
From 190d0fdc073753dc716e6e3a93b51b9f368c316e Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Tue, 4 Dec 2018 16:58:18 +0100
Subject: [PATCH 05/20] Added IPublicTransportPlanner factory for use in
QueryRunnerExponential
---
.../ConnectionsProviderPassthrough.ts | 1 +
.../merge/ConnectionsProviderMerge.ts | 5 ++++-
src/inversify.config.ts | 2 ++
.../QueryRunnerExponential.test.ts | 13 +-----------
.../exponential/QueryRunnerExponential.ts | 20 ++++++++-----------
src/types.ts | 4 ++++
6 files changed, 20 insertions(+), 25 deletions(-)
diff --git a/src/fetcher/connections/ConnectionsProviderPassthrough.ts b/src/fetcher/connections/ConnectionsProviderPassthrough.ts
index 6c3e754b..e7c7c43c 100644
--- a/src/fetcher/connections/ConnectionsProviderPassthrough.ts
+++ b/src/fetcher/connections/ConnectionsProviderPassthrough.ts
@@ -9,6 +9,7 @@ import IConnectionsProvider from "./IConnectionsProvider";
/**
* Passes through one [[IConnectionsFetcher]], the first one if there are multiple
+ * This provider is most/only useful if there is only one fetcher
*/
@injectable()
export default class ConnectionsProviderPassthrough implements IConnectionsProvider {
diff --git a/src/fetcher/connections/merge/ConnectionsProviderMerge.ts b/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
index f6fdd021..010d91e7 100644
--- a/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
+++ b/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
@@ -1,5 +1,5 @@
import { AsyncIterator } from "asynciterator";
-import { inject, injectable, multiInject } from "inversify";
+import { inject, injectable } from "inversify";
import Catalog from "../../../Catalog";
import TYPES, { ConnectionsFetcherFactory } from "../../../types";
import MergeIterator from "../../../util/MergeIterator";
@@ -7,6 +7,9 @@ import IConnection from "../IConnection";
import IConnectionsFetcher from "../IConnectionsFetcher";
import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig";
+/**
+ * Instantiates and merge sorts all registered connection fetchers
+ */
@injectable()
export default class ConnectionsProviderMerge implements IConnectionsFetcher {
diff --git a/src/inversify.config.ts b/src/inversify.config.ts
index fae0acfb..662c3d39 100644
--- a/src/inversify.config.ts
+++ b/src/inversify.config.ts
@@ -35,6 +35,8 @@ container.bind(TYPES.LocationResolver).to(LocationResolverDef
container.bind(TYPES.PublicTransportPlanner)
.to(PublicTransportPlannerCSAProfile);
+container.bind>(TYPES.PublicTransportPlannerFactory)
+ .toAutoFactory(TYPES.PublicTransportPlanner);
container.bind(TYPES.JourneyExtractor)
.to(JourneyExtractorDefault);
diff --git a/src/query-runner/exponential/QueryRunnerExponential.test.ts b/src/query-runner/exponential/QueryRunnerExponential.test.ts
index 5b247881..7b8c69a2 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.test.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.test.ts
@@ -71,18 +71,7 @@ describe("[QueryRunnerExponential]", () => {
);
};
- const fakeContext = {
- getContainer() {
- return {
- get() {
- return createPlanner();
- },
- };
- },
- };
-
- // @ts-ignore
- return new QueryRunnerExponential(fakeContext as Context, locationResolver, null);
+ return new QueryRunnerExponential(locationResolver, createPlanner);
};
const result: IPath[] = [];
diff --git a/src/query-runner/exponential/QueryRunnerExponential.ts b/src/query-runner/exponential/QueryRunnerExponential.ts
index 2397234f..e55a5583 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.ts
@@ -1,6 +1,5 @@
import { AsyncIterator } from "asynciterator";
-import { inject, injectable } from "inversify";
-import Context from "../../Context";
+import { inject, injectable, interfaces } from "inversify";
import Defaults from "../../Defaults";
import ILocation from "../../interfaces/ILocation";
import IPath from "../../interfaces/IPath";
@@ -16,20 +15,17 @@ import SubqueryIterator from "./SubqueryIterator";
@injectable()
export default class QueryRunnerExponential implements IQueryRunner {
- public private;
private locationResolver: ILocationResolver;
- private publicTransportPlanner: IPublicTransportPlanner;
-
- private context: Context;
+ private publicTransportPlannerFactory: interfaces.Factory;
constructor(
- @inject(TYPES.Context) context: Context,
- @inject(TYPES.LocationResolver) locationResolver: ILocationResolver,
- @inject(TYPES.PublicTransportPlanner) publicTransportPlanner: IPublicTransportPlanner,
+ @inject(TYPES.LocationResolver)
+ locationResolver: ILocationResolver,
+ @inject(TYPES.PublicTransportPlannerFactory)
+ publicTransportPlannerFactory: interfaces.Factory,
) {
this.locationResolver = locationResolver;
- this.publicTransportPlanner = publicTransportPlanner;
- this.context = context;
+ this.publicTransportPlannerFactory = publicTransportPlannerFactory;
}
public async run(query: IQuery): Promise> {
@@ -49,7 +45,7 @@ export default class QueryRunnerExponential implements IQueryRunner {
private async runSubquery(query: IResolvedQuery): Promise> {
// TODO investigate if publicTransportPlanner can be reused or reuse some of its aggregated data
- const planner = this.context.getContainer().get(TYPES.PublicTransportPlanner);
+ const planner = this.publicTransportPlannerFactory() as IPublicTransportPlanner;
return planner.plan(query);
}
diff --git a/src/types.ts b/src/types.ts
index 157ebd76..146cd498 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -16,7 +16,11 @@ const TYPES = {
StopsFetcherFactory: Symbol("StopsFetcherFactory"),
PublicTransportPlanner: Symbol("PublicTransportPlanner"),
+ PublicTransportPlannerFactory: Symbol("PublicTransportPlannerFactory"),
+
RoadPlanner: Symbol("RoadPlanner"),
+ RoadPlannerFactory: Symbol("RoadPlannerFactory"),
+
ReachableStopsFinder: Symbol("ReachableStopsFinder"),
JourneyExtractor: Symbol("JourneyExtractor"),
LDFetch: Symbol("LDFetch"),
From 68abc3944ebe5ec71b6bcf59c5d97533946eb70d Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Tue, 4 Dec 2018 17:09:13 +0100
Subject: [PATCH 06/20] Complete De Lijn catalog (breaks demo test)
---
src/catalog.delijn.ts | 16 +++++++++++-----
.../exponential/QueryRunnerExponential.test.ts | 12 ------------
2 files changed, 11 insertions(+), 17 deletions(-)
diff --git a/src/catalog.delijn.ts b/src/catalog.delijn.ts
index d33e64d2..95726afd 100644
--- a/src/catalog.delijn.ts
+++ b/src/catalog.delijn.ts
@@ -2,11 +2,17 @@ import Catalog from "./Catalog";
import TravelMode from "./TravelMode";
const catalog = new Catalog();
-catalog.addStopsFetcher("https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/stops");
-catalog.addStopsFetcher("https://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/stops");
-catalog.addStopsFetcher("https://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/stops");
-catalog.addConnectionsFetcher("https://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/connections", TravelMode.Bus);
-catalog.addConnectionsFetcher("https://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/connections", TravelMode.Bus);
+catalog.addStopsFetcher("http://openplanner.ilabt.imec.be/delijn/Antwerpen/stops");
+catalog.addStopsFetcher("http://openplanner.ilabt.imec.be/delijn/Limburg/stops");
+catalog.addStopsFetcher("http://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/stops");
+catalog.addStopsFetcher("http://openplanner.ilabt.imec.be/delijn/Vlaams-Brabant/stops");
+catalog.addStopsFetcher("http://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/stops");
+
+catalog.addConnectionsFetcher("http://openplanner.ilabt.imec.be/delijn/Antwerpen/connections", TravelMode.Bus);
+catalog.addConnectionsFetcher("http://openplanner.ilabt.imec.be/delijn/Limburg/connections", TravelMode.Bus);
+catalog.addConnectionsFetcher("http://openplanner.ilabt.imec.be/delijn/Oost-Vlaanderen/connections", TravelMode.Bus);
+catalog.addConnectionsFetcher("http://openplanner.ilabt.imec.be/delijn/Vlaams-Brabant/connections", TravelMode.Bus);
+catalog.addConnectionsFetcher("http://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/connections", TravelMode.Bus);
export default catalog;
diff --git a/src/query-runner/exponential/QueryRunnerExponential.test.ts b/src/query-runner/exponential/QueryRunnerExponential.test.ts
index 7b8c69a2..eb8829a8 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.test.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.test.ts
@@ -1,6 +1,5 @@
import "jest";
import LDFetch from "ldfetch";
-import Context from "../../Context";
import ConnectionsFetcherLazy from "../../fetcher/connections/ld-fetch/ConnectionsFetcherLazy";
import StopsFetcherLDFetch from "../../fetcher/stops/ld-fetch/StopsFetcherLDFetch";
import IPath from "../../interfaces/IPath";
@@ -30,17 +29,6 @@ describe("[QueryRunnerExponential]", () => {
const createExponentialQueryRunner = () => {
const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
- const httpStartTimes = {};
-
- ldFetch.on("request", (url) => httpStartTimes[url] = new Date());
-
- ldFetch.on("redirect", (obj) => httpStartTimes[obj.to] = httpStartTimes[obj.from]);
-
- ldFetch.on("response", (url) => {
- const difference = (new Date()).getTime() - httpStartTimes[url].getTime();
- console.log(`HTTP GET - ${url} (${difference}ms)`);
- });
-
const connectionFetcher = new ConnectionsFetcherLazy(ldFetch);
connectionFetcher.setTravelMode(TravelMode.Train);
connectionFetcher.setAccessUrl("https://graph.irail.be/sncb/connections");
From 6fe7e984cc94ff03a088088f29af24a36348dd6a Mon Sep 17 00:00:00 2001
From: Maxim Martin
Date: Wed, 5 Dec 2018 16:36:47 +0100
Subject: [PATCH 07/20] 1. refactor journey extractor. Calculate journeys based
+ footpaths on the profiles. #44 2. Added departureTime to ITransferProfiles.
(pareto optimization p22. paper CSA March 2017 - KIT, +p20 example) 3. update
csa (refactor in next commit). 4. update demo.
---
src/demo.ts | 16 +-
.../data-structure/stops/ITransferProfile.ts | 1 +
.../JourneyExtractorDefault.ts | 307 +++++++-----------
.../PublicTransportPlannerCSAProfile.test.ts | 10 +-
.../PublicTransportPlannerCSAProfile.ts | 71 +++-
5 files changed, 188 insertions(+), 217 deletions(-)
diff --git a/src/demo.ts b/src/demo.ts
index 3d770876..52035885 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -1,4 +1,6 @@
import Planner from "./index";
+import IPath from "./interfaces/IPath";
+import IStep from "./interfaces/IStep";
import Units from "./util/Units";
export default async (logResults) => {
@@ -31,14 +33,12 @@ export default async (logResults) => {
let i = 0;
- publicTransportResult.on("readable", () => {
- let path = publicTransportResult.read();
-
- while (path && i < 5) {
- console.log(i++, path);
-
- path = publicTransportResult.read();
- }
+ publicTransportResult.take(3).on("data", (path: IPath) => {
+ console.log(++i);
+ path.steps.forEach((step: IStep) => {
+ console.log(JSON.stringify(step, null, " "));
+ });
+ console.log("\n");
});
return true;
diff --git a/src/planner/public-transport/CSA/data-structure/stops/ITransferProfile.ts b/src/planner/public-transport/CSA/data-structure/stops/ITransferProfile.ts
index e2fa68f2..9d8b65f9 100644
--- a/src/planner/public-transport/CSA/data-structure/stops/ITransferProfile.ts
+++ b/src/planner/public-transport/CSA/data-structure/stops/ITransferProfile.ts
@@ -9,6 +9,7 @@ import IConnection from "../../../../../fetcher/connections/IConnection";
* to arrive at the arrivalTime in the target [[IStop]].
*/
export default interface ITransferProfile {
+ departureTime: number;
arrivalTime: number;
exitConnection: IConnection;
enterConnection: IConnection;
diff --git a/src/planner/public-transport/JourneyExtractorDefault.ts b/src/planner/public-transport/JourneyExtractorDefault.ts
index 24dde2e2..e987b17a 100644
--- a/src/planner/public-transport/JourneyExtractorDefault.ts
+++ b/src/planner/public-transport/JourneyExtractorDefault.ts
@@ -1,7 +1,6 @@
import { ArrayIterator, AsyncIterator } from "asynciterator";
-import { inject, injectable, tagged } from "inversify";
+import { inject, injectable } from "inversify";
import IConnection from "../../fetcher/connections/IConnection";
-import IStop from "../../fetcher/stops/IStop";
import ILocation from "../../interfaces/ILocation";
import IPath from "../../interfaces/IPath";
import IStep from "../../interfaces/IStep";
@@ -9,19 +8,13 @@ import ILocationResolver from "../../query-runner/ILocationResolver";
import IResolvedQuery from "../../query-runner/IResolvedQuery";
import TravelMode from "../../TravelMode";
import TYPES from "../../types";
-import Iterators from "../../util/Iterators";
import Path from "../Path";
-import IRoadPlanner from "../road/IRoadPlanner";
import Step from "../Step";
-import IReachableStopsFinder, { IReachableStop } from "../stops/IReachableStopsFinder";
-import ReachableStopsFinderMode from "../stops/ReachableStopsFinderMode";
-import ReachableStopsSearchPhase from "../stops/ReachableStopsSearchPhase";
+import IProfile from "./CSA/data-structure/stops/IProfile";
import IProfilesByStop from "./CSA/data-structure/stops/IProfilesByStop";
import ITransferProfile from "./CSA/data-structure/stops/ITransferProfile";
-import Profile from "./CSA/data-structure/stops/Profile";
import ProfileUtil from "./CSA/util/ProfileUtil";
import IJourneyExtractor from "./IJourneyExtractor";
-import JourneyExtractionPhase from "./JourneyExtractionPhase";
/**
* Creates journeys based on the profiles and query from [[PublicTransportPlannerCSAProfile]].
@@ -32,30 +25,12 @@ import JourneyExtractionPhase from "./JourneyExtractionPhase";
*/
@injectable()
export default class JourneyExtractorDefault implements IJourneyExtractor {
- private readonly transferRoadPlanner: IRoadPlanner;
- private readonly finalRoadPlanner: IRoadPlanner;
- private readonly initialReachableStopsFinder: IReachableStopsFinder;
-
private readonly locationResolver: ILocationResolver;
private bestArrivalTime: number[][] = [];
- constructor(
- @inject(TYPES.RoadPlanner)
- @tagged("phase", JourneyExtractionPhase.Transfer)
- transferRoadPlanner: IRoadPlanner,
- @inject(TYPES.RoadPlanner)
- @tagged("phase", JourneyExtractionPhase.Final)
- finalRoadPlanner: IRoadPlanner,
- @inject(TYPES.ReachableStopsFinder)
- @tagged("phase", ReachableStopsSearchPhase.Initial)
- initialReachableStopsFinder: IReachableStopsFinder,
- @inject(TYPES.LocationResolver) locationResolver: ILocationResolver,
- ) {
- this.transferRoadPlanner = transferRoadPlanner;
- this.finalRoadPlanner = finalRoadPlanner;
+ constructor(@inject(TYPES.LocationResolver) locationResolver: ILocationResolver) {
this.locationResolver = locationResolver;
- this.initialReachableStopsFinder = initialReachableStopsFinder;
}
public async extractJourneys(
@@ -64,67 +39,52 @@ export default class JourneyExtractorDefault implements IJourneyExtractor {
): Promise> {
const filteredProfilesByStop: IProfilesByStop = ProfileUtil.filterInfinity(profilesByStop);
- const departureLocation: IStop = query.from[0] as IStop;
-
- const reachableStops = await this.initialReachableStopsFinder.findReachableStops(
- departureLocation,
- ReachableStopsFinderMode.Source,
- query.maximumTransferDuration,
- query.minimumWalkingSpeed,
- );
+ const departureLocation: ILocation = query.from[0];
+ const arrivalLocation: ILocation = query.to[0];
const paths: IPath[] = [];
- for (const reachableStop of reachableStops) {
- const departureStop: IStop = reachableStop.stop;
+ const departureLocationProfiles: IProfile[] = filteredProfilesByStop[departureLocation.id];
- if (!filteredProfilesByStop[departureStop.id]) {
- console.warn("Can't find departure stop: " + departureStop.id);
- continue;
- }
-
- for (const profile of filteredProfilesByStop[departureStop.id]) {
- if (profile.departureTime >= query.minimumDepartureTime.getTime()) {
- const arrivalStop = query.to[0];
-
- for (let amountOfTransfers = 0; amountOfTransfers < profile.transferProfiles.length; amountOfTransfers++) {
- const transferProfile = profile.transferProfiles[amountOfTransfers];
- const enterStop = transferProfile.enterConnection && transferProfile.enterConnection.departureStop;
-
- if (enterStop !== departureStop.id) {
- continue;
- }
-
- if (this.checkBestArrivalTime(transferProfile, departureStop, arrivalStop)) {
- try {
- paths.push(await this.extractJourney(
- arrivalStop,
- reachableStop,
- profile,
- amountOfTransfers,
- filteredProfilesByStop,
- query,
- ));
-
- this.setBestArrivalTime(departureStop, arrivalStop, transferProfile.arrivalTime);
- } catch (e) {
- console.warn(e);
- }
- }
+ // Can't find departure stop;
+ if (!departureLocationProfiles) {
+ return new ArrayIterator(paths);
+ }
+ for (const profile of departureLocationProfiles) {
+
+ for (let amountOfTransfers = 0; amountOfTransfers < profile.transferProfiles.length; amountOfTransfers++) {
+ const transferProfile: ITransferProfile = profile.transferProfiles[amountOfTransfers];
+
+ if (this.checkBestArrivalTime(transferProfile, departureLocation, arrivalLocation)) {
+ try {
+ paths.push(await this.extractJourney(
+ departureLocation,
+ arrivalLocation,
+ transferProfile,
+ amountOfTransfers,
+ filteredProfilesByStop,
+ ));
+
+ this.setBestArrivalTime(
+ departureLocation,
+ arrivalLocation,
+ transferProfile.arrivalTime,
+ );
+ } catch (e) {
+ console.warn(e);
}
-
}
}
- }
+ }
return new ArrayIterator(paths.reverse());
}
private checkBestArrivalTime(
transferProfile: ITransferProfile,
- departureStop: ILocation,
- arrivalStop: ILocation,
+ departureLocation: ILocation,
+ arrivalLocation: ILocation,
): boolean {
const canArrive = transferProfile.arrivalTime < Infinity;
@@ -133,13 +93,13 @@ export default class JourneyExtractorDefault implements IJourneyExtractor {
return false;
}
- const bestArrivalTimesOfDepartureStop = this.bestArrivalTime[departureStop.id];
+ const bestArrivalTimesOfDepartureStop = this.bestArrivalTime[departureLocation.id];
if (!bestArrivalTimesOfDepartureStop) {
return true;
}
- const bestArrivalTime = bestArrivalTimesOfDepartureStop[arrivalStop.id];
+ const bestArrivalTime = bestArrivalTimesOfDepartureStop[arrivalLocation.id];
if (!bestArrivalTime) {
return true;
@@ -148,151 +108,116 @@ export default class JourneyExtractorDefault implements IJourneyExtractor {
return transferProfile.arrivalTime < bestArrivalTime;
}
- private setBestArrivalTime(departureStop: ILocation, arrivalStop: ILocation, arrivalTime: number): void {
- if (!this.bestArrivalTime[departureStop.id]) {
- this.bestArrivalTime[departureStop.id] = [];
+ private setBestArrivalTime(departureLocation: ILocation, arrivalLocation: ILocation, arrivalTime: number): void {
+ if (!this.bestArrivalTime[departureLocation.id]) {
+ this.bestArrivalTime[departureLocation.id] = [];
}
- this.bestArrivalTime[departureStop.id][arrivalStop.id] = arrivalTime;
+ this.bestArrivalTime[departureLocation.id][arrivalLocation.id] = arrivalTime;
}
private async extractJourney(
- arrivalStop: ILocation,
- reachableStop: IReachableStop,
- profile: Profile,
+ departureLocation: ILocation,
+ arrivalLocation: ILocation,
+ transferProfile: ITransferProfile,
transfers: number,
profilesByStop: IProfilesByStop,
- query: IResolvedQuery,
): Promise {
- let shouldReturn = true;
-
- // Extract journey for amount of transfers
const path: Path = Path.create();
- let currentProfile = profile;
- let remainingTransfers = transfers;
+ let currentTransferProfile: ITransferProfile = transferProfile;
+ let departureTime: Date = new Date(transferProfile.departureTime);
- if (reachableStop.duration > 0) {
- const currentTransferProfile = profile.transferProfiles[remainingTransfers];
+ let remainingTransfers: number = transfers;
- this.addInitialFootpath(
- path,
- currentTransferProfile,
- reachableStop,
- query.from[0],
- );
- }
+ let currentLocation: ILocation = departureLocation;
while (remainingTransfers >= 0) {
- // Construct and push step
- const currentTransferProfile = currentProfile.transferProfiles[remainingTransfers];
-
const enterConnection: IConnection = currentTransferProfile.enterConnection;
const exitConnection: IConnection = currentTransferProfile.exitConnection;
- const step: IStep = Step.createFromConnections(enterConnection, exitConnection);
-
- step.startLocation = await this.locationResolver.resolve(step.startLocation.id);
- step.stopLocation = await this.locationResolver.resolve(step.stopLocation.id);
+ const enterLocation: ILocation = await this.locationResolver.resolve(enterConnection.departureStop);
+ const exitLocation: ILocation = await this.locationResolver.resolve(exitConnection.arrivalStop);
+
+ // Initial or transfer footpath.
+ const transferDepartureTime: Date = enterConnection.departureTime;
+
+ if (departureTime.getTime() !== transferDepartureTime.getTime()) {
+ const footpath: IStep = Step.create(
+ currentLocation,
+ enterLocation,
+ TravelMode.Walking,
+ {
+ minimum: transferDepartureTime.getTime() - departureTime.getTime(),
+ },
+ departureTime,
+ transferDepartureTime,
+ );
+
+ path.addStep(footpath);
+ }
+ // Public transport step.
+ const step: IStep = Step.createFromConnections(enterConnection, exitConnection);
+ step.startLocation = enterLocation;
+ step.stopLocation = exitLocation;
path.addStep(step);
+ currentLocation = exitLocation;
remainingTransfers--;
+
+ // Stop if we already arrived.
+ if (path.steps[path.steps.length - 1].stopLocation.id === arrivalLocation.id) {
+ break;
+ }
+
+ // Get next profile based on the arrival time at the current location.
if (remainingTransfers >= 0) {
- const nextProfile = profilesByStop[step.stopLocation.id];
-
- let i = nextProfile.length - 1;
- let found = false;
-
- while (!found && i >= 0) {
- const nextTransferProfile = nextProfile[i].transferProfiles[remainingTransfers];
- const connection = nextTransferProfile.enterConnection;
-
- if (connection) {
- const from = await this.locationResolver.resolve(step.stopLocation.id);
- const to = await this.locationResolver.resolve(connection.departureStop);
-
- const walkingResult = await this.transferRoadPlanner.plan({
- from: [from],
- to: [to],
- minimumWalkingSpeed: query.minimumWalkingSpeed,
- maximumWalkingSpeed: query.maximumWalkingSpeed,
- });
-
- // Get first path
- const walkingPath: IPath = await Iterators.getFirst(walkingResult);
-
- if (walkingPath && walkingPath.steps[0] &&
- connection.departureTime.getTime() >= step.stopTime.getTime() +
- walkingPath.steps[0].duration.minimum
- ) {
- found = true;
- path.addStep(walkingPath.steps[0]);
- currentProfile = nextProfile[i];
- }
- }
+ const currentProfiles: IProfile[] = profilesByStop[currentLocation.id];
+ let i: number = currentProfiles.length - 1;
- i--;
+ currentTransferProfile = currentProfiles[i].transferProfiles[remainingTransfers];
+ departureTime = new Date(currentTransferProfile.departureTime);
+
+ while (i >= 0 && departureTime < exitConnection.arrivalTime) {
+ currentTransferProfile = currentProfiles[--i].transferProfiles[remainingTransfers];
+ departureTime = new Date(currentTransferProfile.departureTime);
}
- if (!found) {
- return Promise.reject("Can't reach arrival stop: " + arrivalStop.id + ", transfers: " + transfers);
+ if (i === -1) {
+ // This should never happen.
+ return Promise.reject("Can't find next connection");
}
+
}
- }
- if (path.steps[path.steps.length - 1].stopLocation.id !== arrivalStop.id) {
- shouldReturn = await this.addFinalFootpath(path, arrivalStop, query);
+ // Final footpath.
+ if (remainingTransfers === -1) {
+ const transferArrivalTime: Date = exitConnection.arrivalTime;
+ const arrivalTime: Date = new Date(currentTransferProfile.arrivalTime);
+
+ if (arrivalTime.getTime() !== transferArrivalTime.getTime()) {
+ const footpath: IStep = Step.create(
+ currentLocation,
+ arrivalLocation,
+ TravelMode.Walking,
+ {
+ minimum: arrivalTime.getTime() - transferArrivalTime.getTime(),
+ },
+ transferArrivalTime,
+ arrivalTime,
+ );
+
+ path.addStep(footpath);
+ }
+ }
}
- if (!shouldReturn) {
- return Promise.reject("Can't reach arrival stop: " + arrivalStop.id + ", transfers: " + transfers);
+ // Check if path ends in the arrival location.
+ if (path.steps[path.steps.length - 1].stopLocation.id !== arrivalLocation.id) {
+ // This should never happen.
+ return Promise.reject("Can't reach arrival stop:");
}
return path as IPath;
}
-
- private addInitialFootpath(
- path: Path,
- transferProfile: ITransferProfile,
- reachableStop: IReachableStop,
- startLocation: ILocation,
- ): void {
- const enterConnection: IConnection = transferProfile.enterConnection;
- const stopTime = enterConnection.departureTime;
- const startTime = new Date(stopTime.valueOf() - reachableStop.duration);
-
- const step = Step.create(
- startLocation,
- reachableStop.stop as ILocation,
- TravelMode.Walking,
- { minimum: reachableStop.duration },
- startTime,
- enterConnection.departureTime,
- undefined,
- );
-
- path.addStep(step);
- }
-
- private async addFinalFootpath(path: Path, arrivalStop: ILocation, query: IResolvedQuery): Promise {
- const lastStep = path.steps[path.steps.length - 1];
-
- const fromLocation = await this.locationResolver.resolve(lastStep.stopLocation.id);
- const toLocation = await this.locationResolver.resolve(arrivalStop);
-
- const walkingResult = await this.finalRoadPlanner.plan({
- from: [fromLocation],
- to: [toLocation],
- minimumWalkingSpeed: query.minimumWalkingSpeed,
- maximumWalkingSpeed: query.maximumWalkingSpeed,
- });
-
- if (walkingResult && walkingResult[0] && walkingResult[0].steps[0] &&
- query.maximumArrivalTime.getTime() >= lastStep.stopTime.getTime() +
- walkingResult[0].steps[0].duration.minimum) {
- path.addStep(walkingResult[0].steps[0]);
- return true;
- }
- return false;
- }
}
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
index 760564ad..ba27eaa2 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
@@ -37,11 +37,7 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
const locationResolver = new LocationResolverDefault(stopsFetcher);
const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher);
- const roadPlanner = new RoadPlannerBirdsEye();
const journeyExtractor = new JourneyExtractorDefault(
- roadPlanner,
- roadPlanner,
- reachableStopsFinder,
locationResolver,
);
@@ -50,6 +46,7 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
locationResolver,
reachableStopsFinder,
reachableStopsFinder,
+ reachableStopsFinder,
journeyExtractor,
);
};
@@ -167,11 +164,7 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
const locationResolver = new LocationResolverDefault(stopsFetcher);
const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher);
- const roadPlanner = new RoadPlannerBirdsEye();
const journeyExtractor = new JourneyExtractorDefault(
- roadPlanner,
- roadPlanner,
- reachableStopsFinder,
locationResolver,
);
@@ -180,6 +173,7 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
locationResolver,
reachableStopsFinder,
reachableStopsFinder,
+ reachableStopsFinder,
journeyExtractor,
);
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
index b2e696a5..8d5476c4 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
@@ -3,13 +3,14 @@ import { inject, injectable, tagged } from "inversify";
import IConnection from "../../fetcher/connections/IConnection";
import IConnectionsProvider from "../../fetcher/connections/IConnectionsProvider";
import IStop from "../../fetcher/stops/IStop";
+import ILocation from "../../interfaces/ILocation";
import IPath from "../../interfaces/IPath";
import { DurationMs } from "../../interfaces/units";
import ILocationResolver from "../../query-runner/ILocationResolver";
import IResolvedQuery from "../../query-runner/IResolvedQuery";
import TYPES from "../../types";
import Vectors from "../../util/Vectors";
-import IReachableStopsFinder from "../stops/IReachableStopsFinder";
+import IReachableStopsFinder, { IReachableStop } from "../stops/IReachableStopsFinder";
import ReachableStopsFinderMode from "../stops/ReachableStopsFinderMode";
import ReachableStopsSearchPhase from "../stops/ReachableStopsSearchPhase";
import IArrivalTimeByTransfers from "./CSA/data-structure/IArrivalTimeByTransfers";
@@ -37,6 +38,7 @@ import IPublicTransportPlanner from "./IPublicTransportPlanner";
export default class PublicTransportPlannerCSAProfile implements IPublicTransportPlanner {
private readonly connectionsProvider: IConnectionsProvider;
private readonly locationResolver: ILocationResolver;
+ private readonly initialReachableStopsFinder: IReachableStopsFinder;
private readonly finalReachableStopsFinder: IReachableStopsFinder;
private readonly transferReachableStopsFinder: IReachableStopsFinder;
private readonly journeyExtractor: IJourneyExtractor;
@@ -45,6 +47,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
private earliestArrivalByTrip: IEarliestArrivalByTrip = {}; // T
private durationToTargetByStop: DurationMs[] = [];
private gtfsTripByConnection = {};
+ private initialReachableStops: IReachableStop[] = [];
private query: IResolvedQuery;
private connectionsIterator: AsyncIterator;
@@ -53,6 +56,9 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
@inject(TYPES.ConnectionsProvider) connectionsProvider: IConnectionsProvider,
@inject(TYPES.LocationResolver) locationResolver: ILocationResolver,
@inject(TYPES.ReachableStopsFinder)
+ @tagged("phase", ReachableStopsSearchPhase.Initial)
+ initialReachableStopsFinder: IReachableStopsFinder,
+ @inject(TYPES.ReachableStopsFinder)
@tagged("phase", ReachableStopsSearchPhase.Transfer)
transferReachableStopsFinder: IReachableStopsFinder,
@inject(TYPES.ReachableStopsFinder)
@@ -62,6 +68,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
) {
this.connectionsProvider = connectionsProvider;
this.locationResolver = locationResolver;
+ this.initialReachableStopsFinder = initialReachableStopsFinder;
this.transferReachableStopsFinder = transferReachableStopsFinder;
this.finalReachableStopsFinder = finalReachableStopsFinder;
this.journeyExtractor = journeyExtractor;
@@ -89,6 +96,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
private async calculateJourneys(): Promise> {
await this.initDurationToTargetByStop();
+ await this.initInitialReachableStops();
this.connectionsIterator = this.connectionsProvider.createIterator();
@@ -209,10 +217,24 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
}
}
+ private async initInitialReachableStops(): Promise {
+ const fromLocation: IStop = this.query.from[0] as IStop;
+
+ this.initialReachableStops = await this.initialReachableStopsFinder.findReachableStops(
+ fromLocation,
+ ReachableStopsFinderMode.Source,
+ this.query.maximumTransferDuration,
+ this.query.minimumWalkingSpeed,
+ );
+ }
+
private walkToTarget(connection: IConnection): IArrivalTimeByTransfers {
const walkingTimeToTarget = this.durationToTargetByStop[connection.arrivalStop];
- if (walkingTimeToTarget === undefined) {
+ if (
+ walkingTimeToTarget === undefined ||
+ connection.arrivalTime.getTime() + walkingTimeToTarget > this.query.maximumArrivalTime.getTime()
+ ) {
return Array(this.query.maximumTransfers + 1).fill({
"arrivalTime": Infinity,
"gtfs:trip": connection["gtfs:trip"],
@@ -291,25 +313,46 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
currentArrivalTimeByTransfers: IArrivalTimeByTransfers,
): Promise {
const depProfile: Profile[] = this.profilesByStop[connection.departureStop];
- const earliestProfileEntry = depProfile[depProfile.length - 1];
+ const earliestProfileEntry: Profile = depProfile[depProfile.length - 1];
- const earliestArrivalTimeByTransfers = Vectors.minVector(
+ const earliestArrivalTimeByTransfers: IArrivalTimeByTransfers = Vectors.minVector(
(c) => c.arrivalTime,
currentArrivalTimeByTransfers,
earliestProfileEntry.getArrivalTimeByTransfers(),
);
- const departureStop = await this.locationResolver.resolve(connection.departureStop);
- const reachableStops = await this.transferReachableStopsFinder.findReachableStops(
+ const canReachInitialStop: IReachableStop = this.initialReachableStops.find((reachable: IReachableStop) =>
+ reachable.stop.id === connection.departureStop,
+ );
+
+ if (canReachInitialStop) {
+ const fromLocation: IStop = this.query.from[0] as IStop;
+
+ this.incorporateInProfile(
+ connection,
+ canReachInitialStop.duration,
+ fromLocation,
+ earliestArrivalTimeByTransfers,
+ );
+ }
+
+ const departureStop: ILocation = await this.locationResolver.resolve(connection.departureStop);
+ const reachableStops: IReachableStop[] = await this.transferReachableStopsFinder.findReachableStops(
departureStop as IStop,
ReachableStopsFinderMode.Source,
this.query.maximumTransferDuration,
this.query.minimumWalkingSpeed,
);
- reachableStops.forEach((reachableStop) => {
- // Incorporate (c_dep_time - f_dur, t_c) into profile of S[f_dep_stop]
- this.incorporateInProfile(connection, reachableStop.duration, reachableStop.stop, earliestArrivalTimeByTransfers);
+ reachableStops.forEach((reachableStop: IReachableStop) => {
+ if (reachableStop.stop.id !== this.query.from[0].id) {
+ this.incorporateInProfile(
+ connection,
+ reachableStop.duration,
+ reachableStop.stop,
+ earliestArrivalTimeByTransfers,
+ );
+ }
});
}
@@ -319,6 +362,12 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
stop: IStop,
arrivalTimeByTransfers: IArrivalTimeByTransfers,
) {
+ const departureTime = connection.departureTime.getTime() - duration;
+
+ if (departureTime < this.query.minimumDepartureTime.getTime()) {
+ return;
+ }
+
let profilesByDepartureStop = this.profilesByStop[stop.id];
if (!profilesByDepartureStop) {
@@ -340,6 +389,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
exitConnection: undefined,
enterConnection: undefined,
arrivalTime: Infinity,
+ departureTime: Infinity,
};
const possibleExitConnection = this.earliestArrivalByTrip[connection["gtfs:trip"]]
@@ -352,9 +402,11 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
) {
newTransferProfile.enterConnection = connection;
newTransferProfile.exitConnection = possibleExitConnection;
+ newTransferProfile.departureTime = departureTime;
} else {
newTransferProfile.enterConnection = transferProfile.enterConnection;
newTransferProfile.exitConnection = transferProfile.exitConnection;
+ newTransferProfile.departureTime = transferProfile.departureTime;
}
if (newTransferProfile.exitConnection && newTransferProfile.enterConnection) {
@@ -364,7 +416,6 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
transferProfiles.push(newTransferProfile);
}
- const departureTime = connection.departureTime.getTime() - duration;
const newProfile: Profile = Profile.createFromTransfers(departureTime, transferProfiles);
let i = profilesByDepartureStop.length - 1;
From cf986ab78bb0fc7c98170a3462feab143a1156cc Mon Sep 17 00:00:00 2001
From: Maxim Martin
Date: Wed, 5 Dec 2018 17:21:39 +0100
Subject: [PATCH 08/20] 1. Make sure there is a profile for the departure
location. 2. Split transfer reachable stops into initial and transfer
reachable stops. #45 #44
---
.../PublicTransportPlannerCSAProfile.test.ts | 16 ++++++++---
.../PublicTransportPlannerCSAProfile.ts | 27 ++++++++++++++-----
2 files changed, 33 insertions(+), 10 deletions(-)
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
index ba27eaa2..c17d1b38 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
@@ -1,5 +1,6 @@
import "jest";
import LDFetch from "ldfetch";
+import Defaults from "../../Defaults";
import ConnectionsFetcherLazy from "../../fetcher/connections/ld-fetch/ConnectionsFetcherLazy";
import ConnectionsFetcherNMBSTest from "../../fetcher/connections/tests/ConnectionsFetcherNMBSTest";
import connectionsIngelmunsterGhent from "../../fetcher/connections/tests/data/ingelmunster-ghent";
@@ -16,7 +17,6 @@ import QueryRunnerDefault from "../../query-runner/QueryRunnerDefault";
import TravelMode from "../../TravelMode";
import Iterators from "../../util/Iterators";
import Units from "../../util/Units";
-import RoadPlannerBirdsEye from "../road/RoadPlannerBirdsEye";
import ReachableStopsFinderBirdsEyeCached from "../stops/ReachableStopsFinderBirdsEyeCached";
import JourneyExtractorDefault from "./JourneyExtractorDefault";
import PublicTransportPlannerCSAProfile from "./PublicTransportPlannerCSAProfile";
@@ -56,17 +56,21 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
const query: IResolvedQuery = {
publicTransportOnly: true,
- from: [{ id: "http://irail.be/stations/NMBS/008896925", latitude: 50.914326, longitude: 3.255416 }],
- to: [{ id: "http://irail.be/stations/NMBS/008892007", latitude: 51.035896, longitude: 3.710675 }],
+ from: [{latitude: 50.914326, longitude: 3.255415 }],
+ to: [{ latitude: 51.035896, longitude: 3.710875 }],
minimumDepartureTime: new Date("2018-11-06T09:00:00.000Z"),
maximumArrivalTime: new Date("2018-11-06T19:00:00.000Z"),
maximumTransfers: 8,
+ minimumWalkingSpeed: Defaults.defaultMinimumWalkingSpeed,
+ maximumWalkingSpeed: Defaults.defaultMaximumWalkingSpeed,
+ maximumTransferDuration: Defaults.defaultMaximumTransferDuration,
};
beforeAll(async () => {
const CSA = createCSA(connectionsIngelmunsterGhent);
const iterator = await CSA.plan(query);
result = await Iterators.toArray(iterator);
+ console.log(result);
});
it("Correct departure and arrival stop", () => {
@@ -91,6 +95,9 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
minimumDepartureTime: new Date("2017-12-19T15:50:00.000Z"),
maximumArrivalTime: new Date("2017-12-19T16:50:00.000Z"),
maximumTransfers: 1,
+ minimumWalkingSpeed: Defaults.defaultMinimumWalkingSpeed,
+ maximumWalkingSpeed: Defaults.defaultMaximumWalkingSpeed,
+ maximumTransferDuration: Defaults.defaultMaximumTransferDuration,
};
beforeAll(async () => {
@@ -125,6 +132,9 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
minimumDepartureTime: new Date("2017-12-19T16:20:00.000Z"),
maximumArrivalTime: new Date("2017-12-19T16:50:00.000Z"),
maximumTransfers: 1,
+ minimumWalkingSpeed: Defaults.defaultMinimumWalkingSpeed,
+ maximumWalkingSpeed: Defaults.defaultMaximumWalkingSpeed,
+ maximumTransferDuration: Defaults.defaultMaximumTransferDuration,
};
beforeAll(async () => {
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
index 8d5476c4..4439ecdc 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
@@ -76,6 +76,19 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
public async plan(query: IResolvedQuery): Promise> {
this.query = query;
+
+ const departureLocation = this.query.from[0];
+ if (!departureLocation.id) {
+ departureLocation.id = "geo:" + departureLocation.latitude + "," + departureLocation.longitude;
+ departureLocation.name = "Departure location";
+ }
+
+ const arrivalLocation = this.query.to[0];
+ if (!arrivalLocation.id) {
+ arrivalLocation.id = "geo:" + arrivalLocation.latitude + "," + arrivalLocation.longitude;
+ arrivalLocation.name = "Arrival location";
+ }
+
this.setBounds();
return this.calculateJourneys();
@@ -312,6 +325,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
connection: IConnection,
currentArrivalTimeByTransfers: IArrivalTimeByTransfers,
): Promise {
+ const departureLocation: IStop = this.query.from[0] as IStop;
const depProfile: Profile[] = this.profilesByStop[connection.departureStop];
const earliestProfileEntry: Profile = depProfile[depProfile.length - 1];
@@ -321,17 +335,16 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
earliestProfileEntry.getArrivalTimeByTransfers(),
);
- const canReachInitialStop: IReachableStop = this.initialReachableStops.find((reachable: IReachableStop) =>
+ const initialReachableStop: IReachableStop = this.initialReachableStops.find(
+ (reachable: IReachableStop) =>
reachable.stop.id === connection.departureStop,
);
- if (canReachInitialStop) {
- const fromLocation: IStop = this.query.from[0] as IStop;
-
+ if (initialReachableStop) {
this.incorporateInProfile(
connection,
- canReachInitialStop.duration,
- fromLocation,
+ initialReachableStop.duration,
+ departureLocation,
earliestArrivalTimeByTransfers,
);
}
@@ -345,7 +358,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
);
reachableStops.forEach((reachableStop: IReachableStop) => {
- if (reachableStop.stop.id !== this.query.from[0].id) {
+ if (reachableStop.stop.id !== departureLocation.id) {
this.incorporateInProfile(
connection,
reachableStop.duration,
From c77776a25d28f734128ffe4c9af1ddbc184113c0 Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Thu, 20 Dec 2018 09:47:11 +0100
Subject: [PATCH 09/20] Revert to nmbs demo
---
src/demo.ts | 8 ++++----
src/inversify.config.ts | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/demo.ts b/src/demo.ts
index 70b4fff2..4e053d31 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -25,10 +25,10 @@ export default async (logResults) => {
// to: "https://data.delijn.be/stops/205910",
// from: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456
// to: "https://data.delijn.be/stops/502481", // Tielt Metaalconstructie Goossens
- from: "https://data.delijn.be/stops/509927", // Tield Rameplein perron 1
- to: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456
- // from: "http://irail.be/stations/NMBS/008896008", // Kortrijk
- // to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
+ // from: "https://data.delijn.be/stops/509927", // Tield Rameplein perron 1
+ // to: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456
+ from: "http://irail.be/stations/NMBS/008896008", // Kortrijk
+ to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
minimumDepartureTime: new Date(),
maximumTransferDuration: Units.fromHours(.01),
});
diff --git a/src/inversify.config.ts b/src/inversify.config.ts
index 662c3d39..8367d9e1 100644
--- a/src/inversify.config.ts
+++ b/src/inversify.config.ts
@@ -1,7 +1,7 @@
import { Container, interfaces } from "inversify";
import LDFetch from "ldfetch";
import Catalog from "./Catalog";
-import catalog from "./catalog.delijn";
+import catalog from "./catalog.nmbs";
import Context from "./Context";
import IConnectionsFetcher from "./fetcher/connections/IConnectionsFetcher";
import IConnectionsProvider from "./fetcher/connections/IConnectionsProvider";
From 735f874a073f29ab386d9a415c19c225f4bafbfe Mon Sep 17 00:00:00 2001
From: Maxim Martin
Date: Thu, 20 Dec 2018 09:47:14 +0100
Subject: [PATCH 10/20] 1. fix joining/splitting tests.
---
.../PublicTransportPlannerCSAProfile.test.ts | 25 +++++++++++++++----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
index c17d1b38..2c48688e 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts
@@ -70,7 +70,6 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
const CSA = createCSA(connectionsIngelmunsterGhent);
const iterator = await CSA.plan(query);
result = await Iterators.toArray(iterator);
- console.log(result);
});
it("Correct departure and arrival stop", () => {
@@ -90,8 +89,16 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
const query: IResolvedQuery = {
publicTransportOnly: true,
- from: [{ id: "http://irail.be/stations/NMBS/008821006" }],
- to: [{ id: "http://irail.be/stations/NMBS/008812005" }],
+ from: [{
+ id: "http://irail.be/stations/NMBS/008821006",
+ latitude: 51.2172,
+ longitude: 4.421101,
+ }],
+ to: [{
+ id: "http://irail.be/stations/NMBS/008812005",
+ latitude: 50.859663,
+ longitude: 4.360846,
+ }],
minimumDepartureTime: new Date("2017-12-19T15:50:00.000Z"),
maximumArrivalTime: new Date("2017-12-19T16:50:00.000Z"),
maximumTransfers: 1,
@@ -127,8 +134,16 @@ describe("[PublicTransportPlannerCSAProfile]", () => {
const query: IResolvedQuery = {
publicTransportOnly: true,
- from: [{ id: "http://irail.be/stations/NMBS/008812005" }],
- to: [{ id: "http://irail.be/stations/NMBS/008821006" }],
+ from: [{
+ id: "http://irail.be/stations/NMBS/008812005",
+ latitude: 50.859663,
+ longitude: 4.360846,
+ }],
+ to: [{
+ id: "http://irail.be/stations/NMBS/008821006",
+ latitude: 51.2172,
+ longitude: 4.421101,
+ }],
minimumDepartureTime: new Date("2017-12-19T16:20:00.000Z"),
maximumArrivalTime: new Date("2017-12-19T16:50:00.000Z"),
maximumTransfers: 1,
From 337128447223c6c7a1ad749a7e71c0685a552caf Mon Sep 17 00:00:00 2001
From: Maxim Martin
Date: Thu, 20 Dec 2018 11:17:35 +0100
Subject: [PATCH 11/20] 1. added minimumTransferDuration. Default 1min.
---
src/Defaults.ts | 1 +
src/interfaces/IQuery.ts | 1 +
.../public-transport/CSA/util/ProfileUtil.ts | 25 ++++++++++++++-----
.../PublicTransportPlannerCSAProfile.ts | 12 +++++++--
src/query-runner/IResolvedQuery.ts | 1 +
src/query-runner/QueryRunnerDefault.ts | 3 ++-
.../exponential/QueryRunnerExponential.ts | 3 ++-
7 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/src/Defaults.ts b/src/Defaults.ts
index 0406b800..58ef7c2e 100644
--- a/src/Defaults.ts
+++ b/src/Defaults.ts
@@ -3,6 +3,7 @@ import Units from "./util/Units";
export default class Defaults {
public static readonly defaultMinimumWalkingSpeed = 3;
public static readonly defaultMaximumWalkingSpeed = 6;
+ public static readonly defaultMinimumTransferDuration = Units.fromSeconds(60);
public static readonly defaultMaximumTransferDuration = Units.fromHours(.4);
public static readonly defaultMaximumTransfers = 8;
}
diff --git a/src/interfaces/IQuery.ts b/src/interfaces/IQuery.ts
index 3041e945..48612005 100644
--- a/src/interfaces/IQuery.ts
+++ b/src/interfaces/IQuery.ts
@@ -11,6 +11,7 @@ export default interface IQuery {
walkingSpeed?: SpeedkmH;
minimumWalkingSpeed?: SpeedkmH;
maximumWalkingSpeed?: SpeedkmH;
+ minimumTransferDuration?: DurationMs;
maximumTransferDuration?: DurationMs;
maximumTransfers?: number;
}
diff --git a/src/planner/public-transport/CSA/util/ProfileUtil.ts b/src/planner/public-transport/CSA/util/ProfileUtil.ts
index 5ae0a827..94c51828 100644
--- a/src/planner/public-transport/CSA/util/ProfileUtil.ts
+++ b/src/planner/public-transport/CSA/util/ProfileUtil.ts
@@ -1,4 +1,5 @@
import IConnection from "../../../../fetcher/connections/IConnection";
+import IArrivalTimeByTransfers from "../data-structure/IArrivalTimeByTransfers";
import IProfilesByStop from "../data-structure/stops/IProfilesByStop";
/**
@@ -19,16 +20,28 @@ export default class ProfileUtil {
return result;
}
- public static getTransferTimes(profilesByStop: IProfilesByStop, connection: IConnection, maxLegs) {
+ public static getTransferTimes(
+ profilesByStop: IProfilesByStop,
+ connection: IConnection,
+ maxLegs,
+ minimumTransferDuration,
+ ): IArrivalTimeByTransfers {
const { arrivalStop, arrivalTime } = connection;
+ const trip = connection["gtfs:trip"];
- let i = profilesByStop[arrivalStop].length - 1;
- while (i >= 0) {
- if (profilesByStop[arrivalStop][i].departureTime >= arrivalTime.getTime()) {
- return profilesByStop[arrivalStop][i].getArrivalTimeByTransfers(connection["gtfs:trip"]).slice();
+ if (connection["gtfs:dropOffType"] !== "gtfs:NotAvailable") {
+
+ let i = profilesByStop[arrivalStop].length - 1;
+ while (i >= 0) {
+ if (profilesByStop[arrivalStop][i].departureTime >= arrivalTime.getTime() + minimumTransferDuration) {
+ const arrivalTimeByTransfers = profilesByStop[arrivalStop][i].getArrivalTimeByTransfers(trip);
+ return arrivalTimeByTransfers.slice() as IArrivalTimeByTransfers;
+ }
+ i--;
}
- i--;
+
}
+
return Array(maxLegs + 1).fill({
"arrivalTime": Infinity,
"gtfs:trip": connection["gtfs:trip"],
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
index 4439ecdc..542f260e 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
@@ -245,7 +245,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
const walkingTimeToTarget = this.durationToTargetByStop[connection.arrivalStop];
if (
- walkingTimeToTarget === undefined ||
+ walkingTimeToTarget === undefined || connection["gtfs:dropOfType"] === "gtfs:NotAvailable" ||
connection.arrivalTime.getTime() + walkingTimeToTarget > this.query.maximumArrivalTime.getTime()
) {
return Array(this.query.maximumTransfers + 1).fill({
@@ -286,8 +286,16 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
}
private takeTransfer(connection: IConnection): IArrivalTimeByTransfers {
+
+ const transferTimes: IArrivalTimeByTransfers = ProfileUtil.getTransferTimes(
+ this.profilesByStop,
+ connection,
+ this.query.maximumTransfers,
+ this.query.minimumTransferDuration,
+ );
+
return Vectors.shiftVector(
- ProfileUtil.getTransferTimes(this.profilesByStop, connection, this.query.maximumTransfers),
+ transferTimes,
{ "arrivalTime": Infinity, "gtfs:trip": connection["gtfs:trip"] },
);
}
diff --git a/src/query-runner/IResolvedQuery.ts b/src/query-runner/IResolvedQuery.ts
index db2f1710..2d8ed37b 100644
--- a/src/query-runner/IResolvedQuery.ts
+++ b/src/query-runner/IResolvedQuery.ts
@@ -10,6 +10,7 @@ export default interface IResolvedQuery {
publicTransportOnly?: boolean;
minimumWalkingSpeed?: SpeedkmH;
maximumWalkingSpeed?: SpeedkmH;
+ minimumTransferDuration?: DurationMs;
maximumTransferDuration?: DurationMs;
maximumTransfers?: number;
}
diff --git a/src/query-runner/QueryRunnerDefault.ts b/src/query-runner/QueryRunnerDefault.ts
index dbd78739..3703bf00 100644
--- a/src/query-runner/QueryRunnerDefault.ts
+++ b/src/query-runner/QueryRunnerDefault.ts
@@ -54,7 +54,7 @@ export default class QueryRunnerDefault implements IQueryRunner {
const {
from, to,
minimumWalkingSpeed, maximumWalkingSpeed, walkingSpeed,
- maximumTransferDuration, maximumTransfers,
+ minimumTransferDuration, maximumTransferDuration, maximumTransfers,
minimumDepartureTime, maximumArrivalTime,
...other
} = query;
@@ -79,6 +79,7 @@ export default class QueryRunnerDefault implements IQueryRunner {
resolvedQuery.minimumWalkingSpeed = minimumWalkingSpeed || walkingSpeed || Defaults.defaultMinimumWalkingSpeed;
resolvedQuery.maximumWalkingSpeed = maximumWalkingSpeed || walkingSpeed || Defaults.defaultMaximumWalkingSpeed;
+ resolvedQuery.minimumTransferDuration = minimumTransferDuration || Defaults.defaultMinimumTransferDuration;
resolvedQuery.maximumTransferDuration = maximumTransferDuration || Defaults.defaultMaximumTransferDuration;
resolvedQuery.maximumTransfers = maximumTransfers || Defaults.defaultMaximumTransfers;
diff --git a/src/query-runner/exponential/QueryRunnerExponential.ts b/src/query-runner/exponential/QueryRunnerExponential.ts
index d5e2f3f2..e1947b94 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.ts
@@ -74,7 +74,7 @@ export default class QueryRunnerExponential implements IQueryRunner {
const {
from, to,
minimumWalkingSpeed, maximumWalkingSpeed, walkingSpeed,
- maximumTransferDuration, maximumTransfers,
+ minimumTransferDuration, maximumTransferDuration, maximumTransfers,
minimumDepartureTime,
...other
} = query;
@@ -89,6 +89,7 @@ export default class QueryRunnerExponential implements IQueryRunner {
resolvedQuery.minimumWalkingSpeed = minimumWalkingSpeed || walkingSpeed || Defaults.defaultMinimumWalkingSpeed;
resolvedQuery.maximumWalkingSpeed = maximumWalkingSpeed || walkingSpeed || Defaults.defaultMaximumWalkingSpeed;
+ resolvedQuery.minimumTransferDuration = minimumTransferDuration || Defaults.defaultMinimumTransferDuration;
resolvedQuery.maximumTransferDuration = maximumTransferDuration || Defaults.defaultMaximumTransferDuration;
resolvedQuery.maximumTransfers = maximumTransfers || Defaults.defaultMaximumTransfers;
From 8626b0f1d0690356def52296b2362496e0b10f40 Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Thu, 20 Dec 2018 11:34:39 +0100
Subject: [PATCH 12/20] Event emitter POC
---
src/Context.ts | 7 ++++--
src/EventTypes.ts | 6 +++++
src/Planner.ts | 56 ++++++++++++++++++++++++++++++++++++++++-
src/demo.ts | 9 +++++++
src/fetcher/LDFetch.ts | 48 +++++++++++++++++++++++++++++++++++
src/inversify.config.ts | 16 ++----------
6 files changed, 125 insertions(+), 17 deletions(-)
create mode 100644 src/EventTypes.ts
create mode 100644 src/fetcher/LDFetch.ts
diff --git a/src/Context.ts b/src/Context.ts
index ad8f2992..2c0fe5cd 100644
--- a/src/Context.ts
+++ b/src/Context.ts
@@ -1,7 +1,10 @@
-import { Container, injectable } from "inversify";
+import { EventEmitter } from "events";
+import { Container, decorate, injectable } from "inversify";
+
+decorate(injectable(), EventEmitter);
@injectable()
-export default class Context {
+export default class Context extends EventEmitter {
private container: Container;
public setContainer(container: Container) {
diff --git a/src/EventTypes.ts b/src/EventTypes.ts
new file mode 100644
index 00000000..c8f8079e
--- /dev/null
+++ b/src/EventTypes.ts
@@ -0,0 +1,6 @@
+enum EventTypes {
+ Query = "query",
+ LDFetchGet = "ldfetch-get",
+}
+
+export default EventTypes;
diff --git a/src/Planner.ts b/src/Planner.ts
index d20bcc79..72f1c07e 100644
--- a/src/Planner.ts
+++ b/src/Planner.ts
@@ -1,5 +1,8 @@
import { AsyncIterator } from "asynciterator";
+// @ts-ignore
+import { EventEmitter, Listener } from "events";
import Context from "./Context";
+import EventTypes from "./EventTypes";
import IPath from "./interfaces/IPath";
import IQuery from "./interfaces/IQuery";
import defaultContainer from "./inversify.config";
@@ -13,7 +16,8 @@ if (!Symbol.asyncIterator) {
/**
* Allows to ask route planning queries over our knowledge graphs
*/
-export default class Planner {
+// @ts-ignore
+export default class Planner implements EventEmitter {
private context: Context;
private queryRunner: IQueryRunner;
@@ -35,6 +39,56 @@ export default class Planner {
* @returns An AsyncIterator of [[IPath]]s
*/
public async query(query: IQuery): Promise> {
+ this.emit(EventTypes.Query, query);
+
return this.queryRunner.run(query);
}
+
+ public addListener(type: string | symbol, listener: Listener): this {
+ this.context.addListener(type, listener);
+
+ return this;
+ }
+
+ public emit(type: string | symbol, ...args: any[]): boolean {
+ return this.context.emit(type, ...args);
+ }
+
+ public listenerCount(type: string | symbol): number {
+ return this.context.listenerCount(type);
+ }
+
+ public listeners(type: string | symbol): Listener[] {
+ return this.context.listeners(type);
+ }
+
+ public on(type: string | symbol, listener: Listener): this {
+ this.context.on(type, listener);
+
+ return this;
+ }
+
+ public once(type: string | symbol, listener: Listener): this {
+ this.context.once(type, listener);
+
+ return this;
+ }
+
+ public removeAllListeners(type?: string | symbol): this {
+ this.context.removeAllListeners(type);
+
+ return this;
+ }
+
+ public removeListener(type: string | symbol, listener: Listener): this {
+ this.context.removeListener(type, listener);
+
+ return this;
+ }
+
+ public setMaxListeners(n: number): this {
+ this.context.setMaxListeners(n);
+
+ return this;
+ }
}
diff --git a/src/demo.ts b/src/demo.ts
index 4e053d31..fffa1eab 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -1,3 +1,4 @@
+import EventTypes from "./EventTypes";
import Planner from "./index";
import Units from "./util/Units";
@@ -19,6 +20,14 @@ export default async (logResults) => {
console.time("Public transport planner");
+ planner.on(EventTypes.Query, (...args) => {
+ console.log("Query", args);
+ });
+
+ planner.on(EventTypes.LDFetchGet, (url, duration) => {
+ console.log(`[GET] ${url} (${duration}ms)`);
+ });
+
const publicTransportResult = await planner.query({
publicTransportOnly: true,
// from: "https://data.delijn.be/stops/201657",
diff --git a/src/fetcher/LDFetch.ts b/src/fetcher/LDFetch.ts
new file mode 100644
index 00000000..b22d269d
--- /dev/null
+++ b/src/fetcher/LDFetch.ts
@@ -0,0 +1,48 @@
+import { inject, injectable } from "inversify";
+import LDFetchBase from "ldfetch";
+import { Triple } from "rdf-js";
+import Context from "../Context";
+import EventTypes from "../EventTypes";
+import TYPES from "../types";
+
+export interface ILDFetchResponse {
+ triples: Triple[];
+ prefixes: object;
+ statusCode: string;
+ url: string;
+}
+
+/**
+ * Proxies an ldfetch instance to transform its events
+ */
+@injectable()
+export default class LDFetch implements LDFetchBase {
+ private context: Context;
+ private ldFetchBase: LDFetchBase;
+ private httpStartTimes: { [url: string]: Date };
+
+ constructor(
+ @inject(TYPES.Context) context: Context,
+ ) {
+ this.ldFetchBase = new LDFetchBase({ headers: { Accept: "application/ld+json" } });
+ this.context = context;
+
+ this.setupEvents();
+ }
+
+ public get(url: string): Promise {
+ return this.ldFetchBase.get(url);
+ }
+
+ private setupEvents(): void {
+ this.httpStartTimes = {};
+
+ this.ldFetchBase.on("request", (url) => this.httpStartTimes[url] = new Date());
+ this.ldFetchBase.on("redirect", (obj) => this.httpStartTimes[obj.to] = this.httpStartTimes[obj.from]);
+ this.ldFetchBase.on("response", (url) => {
+ const duration = (new Date()).getTime() - this.httpStartTimes[url].getTime();
+
+ this.context.emit(EventTypes.LDFetchGet, url, duration);
+ });
+ }
+}
diff --git a/src/inversify.config.ts b/src/inversify.config.ts
index 8367d9e1..de2f9394 100644
--- a/src/inversify.config.ts
+++ b/src/inversify.config.ts
@@ -1,5 +1,4 @@
import { Container, interfaces } from "inversify";
-import LDFetch from "ldfetch";
import Catalog from "./Catalog";
import catalog from "./catalog.nmbs";
import Context from "./Context";
@@ -7,6 +6,7 @@ import IConnectionsFetcher from "./fetcher/connections/IConnectionsFetcher";
import IConnectionsProvider from "./fetcher/connections/IConnectionsProvider";
import ConnectionsFetcherLazy from "./fetcher/connections/ld-fetch/ConnectionsFetcherLazy";
import ConnectionsProviderMerge from "./fetcher/connections/merge/ConnectionsProviderMerge";
+import LDFetch from "./fetcher/LDFetch";
import IStopsFetcher from "./fetcher/stops/IStopsFetcher";
import IStopsProvider from "./fetcher/stops/IStopsProvider";
import StopsFetcherLDFetch from "./fetcher/stops/ld-fetch/StopsFetcherLDFetch";
@@ -85,18 +85,6 @@ container.bind>(TYPES.StopsFetcherFactory)
container.bind(TYPES.Catalog).toConstantValue(catalog);
// Init LDFetch
-const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
-const httpStartTimes = {};
-
-ldFetch.on("request", (url) => httpStartTimes[url] = new Date());
-
-ldFetch.on("redirect", (obj) => httpStartTimes[obj.to] = httpStartTimes[obj.from]);
-
-ldFetch.on("response", (url) => {
- const difference = (new Date()).getTime() - httpStartTimes[url].getTime();
- console.log(`HTTP GET - ${url} (${difference}ms)`);
-});
-
-container.bind(TYPES.LDFetch).toConstantValue(ldFetch);
+container.bind(TYPES.LDFetch).to(LDFetch).inSingletonScope();
export default container;
From e01840c220eb9c8d7aab0a289cfe802a22d40d7c Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Thu, 20 Dec 2018 11:40:03 +0100
Subject: [PATCH 13/20] Test chaining #on()'s
---
src/demo.ts | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/demo.ts b/src/demo.ts
index fffa1eab..9934125f 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -20,13 +20,13 @@ export default async (logResults) => {
console.time("Public transport planner");
- planner.on(EventTypes.Query, (...args) => {
- console.log("Query", args);
- });
-
- planner.on(EventTypes.LDFetchGet, (url, duration) => {
- console.log(`[GET] ${url} (${duration}ms)`);
- });
+ planner
+ .on(EventTypes.Query, (...args) => {
+ console.log("Query", args);
+ })
+ .on(EventTypes.LDFetchGet, (url, duration) => {
+ console.log(`[GET] ${url} (${duration}ms)`);
+ });
const publicTransportResult = await planner.query({
publicTransportOnly: true,
From 0d6a62b635f824ae27a72aa3181ace3e175dc6af Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Thu, 20 Dec 2018 13:02:59 +0100
Subject: [PATCH 14/20] Implement emiterator
---
src/EventType.ts | 7 ++++++
src/EventTypes.ts | 6 -----
src/Planner.ts | 4 +--
src/demo.ts | 13 +++++++---
src/fetcher/LDFetch.ts | 4 +--
.../merge/ConnectionsProviderMerge.ts | 2 +-
.../exponential/QueryRunnerExponential.ts | 14 ++++++++++-
src/util/ReduceIterator.ts | 0
src/util/iterators/Emiterator.ts | 25 +++++++++++++++++++
.../{ => iterators}/MergeIterator.test.ts | 0
src/util/{ => iterators}/MergeIterator.ts | 0
11 files changed, 59 insertions(+), 16 deletions(-)
create mode 100644 src/EventType.ts
delete mode 100644 src/EventTypes.ts
delete mode 100644 src/util/ReduceIterator.ts
create mode 100644 src/util/iterators/Emiterator.ts
rename src/util/{ => iterators}/MergeIterator.test.ts (100%)
rename src/util/{ => iterators}/MergeIterator.ts (100%)
diff --git a/src/EventType.ts b/src/EventType.ts
new file mode 100644
index 00000000..034c045b
--- /dev/null
+++ b/src/EventType.ts
@@ -0,0 +1,7 @@
+enum EventType {
+ Query = "query",
+ QueryExponential = "query-exponential",
+ LDFetchGet = "ldfetch-get",
+}
+
+export default EventType;
diff --git a/src/EventTypes.ts b/src/EventTypes.ts
deleted file mode 100644
index c8f8079e..00000000
--- a/src/EventTypes.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-enum EventTypes {
- Query = "query",
- LDFetchGet = "ldfetch-get",
-}
-
-export default EventTypes;
diff --git a/src/Planner.ts b/src/Planner.ts
index 72f1c07e..65c87b28 100644
--- a/src/Planner.ts
+++ b/src/Planner.ts
@@ -2,7 +2,7 @@ import { AsyncIterator } from "asynciterator";
// @ts-ignore
import { EventEmitter, Listener } from "events";
import Context from "./Context";
-import EventTypes from "./EventTypes";
+import EventType from "./EventType";
import IPath from "./interfaces/IPath";
import IQuery from "./interfaces/IQuery";
import defaultContainer from "./inversify.config";
@@ -39,7 +39,7 @@ export default class Planner implements EventEmitter {
* @returns An AsyncIterator of [[IPath]]s
*/
public async query(query: IQuery): Promise> {
- this.emit(EventTypes.Query, query);
+ this.emit(EventType.Query, query);
return this.queryRunner.run(query);
}
diff --git a/src/demo.ts b/src/demo.ts
index 5fe59a3b..d03d2bc2 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -1,4 +1,4 @@
-import EventTypes from "./EventTypes";
+import EventType from "./EventType";
import Planner from "./index";
import IPath from "./interfaces/IPath";
import IStep from "./interfaces/IStep";
@@ -23,10 +23,15 @@ export default async (logResults) => {
console.time("Public transport planner");
planner
- .on(EventTypes.Query, (...args) => {
- console.log("Query", args);
+ .on(EventType.Query, (query) => {
+ console.log("Query", query);
})
- .on(EventTypes.LDFetchGet, (url, duration) => {
+ .on(EventType.QueryExponential, (query) => {
+ const {minimumDepartureTime, maximumArrivalTime} = query;
+
+ console.log("[Subquery]", minimumDepartureTime, maximumArrivalTime, maximumArrivalTime - minimumDepartureTime);
+ })
+ .on(EventType.LDFetchGet, (url, duration) => {
console.log(`[GET] ${url} (${duration}ms)`);
});
diff --git a/src/fetcher/LDFetch.ts b/src/fetcher/LDFetch.ts
index b22d269d..fa5492a7 100644
--- a/src/fetcher/LDFetch.ts
+++ b/src/fetcher/LDFetch.ts
@@ -2,7 +2,7 @@ import { inject, injectable } from "inversify";
import LDFetchBase from "ldfetch";
import { Triple } from "rdf-js";
import Context from "../Context";
-import EventTypes from "../EventTypes";
+import EventType from "../EventType";
import TYPES from "../types";
export interface ILDFetchResponse {
@@ -42,7 +42,7 @@ export default class LDFetch implements LDFetchBase {
this.ldFetchBase.on("response", (url) => {
const duration = (new Date()).getTime() - this.httpStartTimes[url].getTime();
- this.context.emit(EventTypes.LDFetchGet, url, duration);
+ this.context.emit(EventType.LDFetchGet, url, duration);
});
}
}
diff --git a/src/fetcher/connections/merge/ConnectionsProviderMerge.ts b/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
index 010d91e7..9841fc99 100644
--- a/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
+++ b/src/fetcher/connections/merge/ConnectionsProviderMerge.ts
@@ -2,7 +2,7 @@ import { AsyncIterator } from "asynciterator";
import { inject, injectable } from "inversify";
import Catalog from "../../../Catalog";
import TYPES, { ConnectionsFetcherFactory } from "../../../types";
-import MergeIterator from "../../../util/MergeIterator";
+import MergeIterator from "../../../util/iterators/MergeIterator";
import IConnection from "../IConnection";
import IConnectionsFetcher from "../IConnectionsFetcher";
import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig";
diff --git a/src/query-runner/exponential/QueryRunnerExponential.ts b/src/query-runner/exponential/QueryRunnerExponential.ts
index e0fc7990..f7c7c70a 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.ts
@@ -1,11 +1,14 @@
import { AsyncIterator } from "asynciterator";
import { inject, injectable, interfaces } from "inversify";
+import Context from "../../Context";
import Defaults from "../../Defaults";
+import EventType from "../../EventType";
import ILocation from "../../interfaces/ILocation";
import IPath from "../../interfaces/IPath";
import IQuery from "../../interfaces/IQuery";
import IPublicTransportPlanner from "../../planner/public-transport/IPublicTransportPlanner";
import TYPES from "../../types";
+import Emiterator from "../../util/iterators/Emiterator";
import ILocationResolver from "../ILocationResolver";
import IQueryRunner from "../IQueryRunner";
import IResolvedQuery from "../IResolvedQuery";
@@ -17,13 +20,17 @@ import SubqueryIterator from "./SubqueryIterator";
export default class QueryRunnerExponential implements IQueryRunner {
private locationResolver: ILocationResolver;
private publicTransportPlannerFactory: interfaces.Factory;
+ private context: Context;
constructor(
+ @inject(TYPES.Context)
+ context: Context,
@inject(TYPES.LocationResolver)
locationResolver: ILocationResolver,
@inject(TYPES.PublicTransportPlannerFactory)
publicTransportPlannerFactory: interfaces.Factory,
) {
+ this.context = context;
this.locationResolver = locationResolver;
this.publicTransportPlannerFactory = publicTransportPlannerFactory;
}
@@ -34,7 +41,12 @@ export default class QueryRunnerExponential implements IQueryRunner {
if (baseQuery.publicTransportOnly) {
const queryIterator = new ExponentialQueryIterator(baseQuery, 15 * 60 * 1000);
- const subqueryIterator = new SubqueryIterator(queryIterator, this.runSubquery.bind(this));
+ const emitQueryIterator = new Emiterator(queryIterator, this.context, EventType.QueryExponential);
+
+ const subqueryIterator = new SubqueryIterator(
+ emitQueryIterator,
+ this.runSubquery.bind(this),
+ );
return new FilterUniquePathsIterator(subqueryIterator);
diff --git a/src/util/ReduceIterator.ts b/src/util/ReduceIterator.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/util/iterators/Emiterator.ts b/src/util/iterators/Emiterator.ts
new file mode 100644
index 00000000..d7910073
--- /dev/null
+++ b/src/util/iterators/Emiterator.ts
@@ -0,0 +1,25 @@
+import { AsyncIterator, SimpleTransformIterator, SimpleTransformIteratorOptions } from "asynciterator";
+import Context from "../../Context";
+import EventType from "../../EventType";
+
+/**
+ * Lazily emits an event of specified type for each item that passes through source iterator
+ * This doesn't put the source iterator in flow mode
+ */
+export default class Emiterator extends SimpleTransformIterator {
+ constructor(source: AsyncIterator, context: Context, eventType: EventType) {
+ if (!context || !eventType) {
+ super(source);
+ }
+
+ const options: SimpleTransformIteratorOptions = {
+ map: (item: T) => {
+ context.emit(eventType, item);
+
+ return item;
+ },
+ };
+
+ super(source, options);
+ }
+}
diff --git a/src/util/MergeIterator.test.ts b/src/util/iterators/MergeIterator.test.ts
similarity index 100%
rename from src/util/MergeIterator.test.ts
rename to src/util/iterators/MergeIterator.test.ts
diff --git a/src/util/MergeIterator.ts b/src/util/iterators/MergeIterator.ts
similarity index 100%
rename from src/util/MergeIterator.ts
rename to src/util/iterators/MergeIterator.ts
From 30a06ae3c39467cab07a62fbf274914a3a228570 Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Thu, 20 Dec 2018 14:02:16 +0100
Subject: [PATCH 15/20] Fix tests
---
src/Context.ts | 68 +++++++++++++++++--
.../QueryRunnerExponential.test.ts | 5 +-
2 files changed, 67 insertions(+), 6 deletions(-)
diff --git a/src/Context.ts b/src/Context.ts
index 2c0fe5cd..00fb1d6e 100644
--- a/src/Context.ts
+++ b/src/Context.ts
@@ -1,12 +1,22 @@
-import { EventEmitter } from "events";
-import { Container, decorate, injectable } from "inversify";
-
-decorate(injectable(), EventEmitter);
+// @ts-ignore
+import { EventEmitter, Listener } from "events";
+import { Container, injectable } from "inversify";
+/**
+ * The Context serves as event pass through and holder of the inversify container object.
+ * Proxies an internal EventEmitter because ´decorate(injectable(), EventEmitter)´ causes
+ * errors when running tests in Jest
+ */
@injectable()
-export default class Context extends EventEmitter {
+// @ts-ignore
+export default class Context implements EventEmitter {
+ private emitter: EventEmitter;
private container: Container;
+ constructor() {
+ this.emitter = new EventEmitter();
+ }
+
public setContainer(container: Container) {
this.container = container;
}
@@ -14,4 +24,52 @@ export default class Context extends EventEmitter {
public getContainer() {
return this.container;
}
+
+ public addListener(type: string | symbol, listener: Listener): this {
+ this.emitter.addListener(type, listener);
+
+ return this;
+ }
+
+ public emit(type: string | symbol, ...args: any[]): boolean {
+ return this.emitter.emit(type, ...args);
+ }
+
+ public listenerCount(type: string | symbol): number {
+ return this.emitter.listenerCount(type);
+ }
+
+ public listeners(type: string | symbol): Listener[] {
+ return this.emitter.listeners(type);
+ }
+
+ public on(type: string | symbol, listener: Listener): this {
+ this.emitter.on(type, listener);
+
+ return this;
+ }
+
+ public once(type: string | symbol, listener: Listener): this {
+ this.emitter.once(type, listener);
+
+ return this;
+ }
+
+ public removeAllListeners(type?: string | symbol): this {
+ this.emitter.removeAllListeners(type);
+
+ return this;
+ }
+
+ public removeListener(type: string | symbol, listener: Listener): this {
+ this.emitter.removeListener(type, listener);
+
+ return this;
+ }
+
+ public setMaxListeners(n: number): this {
+ this.emitter.setMaxListeners(n);
+
+ return this;
+ }
}
diff --git a/src/query-runner/exponential/QueryRunnerExponential.test.ts b/src/query-runner/exponential/QueryRunnerExponential.test.ts
index bbbe9068..1e000033 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.test.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.test.ts
@@ -1,5 +1,6 @@
import "jest";
import LDFetch from "ldfetch";
+import Context from "../../Context";
import ConnectionsFetcherLazy from "../../fetcher/connections/ld-fetch/ConnectionsFetcherLazy";
import StopsFetcherLDFetch from "../../fetcher/stops/ld-fetch/StopsFetcherLDFetch";
import IPath from "../../interfaces/IPath";
@@ -38,6 +39,8 @@ describe("[QueryRunnerExponential]", () => {
const locationResolver = new LocationResolverDefault(stopsFetcher);
const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher);
+ const context = new Context();
+
const createJourneyExtractor = () => {
return new JourneyExtractorDefault(
locationResolver,
@@ -55,7 +58,7 @@ describe("[QueryRunnerExponential]", () => {
);
};
- return new QueryRunnerExponential(locationResolver, createPlanner);
+ return new QueryRunnerExponential(context, locationResolver, createPlanner);
};
const result: IPath[] = [];
From 02501d8a54f40659c73a629292628bb262360f4e Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Thu, 20 Dec 2018 14:25:33 +0100
Subject: [PATCH 16/20] Fix demo test
---
src/demo.ts | 71 +++++++++++++++++++++++++----------------------------
1 file changed, 33 insertions(+), 38 deletions(-)
diff --git a/src/demo.ts b/src/demo.ts
index d03d2bc2..1a9d5e84 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -8,32 +8,20 @@ export default async (logResults) => {
const planner = new Planner();
- // const roadOnlyResult = await planner.query({
- // roadOnly: true,
- // from: "http://irail.be/stations/NMBS/008896008", // Kortrijk
- // to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
- // });
-//
- // roadOnlyResult.each((path) => {
- // if (logResults) {
- // console.log(path);
- // }
- // });
-
- console.time("Public transport planner");
-
- planner
- .on(EventType.Query, (query) => {
- console.log("Query", query);
- })
- .on(EventType.QueryExponential, (query) => {
- const {minimumDepartureTime, maximumArrivalTime} = query;
-
- console.log("[Subquery]", minimumDepartureTime, maximumArrivalTime, maximumArrivalTime - minimumDepartureTime);
- })
- .on(EventType.LDFetchGet, (url, duration) => {
- console.log(`[GET] ${url} (${duration}ms)`);
- });
+ if (logResults) {
+ planner
+ .on(EventType.Query, (query) => {
+ console.log("Query", query);
+ })
+ .on(EventType.QueryExponential, (query) => {
+ const { minimumDepartureTime, maximumArrivalTime } = query;
+
+ console.log("[Subquery]", minimumDepartureTime, maximumArrivalTime, maximumArrivalTime - minimumDepartureTime);
+ })
+ .on(EventType.LDFetchGet, (url, duration) => {
+ console.log(`[GET] ${url} (${duration}ms)`);
+ });
+ }
const publicTransportResult = await planner.query({
publicTransportOnly: true,
@@ -49,17 +37,24 @@ export default async (logResults) => {
maximumTransferDuration: Units.fromHours(.01),
});
- console.timeEnd("Public transport planner");
-
- let i = 0;
-
- publicTransportResult.take(3).on("data", (path: IPath) => {
- console.log(++i);
- path.steps.forEach((step: IStep) => {
- console.log(JSON.stringify(step, null, " "));
- });
- console.log("\n");
+ return new Promise((resolve, reject) => {
+ let i = 0;
+
+ publicTransportResult.take(3)
+ .on("data", (path: IPath) => {
+ ++i;
+
+ if (logResults) {
+ console.log(++i);
+ path.steps.forEach((step: IStep) => {
+ console.log(JSON.stringify(step, null, " "));
+ });
+ console.log("\n");
+ }
+
+ if (i === 3) {
+ resolve(true);
+ }
+ });
});
-
- return true;
};
From f895fc5d8bdf71286694264f1d6dc9e1cba5d188 Mon Sep 17 00:00:00 2001
From: Simon Vanneste
Date: Thu, 20 Dec 2018 14:56:07 +0100
Subject: [PATCH 17/20] Combine catalogs method
---
src/Catalog.ts | 12 ++++++++++++
src/inversify.config.ts | 8 ++++++--
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/Catalog.ts b/src/Catalog.ts
index 4a18dee8..c02fdd6b 100644
--- a/src/Catalog.ts
+++ b/src/Catalog.ts
@@ -1,6 +1,18 @@
import TravelMode from "./TravelMode";
export default class Catalog {
+
+ public static combine(...catalogs: Catalog[]): Catalog {
+ const combinedCatalog = new Catalog();
+
+ for (const sourceCatalog of catalogs) {
+ combinedCatalog.stopsFetcherConfigs.push(...sourceCatalog.stopsFetcherConfigs);
+ combinedCatalog.connectionsFetcherConfigs.push(...sourceCatalog.connectionsFetcherConfigs);
+ }
+
+ return combinedCatalog;
+ }
+
public stopsFetcherConfigs = [];
public connectionsFetcherConfigs = [];
diff --git a/src/inversify.config.ts b/src/inversify.config.ts
index 8367d9e1..c087f876 100644
--- a/src/inversify.config.ts
+++ b/src/inversify.config.ts
@@ -1,7 +1,8 @@
import { Container, interfaces } from "inversify";
import LDFetch from "ldfetch";
import Catalog from "./Catalog";
-import catalog from "./catalog.nmbs";
+import catalogDeLijn from "./catalog.delijn";
+import catalogNmbs from "./catalog.nmbs";
import Context from "./Context";
import IConnectionsFetcher from "./fetcher/connections/IConnectionsFetcher";
import IConnectionsProvider from "./fetcher/connections/IConnectionsProvider";
@@ -82,7 +83,10 @@ container.bind>(TYPES.StopsFetcherFactory)
);
// Bind catalog
-container.bind(TYPES.Catalog).toConstantValue(catalog);
+container.bind(TYPES.Catalog).toConstantValue(catalogNmbs);
+
+// const combinedCatalog = Catalog.combine(catalogNmbs, catalogDeLijn);
+// container.bind(TYPES.Catalog).toConstantValue(combinedCatalog);
// Init LDFetch
const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } });
From 733d4cd1f6e80976dfd3a110d7dd336be9aca563 Mon Sep 17 00:00:00 2001
From: Maxim Martin
Date: Fri, 21 Dec 2018 10:02:39 +0100
Subject: [PATCH 18/20] 1. Abort event when query is impossible #53
---
src/EventType.ts | 2 +
src/Planner.ts | 8 +++-
src/demo.ts | 22 ++++++---
.../PublicTransportPlannerCSAProfile.ts | 45 +++++++++++++++----
.../exponential/FilterUniquePathsIterator.ts | 5 ++-
.../QueryRunnerExponential.test.ts | 23 +++-------
.../exponential/QueryRunnerExponential.ts | 10 ++++-
.../exponential/SubqueryIterator.ts | 2 +-
src/util/iterators/Emiterator.ts | 2 +
9 files changed, 83 insertions(+), 36 deletions(-)
diff --git a/src/EventType.ts b/src/EventType.ts
index 034c045b..17dcb13b 100644
--- a/src/EventType.ts
+++ b/src/EventType.ts
@@ -1,7 +1,9 @@
enum EventType {
Query = "query",
QueryExponential = "query-exponential",
+ QueryAbort = "query-abort",
LDFetchGet = "ldfetch-get",
+ ConnectionScan = "connection-scan",
}
export default EventType;
diff --git a/src/Planner.ts b/src/Planner.ts
index 65c87b28..ae3ea6ec 100644
--- a/src/Planner.ts
+++ b/src/Planner.ts
@@ -41,7 +41,13 @@ export default class Planner implements EventEmitter {
public async query(query: IQuery): Promise> {
this.emit(EventType.Query, query);
- return this.queryRunner.run(query);
+ const iterator = await this.queryRunner.run(query);
+
+ this.once(EventType.QueryAbort, () => {
+ iterator.close();
+ });
+
+ return iterator;
}
public addListener(type: string | symbol, listener: Listener): this {
diff --git a/src/demo.ts b/src/demo.ts
index 1a9d5e84..a742f2ea 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -9,6 +9,9 @@ export default async (logResults) => {
const planner = new Planner();
if (logResults) {
+ let scannedPages = 0;
+ let scannedConnections = 0;
+
planner
.on(EventType.Query, (query) => {
console.log("Query", query);
@@ -16,10 +19,16 @@ export default async (logResults) => {
.on(EventType.QueryExponential, (query) => {
const { minimumDepartureTime, maximumArrivalTime } = query;
+ console.log("Total scanned pages", scannedPages);
+ console.log("Total scanned connections", scannedConnections);
console.log("[Subquery]", minimumDepartureTime, maximumArrivalTime, maximumArrivalTime - minimumDepartureTime);
})
.on(EventType.LDFetchGet, (url, duration) => {
+ scannedPages++;
console.log(`[GET] ${url} (${duration}ms)`);
+ })
+ .on(EventType.ConnectionScan, (connection) => {
+ scannedConnections++;
});
}
@@ -31,8 +40,10 @@ export default async (logResults) => {
// to: "https://data.delijn.be/stops/502481", // Tielt Metaalconstructie Goossens
// from: "https://data.delijn.be/stops/509927", // Tield Rameplein perron 1
// to: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456
- from: "http://irail.be/stations/NMBS/008896008", // Kortrijk
+ from: "http://irail.be/stations/NMBS/008896925", // Ingelmunster
to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
+ /* from: {longitude: 3.707352, latitude: 51.011877},
+ to: {longitude: 3.707353, latitude: 51.011877},*/
minimumDepartureTime: new Date(),
maximumTransferDuration: Units.fromHours(.01),
});
@@ -45,16 +56,17 @@ export default async (logResults) => {
++i;
if (logResults) {
- console.log(++i);
- path.steps.forEach((step: IStep) => {
- console.log(JSON.stringify(step, null, " "));
- });
+ console.log(i);
+ console.log(JSON.stringify(path, null, " "));
console.log("\n");
}
if (i === 3) {
resolve(true);
}
+ })
+ .on("end", () => {
+ resolve(false);
});
});
};
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
index 542f260e..889fab7d 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
@@ -1,5 +1,7 @@
import { AsyncIterator } from "asynciterator";
import { inject, injectable, tagged } from "inversify";
+import Context from "../../Context";
+import EventType from "../../EventType";
import IConnection from "../../fetcher/connections/IConnection";
import IConnectionsProvider from "../../fetcher/connections/IConnectionsProvider";
import IStop from "../../fetcher/stops/IStop";
@@ -42,6 +44,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
private readonly finalReachableStopsFinder: IReachableStopsFinder;
private readonly transferReachableStopsFinder: IReachableStopsFinder;
private readonly journeyExtractor: IJourneyExtractor;
+ private readonly context: Context;
private profilesByStop: IProfilesByStop = {}; // S
private earliestArrivalByTrip: IEarliestArrivalByTrip = {}; // T
@@ -53,8 +56,10 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
private connectionsIterator: AsyncIterator;
constructor(
- @inject(TYPES.ConnectionsProvider) connectionsProvider: IConnectionsProvider,
- @inject(TYPES.LocationResolver) locationResolver: ILocationResolver,
+ @inject(TYPES.ConnectionsProvider)
+ connectionsProvider: IConnectionsProvider,
+ @inject(TYPES.LocationResolver)
+ locationResolver: ILocationResolver,
@inject(TYPES.ReachableStopsFinder)
@tagged("phase", ReachableStopsSearchPhase.Initial)
initialReachableStopsFinder: IReachableStopsFinder,
@@ -64,7 +69,10 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
@inject(TYPES.ReachableStopsFinder)
@tagged("phase", ReachableStopsSearchPhase.Final)
finalReachableStopsFinder: IReachableStopsFinder,
- @inject(TYPES.JourneyExtractor) journeyExtractor: IJourneyExtractor,
+ @inject(TYPES.JourneyExtractor)
+ journeyExtractor: IJourneyExtractor,
+ @inject(TYPES.Context)
+ context?: Context,
) {
this.connectionsProvider = connectionsProvider;
this.locationResolver = locationResolver;
@@ -72,6 +80,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
this.transferReachableStopsFinder = transferReachableStopsFinder;
this.finalReachableStopsFinder = finalReachableStopsFinder;
this.journeyExtractor = journeyExtractor;
+ this.context = context;
}
public async plan(query: IResolvedQuery): Promise> {
@@ -117,18 +126,18 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
return new Promise((resolve, reject) => {
- const done = () => {
+ const done = () => {
self.journeyExtractor.extractJourneys(self.profilesByStop, self.query)
.then((resultIterator) => {
resolve(resultIterator);
});
};
- this.connectionsIterator.on("readable", () =>
+ this.connectionsIterator.on("readable", () =>
self.processNextConnection(done),
);
- this.connectionsIterator.on("end", () => done());
+ this.connectionsIterator.on("end", () => done());
}) as Promise>;
}
@@ -148,6 +157,10 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
return;
}
+ if (this.context) {
+ this.context.emit(EventType.ConnectionScan, connection);
+ }
+
this.discoverConnection(connection);
const earliestArrivalTime = this.calculateEarliestArrivalTime(connection);
@@ -214,7 +227,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
return Vectors.minVector((c) => c.arrivalTime, walkToTargetTime, remainSeatedTime, takeTransferTime);
}
- private async initDurationToTargetByStop(): Promise {
+ private async initDurationToTargetByStop(): Promise {
const arrivalStop: IStop = this.query.to[0] as IStop;
const reachableStops = await this.finalReachableStopsFinder
@@ -225,12 +238,20 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
this.query.minimumWalkingSpeed,
);
+ if (reachableStops.length === 0 && this.context) {
+ this.context.emit(EventType.QueryAbort);
+
+ return false;
+ }
+
for (const reachableStop of reachableStops) {
this.durationToTargetByStop[reachableStop.stop.id] = reachableStop.duration;
}
+
+ return true;
}
- private async initInitialReachableStops(): Promise {
+ private async initInitialReachableStops(): Promise {
const fromLocation: IStop = this.query.from[0] as IStop;
this.initialReachableStops = await this.initialReachableStopsFinder.findReachableStops(
@@ -239,6 +260,14 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
this.query.maximumTransferDuration,
this.query.minimumWalkingSpeed,
);
+
+ if (this.initialReachableStops.length === 0 && this.context) {
+ this.context.emit(EventType.QueryAbort);
+
+ return false;
+ }
+
+ return true;
}
private walkToTarget(connection: IConnection): IArrivalTimeByTransfers {
diff --git a/src/query-runner/exponential/FilterUniquePathsIterator.ts b/src/query-runner/exponential/FilterUniquePathsIterator.ts
index 6478b97d..bec79048 100644
--- a/src/query-runner/exponential/FilterUniquePathsIterator.ts
+++ b/src/query-runner/exponential/FilterUniquePathsIterator.ts
@@ -7,7 +7,10 @@ export default class FilterUniquePathsIterator extends SimpleTransformIterator) {
- super(source);
+ super(source, {
+ maxBufferSize: 1,
+ autoStart: false,
+ });
this.store = [];
}
diff --git a/src/query-runner/exponential/QueryRunnerExponential.test.ts b/src/query-runner/exponential/QueryRunnerExponential.test.ts
index 1e000033..06985aef 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.test.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.test.ts
@@ -69,26 +69,13 @@ describe("[QueryRunnerExponential]", () => {
publicTransportResult = await queryRunner.run(query);
- let i = 0;
- publicTransportResult.on("readable", () => {
- let path = publicTransportResult.read();
- if (path) {
+ await publicTransportResult.take(3)
+ .on("data", (path: IPath) => {
result.push(path);
- }
-
- while (path && i < 10) {
- i++;
- path = publicTransportResult.read();
-
- if (path) {
- result.push(path);
- }
- }
-
- if (i === 10) {
+ })
+ .on("end", () => {
done();
- }
- });
+ });
});
it("Correct departure and arrival stop", () => {
diff --git a/src/query-runner/exponential/QueryRunnerExponential.ts b/src/query-runner/exponential/QueryRunnerExponential.ts
index f7c7c70a..68c70678 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.ts
@@ -41,10 +41,14 @@ export default class QueryRunnerExponential implements IQueryRunner {
if (baseQuery.publicTransportOnly) {
const queryIterator = new ExponentialQueryIterator(baseQuery, 15 * 60 * 1000);
- const emitQueryIterator = new Emiterator(queryIterator, this.context, EventType.QueryExponential);
+ // const emitQueryIterator = new Emiterator(
+ // queryIterator,
+ // this.context,
+ // EventType.QueryExponential,
+ // );
const subqueryIterator = new SubqueryIterator(
- emitQueryIterator,
+ queryIterator,
this.runSubquery.bind(this),
);
@@ -57,6 +61,8 @@ export default class QueryRunnerExponential implements IQueryRunner {
private async runSubquery(query: IResolvedQuery): Promise> {
// TODO investigate if publicTransportPlanner can be reused or reuse some of its aggregated data
+ this.context.emit(EventType.QueryExponential, query);
+
const planner = this.publicTransportPlannerFactory() as IPublicTransportPlanner;
return planner.plan(query);
diff --git a/src/query-runner/exponential/SubqueryIterator.ts b/src/query-runner/exponential/SubqueryIterator.ts
index 10485a6e..e749e5e3 100644
--- a/src/query-runner/exponential/SubqueryIterator.ts
+++ b/src/query-runner/exponential/SubqueryIterator.ts
@@ -53,7 +53,7 @@ export default class SubqueryIterator extends BufferedIterator {
}
// Iterator was empty
- if (self.currentResultPushed === 0) {
+ if (self.currentResultPushed === 0 && !this.closed) {
self._read(null, done);
}
});
diff --git a/src/util/iterators/Emiterator.ts b/src/util/iterators/Emiterator.ts
index d7910073..3b54f8a3 100644
--- a/src/util/iterators/Emiterator.ts
+++ b/src/util/iterators/Emiterator.ts
@@ -18,6 +18,8 @@ export default class Emiterator extends SimpleTransformIterator {
return item;
},
+ maxBufferSize: 1,
+ autoStart: false,
};
super(source, options);
From db68853af237a1d6b6420e9ff793eeebf8ae106f Mon Sep 17 00:00:00 2001
From: Maxim Martin
Date: Fri, 21 Dec 2018 13:45:18 +0100
Subject: [PATCH 19/20] 1. Added maximumWalkingDuration and
maximumWalkingDistance #42 2. close iterators.
---
src/Defaults.ts | 3 +-
src/EventType.ts | 3 +
src/demo.ts | 5 +-
.../ld-fetch/ConnectionsIteratorLazy.ts | 24 +++----
src/interfaces/IQuery.ts | 4 +-
.../public-transport/CSA/util/ProfileUtil.ts | 25 +++++---
.../PublicTransportPlannerCSAProfile.ts | 64 ++++++++++++++-----
src/query-runner/IResolvedQuery.ts | 1 +
src/query-runner/QueryRunnerDefault.ts | 4 ++
.../exponential/QueryRunnerExponential.ts | 4 ++
src/util/iterators/MergeIterator.ts | 8 +++
11 files changed, 103 insertions(+), 42 deletions(-)
diff --git a/src/Defaults.ts b/src/Defaults.ts
index 58ef7c2e..2be8ecc1 100644
--- a/src/Defaults.ts
+++ b/src/Defaults.ts
@@ -3,7 +3,8 @@ import Units from "./util/Units";
export default class Defaults {
public static readonly defaultMinimumWalkingSpeed = 3;
public static readonly defaultMaximumWalkingSpeed = 6;
+ public static readonly defaultWalkingDuration = Units.fromSeconds(10 * 60);
public static readonly defaultMinimumTransferDuration = Units.fromSeconds(60);
public static readonly defaultMaximumTransferDuration = Units.fromHours(.4);
- public static readonly defaultMaximumTransfers = 8;
+ public static readonly defaultMaximumTransfers = 4;
}
diff --git a/src/EventType.ts b/src/EventType.ts
index 17dcb13b..d7cfe1a7 100644
--- a/src/EventType.ts
+++ b/src/EventType.ts
@@ -4,6 +4,9 @@ enum EventType {
QueryAbort = "query-abort",
LDFetchGet = "ldfetch-get",
ConnectionScan = "connection-scan",
+ FinalReachableStops = "final-reachable-stops",
+ InitialReachableStops = "initial-reachable-stops",
+ AddedNewTransferProfile = "added-new-transfer-profile",
}
export default EventType;
diff --git a/src/demo.ts b/src/demo.ts
index a742f2ea..240f8605 100644
--- a/src/demo.ts
+++ b/src/demo.ts
@@ -1,7 +1,6 @@
import EventType from "./EventType";
import Planner from "./index";
import IPath from "./interfaces/IPath";
-import IStep from "./interfaces/IStep";
import Units from "./util/Units";
export default async (logResults) => {
@@ -42,10 +41,8 @@ export default async (logResults) => {
// to: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456
from: "http://irail.be/stations/NMBS/008896925", // Ingelmunster
to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters
- /* from: {longitude: 3.707352, latitude: 51.011877},
- to: {longitude: 3.707353, latitude: 51.011877},*/
minimumDepartureTime: new Date(),
- maximumTransferDuration: Units.fromHours(.01),
+ maximumTransferDuration: Units.fromHours(0.5),
});
return new Promise((resolve, reject) => {
diff --git a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLazy.ts b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLazy.ts
index d1760dd0..5308d950 100644
--- a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLazy.ts
+++ b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLazy.ts
@@ -85,22 +85,22 @@ export default class ConnectionsIteratorLazy extends BufferedIterator= 0) {
- this._push(connections[c]);
- c--;
- }
+ while (c >= 0) {
+ this._push(connections[c]);
+ c--;
+ }
- // Forwards
- } else {
- for (const connection of connections) {
- this._push(connection);
- }
+ // Forwards
+ } else {
+ for (const connection of connections) {
+ this._push(connection);
}
}
+ }
}
diff --git a/src/interfaces/IQuery.ts b/src/interfaces/IQuery.ts
index 48612005..1142c149 100644
--- a/src/interfaces/IQuery.ts
+++ b/src/interfaces/IQuery.ts
@@ -1,5 +1,5 @@
import ILocation from "./ILocation";
-import { DurationMs, SpeedkmH } from "./units";
+import { DistanceM, DurationMs, SpeedkmH } from "./units";
export default interface IQuery {
from?: string | string[] | ILocation | ILocation[];
@@ -11,6 +11,8 @@ export default interface IQuery {
walkingSpeed?: SpeedkmH;
minimumWalkingSpeed?: SpeedkmH;
maximumWalkingSpeed?: SpeedkmH;
+ maximumWalkingDuration?: DurationMs;
+ maximumWalkingDistance?: DistanceM;
minimumTransferDuration?: DurationMs;
maximumTransferDuration?: DurationMs;
maximumTransfers?: number;
diff --git a/src/planner/public-transport/CSA/util/ProfileUtil.ts b/src/planner/public-transport/CSA/util/ProfileUtil.ts
index 94c51828..f8317df9 100644
--- a/src/planner/public-transport/CSA/util/ProfileUtil.ts
+++ b/src/planner/public-transport/CSA/util/ProfileUtil.ts
@@ -1,4 +1,6 @@
+import DropOffType from "../../../../fetcher/connections/DropOffType";
import IConnection from "../../../../fetcher/connections/IConnection";
+import { DurationMs } from "../../../../interfaces/units";
import IArrivalTimeByTransfers from "../data-structure/IArrivalTimeByTransfers";
import IProfilesByStop from "../data-structure/stops/IProfilesByStop";
@@ -23,21 +25,28 @@ export default class ProfileUtil {
public static getTransferTimes(
profilesByStop: IProfilesByStop,
connection: IConnection,
- maxLegs,
- minimumTransferDuration,
+ maxLegs: number,
+ minimumTransferDuration: DurationMs,
+ maximumTransferDuration: DurationMs,
): IArrivalTimeByTransfers {
const { arrivalStop, arrivalTime } = connection;
const trip = connection["gtfs:trip"];
- if (connection["gtfs:dropOffType"] !== "gtfs:NotAvailable") {
+ if (connection["gtfs:dropOffType"] !== DropOffType.NotAvailable) {
- let i = profilesByStop[arrivalStop].length - 1;
- while (i >= 0) {
- if (profilesByStop[arrivalStop][i].departureTime >= arrivalTime.getTime() + minimumTransferDuration) {
- const arrivalTimeByTransfers = profilesByStop[arrivalStop][i].getArrivalTimeByTransfers(trip);
+ let profileIndex = profilesByStop[arrivalStop].length - 1;
+ while (profileIndex >= 0) {
+
+ const departure: number = profilesByStop[arrivalStop][profileIndex].departureTime;
+ const arrival: number = arrivalTime.getTime();
+
+ const transferDuration = departure - arrival;
+
+ if (transferDuration >= minimumTransferDuration && transferDuration <= maximumTransferDuration) {
+ const arrivalTimeByTransfers = profilesByStop[arrivalStop][profileIndex].getArrivalTimeByTransfers(trip);
return arrivalTimeByTransfers.slice() as IArrivalTimeByTransfers;
}
- i--;
+ profileIndex--;
}
}
diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
index 889fab7d..e8c7db71 100644
--- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
+++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts
@@ -2,8 +2,10 @@ import { AsyncIterator } from "asynciterator";
import { inject, injectable, tagged } from "inversify";
import Context from "../../Context";
import EventType from "../../EventType";
+import DropOffType from "../../fetcher/connections/DropOffType";
import IConnection from "../../fetcher/connections/IConnection";
import IConnectionsProvider from "../../fetcher/connections/IConnectionsProvider";
+import PickupType from "../../fetcher/connections/PickupType";
import IStop from "../../fetcher/stops/IStop";
import ILocation from "../../interfaces/ILocation";
import IPath from "../../interfaces/IPath";
@@ -125,19 +127,24 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
const self = this;
return new Promise((resolve, reject) => {
-
- const done = () => {
- self.journeyExtractor.extractJourneys(self.profilesByStop, self.query)
- .then((resultIterator) => {
- resolve(resultIterator);
- });
+ let isDone = false;
+ const done = () => {
+ if (!isDone) {
+ self.connectionsIterator.close();
+
+ self.journeyExtractor.extractJourneys(self.profilesByStop, self.query)
+ .then((resultIterator) => {
+ resolve(resultIterator);
+ });
+ isDone = true;
+ }
};
- this.connectionsIterator.on("readable", () =>
+ this.connectionsIterator.on("readable", () =>
self.processNextConnection(done),
);
- this.connectionsIterator.on("end", () => done());
+ this.connectionsIterator.on("end", () => done());
}) as Promise>;
}
@@ -234,7 +241,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
.findReachableStops(
arrivalStop,
ReachableStopsFinderMode.Target,
- this.query.maximumTransferDuration,
+ this.query.maximumWalkingDuration,
this.query.minimumWalkingSpeed,
);
@@ -244,6 +251,10 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
return false;
}
+ if (this.context) {
+ this.context.emit(EventType.FinalReachableStops, reachableStops);
+ }
+
for (const reachableStop of reachableStops) {
this.durationToTargetByStop[reachableStop.stop.id] = reachableStop.duration;
}
@@ -257,7 +268,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
this.initialReachableStops = await this.initialReachableStopsFinder.findReachableStops(
fromLocation,
ReachableStopsFinderMode.Source,
- this.query.maximumTransferDuration,
+ this.query.maximumWalkingDuration,
this.query.minimumWalkingSpeed,
);
@@ -267,6 +278,10 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
return false;
}
+ if (this.context) {
+ this.context.emit(EventType.InitialReachableStops, this.initialReachableStops);
+ }
+
return true;
}
@@ -321,7 +336,8 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
connection,
this.query.maximumTransfers,
this.query.minimumTransferDuration,
- );
+ this.query.maximumTransferDuration,
+ );
return Vectors.shiftVector(
transferTimes,
@@ -374,7 +390,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
const initialReachableStop: IReachableStop = this.initialReachableStops.find(
(reachable: IReachableStop) =>
- reachable.stop.id === connection.departureStop,
+ reachable.stop.id === connection.departureStop,
);
if (initialReachableStop) {
@@ -390,7 +406,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
const reachableStops: IReachableStop[] = await this.transferReachableStopsFinder.findReachableStops(
departureStop as IStop,
ReachableStopsFinderMode.Source,
- this.query.maximumTransferDuration,
+ this.query.maximumWalkingDuration,
this.query.minimumWalkingSpeed,
);
@@ -406,12 +422,23 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
});
}
+ private async emitTransferProfile(transferProfile: ITransferProfile, amountOfTransfers: number): Promise {
+ const departureStop = await this.locationResolver.resolve(transferProfile.enterConnection.departureStop);
+ const arrivalStop = await this.locationResolver.resolve(transferProfile.exitConnection.arrivalStop);
+
+ this.context.emit(EventType.AddedNewTransferProfile, {
+ departureStop,
+ arrivalStop,
+ amountOfTransfers,
+ });
+ }
+
private incorporateInProfile(
connection: IConnection,
duration: DurationMs,
stop: IStop,
arrivalTimeByTransfers: IArrivalTimeByTransfers,
- ) {
+ ): void {
const departureTime = connection.departureTime.getTime() - duration;
if (departureTime < this.query.minimumDepartureTime.getTime()) {
@@ -447,12 +474,17 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor
if (
arrivalTimeByTransfers[amountOfTransfers].arrivalTime < transferProfile.arrivalTime &&
- connection["gtfs:pickupType"] !== "gtfs:NotAvailable" &&
- possibleExitConnection["gtfs:dropOfType"] !== "gtfs:NotAvailable"
+ connection["gtfs:pickupType"] !== PickupType.NotAvailable &&
+ possibleExitConnection["gtfs:dropOfType"] !== DropOffType.NotAvailable
) {
newTransferProfile.enterConnection = connection;
newTransferProfile.exitConnection = possibleExitConnection;
newTransferProfile.departureTime = departureTime;
+
+ if (this.context && this.context.listenerCount(EventType.AddedNewTransferProfile) > 0) {
+ this.emitTransferProfile(newTransferProfile, amountOfTransfers);
+ }
+
} else {
newTransferProfile.enterConnection = transferProfile.enterConnection;
newTransferProfile.exitConnection = transferProfile.exitConnection;
diff --git a/src/query-runner/IResolvedQuery.ts b/src/query-runner/IResolvedQuery.ts
index 2d8ed37b..210fe29f 100644
--- a/src/query-runner/IResolvedQuery.ts
+++ b/src/query-runner/IResolvedQuery.ts
@@ -10,6 +10,7 @@ export default interface IResolvedQuery {
publicTransportOnly?: boolean;
minimumWalkingSpeed?: SpeedkmH;
maximumWalkingSpeed?: SpeedkmH;
+ maximumWalkingDuration?: DurationMs;
minimumTransferDuration?: DurationMs;
maximumTransferDuration?: DurationMs;
maximumTransfers?: number;
diff --git a/src/query-runner/QueryRunnerDefault.ts b/src/query-runner/QueryRunnerDefault.ts
index 3703bf00..d78e60cf 100644
--- a/src/query-runner/QueryRunnerDefault.ts
+++ b/src/query-runner/QueryRunnerDefault.ts
@@ -6,6 +6,7 @@ import IPath from "../interfaces/IPath";
import IQuery from "../interfaces/IQuery";
import IPublicTransportPlanner from "../planner/public-transport/IPublicTransportPlanner";
import TYPES from "../types";
+import Units from "../util/Units";
import ILocationResolver from "./ILocationResolver";
import IQueryRunner from "./IQueryRunner";
import IResolvedQuery from "./IResolvedQuery";
@@ -54,6 +55,7 @@ export default class QueryRunnerDefault implements IQueryRunner {
const {
from, to,
minimumWalkingSpeed, maximumWalkingSpeed, walkingSpeed,
+ maximumWalkingDuration, maximumWalkingDistance,
minimumTransferDuration, maximumTransferDuration, maximumTransfers,
minimumDepartureTime, maximumArrivalTime,
...other
@@ -78,6 +80,8 @@ export default class QueryRunnerDefault implements IQueryRunner {
resolvedQuery.to = await this.resolveEndpoint(to);
resolvedQuery.minimumWalkingSpeed = minimumWalkingSpeed || walkingSpeed || Defaults.defaultMinimumWalkingSpeed;
resolvedQuery.maximumWalkingSpeed = maximumWalkingSpeed || walkingSpeed || Defaults.defaultMaximumWalkingSpeed;
+ resolvedQuery.maximumWalkingDuration = maximumWalkingDuration ||
+ Units.toDuration(maximumWalkingDistance, resolvedQuery.minimumWalkingSpeed) || Defaults.defaultWalkingDuration;
resolvedQuery.minimumTransferDuration = minimumTransferDuration || Defaults.defaultMinimumTransferDuration;
resolvedQuery.maximumTransferDuration = maximumTransferDuration || Defaults.defaultMaximumTransferDuration;
diff --git a/src/query-runner/exponential/QueryRunnerExponential.ts b/src/query-runner/exponential/QueryRunnerExponential.ts
index 68c70678..cde5f40b 100644
--- a/src/query-runner/exponential/QueryRunnerExponential.ts
+++ b/src/query-runner/exponential/QueryRunnerExponential.ts
@@ -9,6 +9,7 @@ import IQuery from "../../interfaces/IQuery";
import IPublicTransportPlanner from "../../planner/public-transport/IPublicTransportPlanner";
import TYPES from "../../types";
import Emiterator from "../../util/iterators/Emiterator";
+import Units from "../../util/Units";
import ILocationResolver from "../ILocationResolver";
import IQueryRunner from "../IQueryRunner";
import IResolvedQuery from "../IResolvedQuery";
@@ -88,6 +89,7 @@ export default class QueryRunnerExponential implements IQueryRunner {
const {
from, to,
minimumWalkingSpeed, maximumWalkingSpeed, walkingSpeed,
+ maximumWalkingDuration, maximumWalkingDistance,
minimumTransferDuration, maximumTransferDuration, maximumTransfers,
minimumDepartureTime,
...other
@@ -102,6 +104,8 @@ export default class QueryRunnerExponential implements IQueryRunner {
resolvedQuery.to = await this.resolveEndpoint(to);
resolvedQuery.minimumWalkingSpeed = minimumWalkingSpeed || walkingSpeed || Defaults.defaultMinimumWalkingSpeed;
resolvedQuery.maximumWalkingSpeed = maximumWalkingSpeed || walkingSpeed || Defaults.defaultMaximumWalkingSpeed;
+ resolvedQuery.maximumWalkingDuration = maximumWalkingDuration ||
+ Units.toDuration(maximumWalkingDistance, resolvedQuery.minimumWalkingSpeed) || Defaults.defaultWalkingDuration;
resolvedQuery.minimumTransferDuration = minimumTransferDuration || Defaults.defaultMinimumTransferDuration;
resolvedQuery.maximumTransferDuration = maximumTransferDuration || Defaults.defaultMaximumTransferDuration;
diff --git a/src/util/iterators/MergeIterator.ts b/src/util/iterators/MergeIterator.ts
index 169bf868..384ff3a7 100644
--- a/src/util/iterators/MergeIterator.ts
+++ b/src/util/iterators/MergeIterator.ts
@@ -40,6 +40,14 @@ export default class MergeIterator extends BufferedIterator {
});
}
+ public close() {
+ for (const iterator of this.sourceIterators) {
+ iterator.close();
+ }
+
+ super.close();
+ }
+
private fillFirstValues(done) {
this.values = Array(this.sourceIterators.length).fill(undefined);
let filledValues = 0;
From fe167a1ae507df31bd4d1ded6fd19eaa8991b221 Mon Sep 17 00:00:00 2001
From: Maxim Martin
Date: Fri, 21 Dec 2018 15:52:02 +0100
Subject: [PATCH 20/20] Add map demo to CodePen in docs #47
---
docs/index.html | 14 ++++++++++++++
src/Planner.ts | 12 ++++++++++++
2 files changed, 26 insertions(+)
diff --git a/docs/index.html b/docs/index.html
index 8ff8f211..061f60d4 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -27,6 +27,7 @@ Planner.js