From 5660980bc6c192eecff6a18318d46bedcd8b8ecc Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Mon, 5 Nov 2018 12:33:28 +0100 Subject: [PATCH 01/97] Add IStopsFetcherMediator that combines IStopsFetchers Add Geo helper class --- src/fetcher/stops/IStopsFetcher.ts | 5 +-- src/fetcher/stops/IStopsFetcherMediator.ts | 6 +++ .../stops/{ => json}/StopsFetcherNMBSJSON.ts | 14 ++++-- .../{ => ld-fetch}/StopsFetcherDeLijn.test.ts | 4 +- .../{ => ld-fetch}/StopsFetcherDeLijn.ts | 2 +- .../{ => ld-fetch}/StopsFetcherLDFetch.ts | 14 ++++-- .../{ => ld-fetch}/StopsFetcherNMBS.test.ts | 4 +- .../stops/{ => ld-fetch}/StopsFetcherNMBS.ts | 0 src/fetcher/stops/proxy/StopsFetcherProxy.ts | 44 +++++++++++++++++++ src/inversify.config.ts | 7 ++- src/planner/road/RoadPlannerBirdsEye.test.ts | 22 ++-------- src/planner/road/RoadPlannerBirdsEye.ts | 22 +--------- .../LocationResolverDefault.test.ts | 18 +------- src/query-runner/LocationResolverDefault.ts | 42 +++++------------- src/types.ts | 1 + src/util/Geo.ts | 22 ++++++++++ 16 files changed, 128 insertions(+), 99 deletions(-) create mode 100644 src/fetcher/stops/IStopsFetcherMediator.ts rename src/fetcher/stops/{ => json}/StopsFetcherNMBSJSON.ts (78%) rename src/fetcher/stops/{ => ld-fetch}/StopsFetcherDeLijn.test.ts (88%) rename src/fetcher/stops/{ => ld-fetch}/StopsFetcherDeLijn.ts (93%) rename src/fetcher/stops/{ => ld-fetch}/StopsFetcherLDFetch.ts (88%) rename src/fetcher/stops/{ => ld-fetch}/StopsFetcherNMBS.test.ts (82%) rename src/fetcher/stops/{ => ld-fetch}/StopsFetcherNMBS.ts (100%) create mode 100644 src/fetcher/stops/proxy/StopsFetcherProxy.ts create mode 100644 src/util/Geo.ts diff --git a/src/fetcher/stops/IStopsFetcher.ts b/src/fetcher/stops/IStopsFetcher.ts index f8ac2fe1..2cf6b351 100644 --- a/src/fetcher/stops/IStopsFetcher.ts +++ b/src/fetcher/stops/IStopsFetcher.ts @@ -1,6 +1,5 @@ -import IStop from "./IStop"; +import IStopsFetcherMediator from "./IStopsFetcherMediator"; -export default interface IStopsFetcher { +export default interface IStopsFetcher extends IStopsFetcherMediator { prefix: string; - getStopById: (stopId: string | string[]) => Promise; } diff --git a/src/fetcher/stops/IStopsFetcherMediator.ts b/src/fetcher/stops/IStopsFetcherMediator.ts new file mode 100644 index 00000000..d026f97b --- /dev/null +++ b/src/fetcher/stops/IStopsFetcherMediator.ts @@ -0,0 +1,6 @@ +import IStop from "./IStop"; + +export default interface IStopsFetcherMediator { + getStopById: (stopId: string) => Promise; + getAllStops: () => Promise; +} diff --git a/src/fetcher/stops/StopsFetcherNMBSJSON.ts b/src/fetcher/stops/json/StopsFetcherNMBSJSON.ts similarity index 78% rename from src/fetcher/stops/StopsFetcherNMBSJSON.ts rename to src/fetcher/stops/json/StopsFetcherNMBSJSON.ts index ba6a6dce..b31453f8 100644 --- a/src/fetcher/stops/StopsFetcherNMBSJSON.ts +++ b/src/fetcher/stops/json/StopsFetcherNMBSJSON.ts @@ -1,6 +1,6 @@ import { injectable } from "inversify"; -import IStop from "./IStop"; -import IStopsFetcher from "./IStopsFetcher"; +import IStop from "../IStop"; +import IStopsFetcher from "../IStopsFetcher"; const IRAIL_STATIONS_URL = "https://api.irail.be/stations/?format=json"; @@ -28,10 +28,18 @@ export default class StopsFetcherNMBSJSON implements IStopsFetcher { return this.stops[stopId]; } + public async getAllStops(): Promise { + if (this.loadPromise) { + await this.loadPromise; + } + + return Object.values(this.stops); + } + private loadStops() { this.loadPromise = fetch(IRAIL_STATIONS_URL) .then((response: Response) => response.json()) - .then(({station: stations}) => { + .then(({ station: stations }) => { this.stops = stations.reduce((accu: IStopMap, stop) => { stop.latitude = parseFloat(stop.locationY); diff --git a/src/fetcher/stops/StopsFetcherDeLijn.test.ts b/src/fetcher/stops/ld-fetch/StopsFetcherDeLijn.test.ts similarity index 88% rename from src/fetcher/stops/StopsFetcherDeLijn.test.ts rename to src/fetcher/stops/ld-fetch/StopsFetcherDeLijn.test.ts index a0a5486d..d3b087a2 100644 --- a/src/fetcher/stops/StopsFetcherDeLijn.test.ts +++ b/src/fetcher/stops/ld-fetch/StopsFetcherDeLijn.test.ts @@ -1,6 +1,6 @@ import "jest"; -import IStop from "./IStop"; -import IStopsFetcher from "./IStopsFetcher"; +import IStop from "../IStop"; +import IStopsFetcher from "../IStopsFetcher"; import StopsFetcherDeLijn from "./StopsFetcherDeLijn"; const fetcher: IStopsFetcher = new StopsFetcherDeLijn(); diff --git a/src/fetcher/stops/StopsFetcherDeLijn.ts b/src/fetcher/stops/ld-fetch/StopsFetcherDeLijn.ts similarity index 93% rename from src/fetcher/stops/StopsFetcherDeLijn.ts rename to src/fetcher/stops/ld-fetch/StopsFetcherDeLijn.ts index 932f69fb..1ea9bc7b 100644 --- a/src/fetcher/stops/StopsFetcherDeLijn.ts +++ b/src/fetcher/stops/ld-fetch/StopsFetcherDeLijn.ts @@ -1,5 +1,5 @@ import { injectable } from "inversify"; -import IStopsFetcher from "./IStopsFetcher"; +import IStopsFetcher from "../IStopsFetcher"; import StopsFetcherLDFetch from "./StopsFetcherLDFetch"; const STOPS_URLS = [ diff --git a/src/fetcher/stops/StopsFetcherLDFetch.ts b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts similarity index 88% rename from src/fetcher/stops/StopsFetcherLDFetch.ts rename to src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts index bd727c95..52cf4feb 100644 --- a/src/fetcher/stops/StopsFetcherLDFetch.ts +++ b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts @@ -1,9 +1,9 @@ import { injectable, unmanaged } from "inversify"; import LDFetch from "ldfetch"; import { Triple } from "rdf-js"; -import { transformPredicate } from "../helpers"; -import IStop from "./IStop"; -import IStopsFetcher from "./IStopsFetcher"; +import { transformPredicate } from "../../helpers"; +import IStop from "../IStop"; +import IStopsFetcher from "../IStopsFetcher"; interface IPartialStopMap { [stopId: string]: Partial; @@ -38,6 +38,14 @@ export default class StopsFetcherLDFetch implements IStopsFetcher { return this.stops[stopId]; } + public async getAllStops(): Promise { + if (this.loadPromise) { + await this.loadPromise; + } + + return Object.values(this.stops); + } + private loadStops() { if (this.sources) { diff --git a/src/fetcher/stops/StopsFetcherNMBS.test.ts b/src/fetcher/stops/ld-fetch/StopsFetcherNMBS.test.ts similarity index 82% rename from src/fetcher/stops/StopsFetcherNMBS.test.ts rename to src/fetcher/stops/ld-fetch/StopsFetcherNMBS.test.ts index c889cf63..e46eebd3 100644 --- a/src/fetcher/stops/StopsFetcherNMBS.test.ts +++ b/src/fetcher/stops/ld-fetch/StopsFetcherNMBS.test.ts @@ -1,6 +1,6 @@ import "jest"; -import IStop from "./IStop"; -import IStopsFetcher from "./IStopsFetcher"; +import IStop from "../IStop"; +import IStopsFetcher from "../IStopsFetcher"; import StopsFetcherNMBS from "./StopsFetcherNMBS"; test("[StopsFetcherNMBS]", async () => { diff --git a/src/fetcher/stops/StopsFetcherNMBS.ts b/src/fetcher/stops/ld-fetch/StopsFetcherNMBS.ts similarity index 100% rename from src/fetcher/stops/StopsFetcherNMBS.ts rename to src/fetcher/stops/ld-fetch/StopsFetcherNMBS.ts diff --git a/src/fetcher/stops/proxy/StopsFetcherProxy.ts b/src/fetcher/stops/proxy/StopsFetcherProxy.ts new file mode 100644 index 00000000..39cad306 --- /dev/null +++ b/src/fetcher/stops/proxy/StopsFetcherProxy.ts @@ -0,0 +1,44 @@ +import { multiInject } from "inversify"; +import TYPES from "../../../types"; +import IStop from "../IStop"; +import IStopsFetcher from "../IStopsFetcher"; +import IStopsFetcherMediator from "../IStopsFetcherMediator"; + +export default class StopsFetcherProxy implements IStopsFetcherMediator { + + private readonly stopsFetchers: IStopsFetcher[]; + + constructor( + @multiInject(TYPES.StopsFetcher) stopsFetchers: IStopsFetcher[], + ) { + this.stopsFetchers = stopsFetchers; + } + + public async getStopById(stopId: string): Promise { + const fetcher = this.determineStopFetcher(stopId); + + if (fetcher) { + return fetcher.getStopById(stopId); + } + } + + public async getAllStops(): Promise { + return Promise.all(this.stopsFetchers + .map((stopsFetcher: IStopsFetcher) => + stopsFetcher.getAllStops(), + ), + ).then((results: IStop[][]) => results.flat()); + } + + private determineStopFetcher(stopId: string): IStopsFetcher { + if (!this.stopsFetchers || !this.stopsFetchers.length) { + return null; + } + + for (const fetcher of this.stopsFetchers) { + if (stopId.indexOf(fetcher.prefix) === 0) { + return fetcher; + } + } + } +} diff --git a/src/inversify.config.ts b/src/inversify.config.ts index fffd8096..eb063108 100644 --- a/src/inversify.config.ts +++ b/src/inversify.config.ts @@ -5,7 +5,10 @@ import ConnectionsFetcherDeLijn from "./fetcher/connections/ld-fetch/Connections import ConnectionsFetcherNMBS from "./fetcher/connections/ld-fetch/ConnectionsFetcherNMBS"; import ConnectionsFetcherMerge from "./fetcher/connections/merge/ConnectionsFetcherMerge"; import IStopsFetcher from "./fetcher/stops/IStopsFetcher"; -import StopsFetcherNMBS from "./fetcher/stops/StopsFetcherNMBS"; +import IStopsFetcherMediator from "./fetcher/stops/IStopsFetcherMediator"; +import StopsFetcherDeLijn from "./fetcher/stops/ld-fetch/StopsFetcherDeLijn"; +import StopsFetcherNMBS from "./fetcher/stops/ld-fetch/StopsFetcherNMBS"; +import StopsFetcherProxy from "./fetcher/stops/proxy/StopsFetcherProxy"; import IPublicTransportPlanner from "./planner/public-transport/IPublicTransportPlanner"; import PublicTransportPlannerCSAProfile from "./planner/public-transport/PublicTransportPlannerCSAProfile"; import IRoadPlanner from "./planner/road/IRoadPlanner"; @@ -34,5 +37,7 @@ container.bind(TYPES.ConnectionsFetcher) .to(ConnectionsFetcherMerge).whenTargetTagged("type", "merge");*/ container.bind(TYPES.StopsFetcher).to(StopsFetcherNMBS); +container.bind(TYPES.StopsFetcher).to(StopsFetcherDeLijn); +container.bind(TYPES.StopsFetcherMediator).to(StopsFetcherProxy); export default container; diff --git a/src/planner/road/RoadPlannerBirdsEye.test.ts b/src/planner/road/RoadPlannerBirdsEye.test.ts index 5848ecb6..679b4256 100644 --- a/src/planner/road/RoadPlannerBirdsEye.test.ts +++ b/src/planner/road/RoadPlannerBirdsEye.test.ts @@ -1,31 +1,17 @@ import "jest"; -import Context from "../../Context"; -import StopsFetcherNMBS from "../../fetcher/stops/StopsFetcherNMBS"; +import StopsFetcherNMBS from "../../fetcher/stops/ld-fetch/StopsFetcherNMBS"; import IPath from "../../interfaces/IPath"; import LocationResolverDefault from "../../query-runner/LocationResolverDefault"; -import TYPES from "../../types"; import IRoadPlanner from "./IRoadPlanner"; import RoadPlannerBirdsEye from "./RoadPlannerBirdsEye"; -const dummyContext = { - getContainer() { - return { - getAll(type) { - if (type === TYPES.StopsFetcher) { - return [new StopsFetcherNMBS()]; - } - }, - }; - }, -}; - const planner: IRoadPlanner = new RoadPlannerBirdsEye(); -const locationResolver = new LocationResolverDefault(dummyContext as Context); +const locationResolver = new LocationResolverDefault(new StopsFetcherNMBS()); test("[RoadPlannerBirdsEye] distance between stops", async () => { - const kortrijkLocation = await locationResolver.resolve({id: "http://irail.be/stations/NMBS/008896008"}); - const ghentLocation = await locationResolver.resolve({id: "http://irail.be/stations/NMBS/008892007"}); + const kortrijkLocation = await locationResolver.resolve({ id: "http://irail.be/stations/NMBS/008896008" }); + const ghentLocation = await locationResolver.resolve({ id: "http://irail.be/stations/NMBS/008892007" }); const result: IPath[] = await planner.plan({ from: [kortrijkLocation], // Kortrijk diff --git a/src/planner/road/RoadPlannerBirdsEye.ts b/src/planner/road/RoadPlannerBirdsEye.ts index 380568a8..57fa3488 100644 --- a/src/planner/road/RoadPlannerBirdsEye.ts +++ b/src/planner/road/RoadPlannerBirdsEye.ts @@ -1,9 +1,9 @@ -import haversine from "haversine"; import { injectable } from "inversify"; import ILocation from "../../interfaces/ILocation"; import IPath from "../../interfaces/IPath"; import IProbabilisticValue from "../../interfaces/IProbabilisticValue"; import IResolvedQuery from "../../query-runner/IResolvedQuery"; +import Geo from "../../util/Geo"; import IRoadPlanner from "./IRoadPlanner"; @injectable() @@ -33,7 +33,7 @@ export default class RoadPlannerBirdsEye implements IRoadPlanner { maxWalkingSpeed: number, ): IPath { - const distance = this.getDistanceBetweenLocations(from, to); + const distance = Geo.getDistanceBetweenLocations(from, to); const minDuration = distance / maxWalkingSpeed; const maxDuration = distance / minWalkingSpeed; @@ -52,22 +52,4 @@ export default class RoadPlannerBirdsEye implements IRoadPlanner { }], }; } - - private getDistanceBetweenLocations(from: ILocation, to: ILocation): number { - const { longitude: depLongitude, latitude: depLatitude } = from; - const { longitude: arrLongitude, latitude: arrLatitude } = to; - - if (depLongitude === undefined || depLatitude === undefined || - arrLongitude === undefined || arrLatitude === undefined) { - return Number.POSITIVE_INFINITY; - } - - return haversine({ - latitude: depLatitude, - longitude: depLongitude, - }, { - latitude: arrLatitude, - longitude: arrLongitude, - }); - } } diff --git a/src/query-runner/LocationResolverDefault.test.ts b/src/query-runner/LocationResolverDefault.test.ts index e8507c4b..28aa6b54 100644 --- a/src/query-runner/LocationResolverDefault.test.ts +++ b/src/query-runner/LocationResolverDefault.test.ts @@ -1,22 +1,8 @@ import "jest"; -import Context from "../Context"; -import StopsFetcherNMBS from "../fetcher/stops/StopsFetcherNMBS"; -import TYPES from "../types"; +import StopsFetcherNMBS from "../fetcher/stops/ld-fetch/StopsFetcherNMBS"; import LocationResolverDefault from "./LocationResolverDefault"; -const dummyContext = { - getContainer() { - return { - getAll(type) { - if (type === TYPES.StopsFetcher) { - return [new StopsFetcherNMBS()]; - } - }, - }; - }, -}; - -const locationResolver = new LocationResolverDefault(dummyContext as Context); +const locationResolver = new LocationResolverDefault(new StopsFetcherNMBS()); test("[LocationResolverDefault] Input {id: 'http://...'}", async () => { diff --git a/src/query-runner/LocationResolverDefault.ts b/src/query-runner/LocationResolverDefault.ts index 2fae1390..78f08839 100644 --- a/src/query-runner/LocationResolverDefault.ts +++ b/src/query-runner/LocationResolverDefault.ts @@ -1,20 +1,18 @@ import { inject, injectable } from "inversify"; -import Context from "../Context"; import IStop from "../fetcher/stops/IStop"; -import IStopsFetcher from "../fetcher/stops/IStopsFetcher"; +import IStopsFetcherMediator from "../fetcher/stops/IStopsFetcherMediator"; import ILocation from "../interfaces/ILocation"; import TYPES from "../types"; import ILocationResolver from "./ILocationResolver"; @injectable() export default class LocationResolverDefault implements ILocationResolver { - private readonly stopsFetchers: IStopsFetcher[]; + private readonly stopsFetcherMediator: IStopsFetcherMediator; constructor( - @inject(TYPES.Context) context: Context, + @inject(TYPES.StopsFetcherMediator) stopsFetcherMediator: IStopsFetcherMediator, ) { - this.stopsFetchers = context.getContainer() - .getAll(TYPES.StopsFetcher); + this.stopsFetcherMediator = stopsFetcherMediator; } public async resolve(input: ILocation | string): Promise { @@ -46,35 +44,19 @@ export default class LocationResolverDefault implements ILocationResolver { } private async resolveById(id: string): Promise { - const fetcher = this.determineStopFetcher(id); - - if (fetcher) { - const stop: IStop = await fetcher.getStopById(id); - - if (stop) { - return { - id, - latitude: stop.latitude, - longitude: stop.longitude, - }; - } + const stop: IStop = await this.stopsFetcherMediator.getStopById(id); + + if (stop) { + return { + id, + latitude: stop.latitude, + longitude: stop.longitude, + }; } throw new Error(`No fetcher for id ${id}`); } - private determineStopFetcher(id: string): IStopsFetcher { - if (!this.stopsFetchers || !this.stopsFetchers.length) { - return null; - } - - for (const fetcher of this.stopsFetchers) { - if (id.indexOf(fetcher.prefix) === 0) { - return fetcher; - } - } - } - private isId(testString: string): boolean { return testString.indexOf("http://") === 0 || testString.indexOf("https://") === 0; } diff --git a/src/types.ts b/src/types.ts index 8bbf8a4a..50086179 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,6 +4,7 @@ const TYPES = { LocationResolver: Symbol("LocationResolver"), ConnectionsFetcher: Symbol("ConnectionsFetcher"), StopsFetcher: Symbol("StopsFetcher"), + StopsFetcherMediator: Symbol("StopsFetcherMediator"), PublicTransportPlanner: Symbol("PublicTransportPlanner"), RoadPlanner: Symbol("RoadPlanner"), }; diff --git a/src/util/Geo.ts b/src/util/Geo.ts new file mode 100644 index 00000000..29ddb6b3 --- /dev/null +++ b/src/util/Geo.ts @@ -0,0 +1,22 @@ +import haversine from "haversine"; +import ILocation from "../interfaces/ILocation"; + +export default class Geo { + public static getDistanceBetweenLocations(start: ILocation, stop: ILocation): number { + const { longitude: depLongitude, latitude: depLatitude } = start; + const { longitude: arrLongitude, latitude: arrLatitude } = stop; + + if (depLongitude === undefined || depLatitude === undefined || + arrLongitude === undefined || arrLatitude === undefined) { + return Number.POSITIVE_INFINITY; + } + + return haversine({ + latitude: depLatitude, + longitude: depLongitude, + }, { + latitude: arrLatitude, + longitude: arrLongitude, + }); + } +} From 0cfcb0d441079d74bc62ebc6727af73fca49023b Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Mon, 5 Nov 2018 13:21:15 +0100 Subject: [PATCH 02/97] Dumb ReachableStopsFinderBirdsEye Move time from ILocation to IStep Rename "@id" to just id --- src/fetcher/connections/IConnection.ts | 2 +- .../ld-fetch/ConnectionsIteratorLDFetch.ts | 2 +- src/fetcher/stops/IStop.ts | 2 +- .../stops/json/StopsFetcherNMBSJSON.ts | 2 +- .../stops/ld-fetch/StopsFetcherLDFetch.ts | 2 +- src/interfaces/ILocation.ts | 1 - src/interfaces/IStep.ts | 2 + .../public-transport/CSA/JourneyExtractor.ts | 6 +-- src/planner/stops/IReachableStopsFinder.ts | 7 ++++ .../ReachableStopsFinderBirdsEye.test.ts | 18 +++++++++ .../stops/ReachableStopsFinderBirdsEye.ts | 37 +++++++++++++++++++ .../stops/ReachableStopsFinderRoadPlanner.ts | 0 src/test/test-connections-iterator.ts | 2 +- src/util/Geo.ts | 5 +++ 14 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 src/planner/stops/IReachableStopsFinder.ts create mode 100644 src/planner/stops/ReachableStopsFinderBirdsEye.test.ts create mode 100644 src/planner/stops/ReachableStopsFinderBirdsEye.ts create mode 100644 src/planner/stops/ReachableStopsFinderRoadPlanner.ts diff --git a/src/fetcher/connections/IConnection.ts b/src/fetcher/connections/IConnection.ts index 8b1c1e0c..361bebb7 100644 --- a/src/fetcher/connections/IConnection.ts +++ b/src/fetcher/connections/IConnection.ts @@ -1,5 +1,5 @@ export default interface IConnection { - "@id": string; + id: string; arrivalTime: Date; arrivalStop: string; diff --git a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts index cc942386..0f45d1a7 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts @@ -125,7 +125,7 @@ export default class ConnectionsIteratorLDFetch implements AsyncIterator { stop.latitude = parseFloat(stop.locationY); stop.longitude = parseFloat(stop.locationX); - accu[stop["@id"]] = stop as IStop; + accu[stop.id] = stop as IStop; return accu; }, {}); diff --git a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts index 52cf4feb..be817e10 100644 --- a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts +++ b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts @@ -80,7 +80,7 @@ export default class StopsFetcherLDFetch implements IStopsFetcher { if (!(subject in stopMap)) { stopMap[subject] = { - "@id": subject, + id: subject, }; } diff --git a/src/interfaces/ILocation.ts b/src/interfaces/ILocation.ts index 575a4204..6e607327 100644 --- a/src/interfaces/ILocation.ts +++ b/src/interfaces/ILocation.ts @@ -1,6 +1,5 @@ export default interface ILocation { id?: string; - time?: Date; longitude?: number; latitude?: number; } diff --git a/src/interfaces/IStep.ts b/src/interfaces/IStep.ts index 3f3b7aea..e952474f 100644 --- a/src/interfaces/IStep.ts +++ b/src/interfaces/IStep.ts @@ -4,6 +4,8 @@ import IProbabilisticValue from "./IProbabilisticValue"; export default interface IStep { startLocation: ILocation; stopLocation: ILocation; + startTime?: Date; + stopTime?: Date; duration: IProbabilisticValue; distance?: number; } diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index 9a145e0b..9554de9b 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -83,7 +83,7 @@ export default class JourneyExtractor { }); if (walkingResult && walkingResult[0] && walkingResult[0].steps[0] && - connection.departureTime.getTime() >= step.stopLocation.time.getTime() + + connection.departureTime.getTime() >= step.stopTime.getTime() + walkingResult[0].steps[0].duration.average ) { found = true; @@ -115,12 +115,12 @@ export default class JourneyExtractor { return { startLocation: { id: enterConnection.departureStop, - time: exitConnection.departureTime, }, stopLocation: { id: exitConnection.arrivalStop, - time: exitConnection.arrivalTime, }, + startTime: exitConnection.departureTime, + stopTime: exitConnection.arrivalTime, duration: { average: ( exitConnection.arrivalTime.getTime() - diff --git a/src/planner/stops/IReachableStopsFinder.ts b/src/planner/stops/IReachableStopsFinder.ts new file mode 100644 index 00000000..751bb7d4 --- /dev/null +++ b/src/planner/stops/IReachableStopsFinder.ts @@ -0,0 +1,7 @@ +import IStop from "../../fetcher/stops/IStop"; + +export default interface IReachableStopsFinder { + findReachableStops: (sourceStop: IStop, maximumDuration: number, minimumSpeed: number) => Promise; +} + +export type IReachableStop = [IStop, number]; diff --git a/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts b/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts new file mode 100644 index 00000000..5d7fbb8a --- /dev/null +++ b/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts @@ -0,0 +1,18 @@ +import "jest"; +import StopsFetcherDeLijn from "../../fetcher/stops/ld-fetch/StopsFetcherDeLijn"; +import ReachableStopsFinderBirdsEye from "./ReachableStopsFinderBirdsEye"; + +const stopsFetcher = new StopsFetcherDeLijn(); +const reachableStopsFinder = new ReachableStopsFinderBirdsEye(stopsFetcher); + +test("[ReachableStopsFinderBirdsEye] reachable stops", async () => { + + const sourceStop = await stopsFetcher.getStopById("https://data.delijn.be/stops/590009"); + + expect(sourceStop).toBeDefined(); + + // Get reachable stops in 1 km (.25h at 4km/h) + const reachableStops = await reachableStopsFinder.findReachableStops(sourceStop, .25, 4); + + expect(reachableStops.length).toBeGreaterThan(1); +}); diff --git a/src/planner/stops/ReachableStopsFinderBirdsEye.ts b/src/planner/stops/ReachableStopsFinderBirdsEye.ts new file mode 100644 index 00000000..1f855c49 --- /dev/null +++ b/src/planner/stops/ReachableStopsFinderBirdsEye.ts @@ -0,0 +1,37 @@ +import { inject } from "inversify"; +import IStop from "../../fetcher/stops/IStop"; +import IStopsFetcherMediator from "../../fetcher/stops/IStopsFetcherMediator"; +import TYPES from "../../types"; +import Geo from "../../util/Geo"; +import IReachableStopsFinder, { IReachableStop } from "./IReachableStopsFinder"; + +export default class ReachableStopsFinderBirdsEye implements IReachableStopsFinder { + private readonly stopsFetcherMediator: IStopsFetcherMediator; + + constructor( + @inject(TYPES.StopsFetcherMediator) stopsFetcherMediator: IStopsFetcherMediator, + ) { + this.stopsFetcherMediator = stopsFetcherMediator; + } + + public async findReachableStops( + source: IStop, + maximumDuration: number, + minimumSpeed: number): Promise { + + const allStops = await this.stopsFetcherMediator.getAllStops(); + + return allStops.map((possibleTarget: IStop): IReachableStop => { + if (possibleTarget.id === source.id) { + return [source, 0]; + } + + const distance = Geo.getDistanceBetweenStops(source, possibleTarget); + const duration = distance / minimumSpeed; + + if (duration <= maximumDuration) { + return [possibleTarget, duration]; + } + }).filter((reachableStop) => !!reachableStop); + } +} diff --git a/src/planner/stops/ReachableStopsFinderRoadPlanner.ts b/src/planner/stops/ReachableStopsFinderRoadPlanner.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/test/test-connections-iterator.ts b/src/test/test-connections-iterator.ts index dec46a57..315c248e 100644 --- a/src/test/test-connections-iterator.ts +++ b/src/test/test-connections-iterator.ts @@ -20,7 +20,7 @@ fetcher.setConfig({ let i = 0; for await (const connection of fetcher) { - console.log(i++, connection["@id"], connection.departureTime); + console.log(i++, connection.id, connection.departureTime); if (i++ > 1000) { break; diff --git a/src/util/Geo.ts b/src/util/Geo.ts index 29ddb6b3..25ee04ef 100644 --- a/src/util/Geo.ts +++ b/src/util/Geo.ts @@ -1,4 +1,5 @@ import haversine from "haversine"; +import IStop from "../fetcher/stops/IStop"; import ILocation from "../interfaces/ILocation"; export default class Geo { @@ -19,4 +20,8 @@ export default class Geo { longitude: arrLongitude, }); } + + public static getDistanceBetweenStops(start: IStop, stop: IStop) { + return this.getDistanceBetweenLocations(start as ILocation, stop as ILocation); + } } From e3949d5ac26cc3638ab266752d4fe047586446f7 Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Mon, 5 Nov 2018 14:19:59 +0100 Subject: [PATCH 03/97] 1. refactor CSA --- .../ld-fetch/ConnectionsFetcherLDFetch.ts | 2 +- .../public-transport/CSA/JourneyExtractor.ts | 81 +++++---- .../EarliestArrival.ts | 0 .../data-structure/IArrivalTimeByTransfers.ts | 3 + .../IEarliestArrivalByTrip.ts | 0 .../IProfilesByStop.ts | 0 .../Profile.ts | 3 +- src/planner/public-transport/CSA/utils.ts | 9 +- .../PublicTransportPlannerCSAProfile.ts | 169 ++++++++++-------- 9 files changed, 157 insertions(+), 110 deletions(-) rename src/planner/public-transport/CSA/{dataStructure => data-structure}/EarliestArrival.ts (100%) create mode 100644 src/planner/public-transport/CSA/data-structure/IArrivalTimeByTransfers.ts rename src/planner/public-transport/CSA/{dataStructure => data-structure}/IEarliestArrivalByTrip.ts (100%) rename src/planner/public-transport/CSA/{dataStructure => data-structure}/IProfilesByStop.ts (100%) rename src/planner/public-transport/CSA/{dataStructure => data-structure}/Profile.ts (80%) diff --git a/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts b/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts index ef3aff68..8d73f3f0 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts @@ -4,7 +4,7 @@ import IConnection from "../IConnection"; import IConnectionsFetcher from "../IConnectionsFetcher"; import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig"; - +(Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator"); @injectable() export default abstract class ConnectionsFetcherLDFetch implements IConnectionsFetcher { diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index 9554de9b..a3fce961 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -3,9 +3,10 @@ import ILocation from "../../../interfaces/ILocation"; import IPath from "../../../interfaces/IPath"; import IStep from "../../../interfaces/IStep"; import ILocationResolver from "../../../query-runner/ILocationResolver"; +import IResolvedQuery from "../../../query-runner/IResolvedQuery"; import IRoadPlanner from "../../road/IRoadPlanner"; -import IProfilesByStop from "./dataStructure/IProfilesByStop"; -import Profile from "./dataStructure/Profile"; +import IProfilesByStop from "./data-structure/IProfilesByStop"; +import Profile from "./data-structure/Profile"; import { filterInfinity } from "./utils"; export default class JourneyExtractor { @@ -21,37 +22,51 @@ export default class JourneyExtractor { public async extractJourneys( profilesByStop: IProfilesByStop, - from: ILocation, - to: ILocation, - departureTime: Date, + query: IResolvedQuery, ): Promise { - const profile: IProfilesByStop = filterInfinity(profilesByStop); + const filteredProfilesByStop: IProfilesByStop = filterInfinity(profilesByStop); const journeys = []; - for (const entry of profile[from.id]) { - if (entry.departureTime >= departureTime.getTime()) { - for (let transfers = 0; transfers < entry.arrivalTimes.length; transfers++) { - if (entry.arrivalTimes[transfers] < this.bestArrivalTime) { - const journey = await this.extractJourney(to, entry, transfers, profile); - journeys.push(journey); + for (const departureStop of query.from) { + + for (const profile of filteredProfilesByStop[departureStop.id]) { + if (profile.departureTime >= query.minimumDepartureTime.getTime()) { + + for (let transfers = 0; transfers < profile.arrivalTimes.length; transfers++) { + for (const arrivalStop of query.to) { + if (profile.arrivalTimes[transfers] < this.bestArrivalTime) { // TODO check for all from - to pairs + const journey = await this.extractJourney( + arrivalStop, + profile, + transfers, + filteredProfilesByStop, + query, + ); + + journeys.push(journey); + } + } } } + } } + return journeys; } private async extractJourney( - target: ILocation, - entry: Profile, + arrivalStop: ILocation, + profile: Profile, transfers: number, - profile: IProfilesByStop, + profilesByStop: IProfilesByStop, + query: IResolvedQuery, ): Promise { // Extract journey for amount of transfers - const journey: IPath = this.createJourney(entry, transfers); - this.bestArrivalTime = entry.arrivalTimes[transfers]; + const journey: IPath = this.createJourney(profile, transfers); + this.bestArrivalTime = profile.arrivalTimes[transfers]; - let currentEntry = entry; + let currentEntry = profile; let remainingTransfers = transfers; while (remainingTransfers >= 0) { @@ -64,7 +79,7 @@ export default class JourneyExtractor { remainingTransfers--; if (remainingTransfers >= 0) { - const nextProfile = profile[step.stopLocation.id]; + const nextProfile = profilesByStop[step.stopLocation.id]; let i = nextProfile.length - 1; let found = false; @@ -72,14 +87,14 @@ export default class JourneyExtractor { while (!found) { const connection = nextProfile[i].enterConnections[remainingTransfers]; - const from = await this.locationResolver.resolve({id: step.stopLocation.id }); - const to = await this.locationResolver.resolve({id: connection.departureStop}); + const from = await this.locationResolver.resolve(step.stopLocation.id); + const to = await this.locationResolver.resolve(connection.departureStop); const walkingResult = await this.roadPlanner.plan({ from: [from], to: [to], - minimumWalkingSpeed: 3, - maximumWalkingSpeed: 6, + minimumWalkingSpeed: query.minimumWalkingSpeed || 3, + maximumWalkingSpeed: query.maximumWalkingSpeed || 6, }); if (walkingResult && walkingResult[0] && walkingResult[0].steps[0] && @@ -95,17 +110,17 @@ export default class JourneyExtractor { } } - if (journey.steps[journey.steps.length - 1].stopLocation.id !== target.id) { - await this.addFinalFootpath(journey, target); + if (journey.steps[journey.steps.length - 1].stopLocation.id !== arrivalStop.id) { + await this.addFinalFootpath(journey, arrivalStop, query); } return journey; } - private createJourney(entry: Profile, transfers: number): IPath { + private createJourney(profile: Profile, transfers: number): IPath { return { - departureTime: new Date(entry.departureTime), - arrivalTime: new Date(entry.arrivalTimes[transfers]), + departureTime: new Date(profile.departureTime), + arrivalTime: new Date(profile.arrivalTimes[transfers]), transfers, steps: [], }; @@ -130,17 +145,17 @@ export default class JourneyExtractor { }; } - private async addFinalFootpath(journey: IPath, to: ILocation): Promise { + private async addFinalFootpath(journey: IPath, arrivalStop: ILocation, query: IResolvedQuery): Promise { const lastStep = journey.steps[journey.steps.length - 1]; - const fromLocation = await this.locationResolver.resolve({id: lastStep.stopLocation.id }); - const toLocation = await this.locationResolver.resolve(to); + const fromLocation = await this.locationResolver.resolve(lastStep.stopLocation.id ); + const toLocation = await this.locationResolver.resolve(arrivalStop); const walkingResult = await this.roadPlanner.plan({ from: [fromLocation], to: [toLocation], - minimumWalkingSpeed: 3, - maximumWalkingSpeed: 6, + minimumWalkingSpeed: query.minimumWalkingSpeed || 3, + maximumWalkingSpeed: query.maximumWalkingSpeed || 6, }); if (walkingResult && walkingResult[0] && walkingResult[0].steps[0]) { diff --git a/src/planner/public-transport/CSA/dataStructure/EarliestArrival.ts b/src/planner/public-transport/CSA/data-structure/EarliestArrival.ts similarity index 100% rename from src/planner/public-transport/CSA/dataStructure/EarliestArrival.ts rename to src/planner/public-transport/CSA/data-structure/EarliestArrival.ts diff --git a/src/planner/public-transport/CSA/data-structure/IArrivalTimeByTransfers.ts b/src/planner/public-transport/CSA/data-structure/IArrivalTimeByTransfers.ts new file mode 100644 index 00000000..87a5fca3 --- /dev/null +++ b/src/planner/public-transport/CSA/data-structure/IArrivalTimeByTransfers.ts @@ -0,0 +1,3 @@ +export default interface IArrivalTimeByTransfers extends Array { + [amountOfTransfers: number]: number; +} diff --git a/src/planner/public-transport/CSA/dataStructure/IEarliestArrivalByTrip.ts b/src/planner/public-transport/CSA/data-structure/IEarliestArrivalByTrip.ts similarity index 100% rename from src/planner/public-transport/CSA/dataStructure/IEarliestArrivalByTrip.ts rename to src/planner/public-transport/CSA/data-structure/IEarliestArrivalByTrip.ts diff --git a/src/planner/public-transport/CSA/dataStructure/IProfilesByStop.ts b/src/planner/public-transport/CSA/data-structure/IProfilesByStop.ts similarity index 100% rename from src/planner/public-transport/CSA/dataStructure/IProfilesByStop.ts rename to src/planner/public-transport/CSA/data-structure/IProfilesByStop.ts diff --git a/src/planner/public-transport/CSA/dataStructure/Profile.ts b/src/planner/public-transport/CSA/data-structure/Profile.ts similarity index 80% rename from src/planner/public-transport/CSA/dataStructure/Profile.ts rename to src/planner/public-transport/CSA/data-structure/Profile.ts index 20a2cf0e..d305e24e 100644 --- a/src/planner/public-transport/CSA/dataStructure/Profile.ts +++ b/src/planner/public-transport/CSA/data-structure/Profile.ts @@ -1,8 +1,9 @@ import IConnection from "../../../../fetcher/connections/IConnection"; +import IArrivalTimeByTransfers from "./IArrivalTimeByTransfers"; export default class Profile { public departureTime: number; - public arrivalTimes: number[]; + public arrivalTimes: IArrivalTimeByTransfers; public enterConnections: IConnection[]; public exitConnections: IConnection[]; diff --git a/src/planner/public-transport/CSA/utils.ts b/src/planner/public-transport/CSA/utils.ts index f2628172..b49b5bc0 100644 --- a/src/planner/public-transport/CSA/utils.ts +++ b/src/planner/public-transport/CSA/utils.ts @@ -1,5 +1,6 @@ import IConnection from "../../../fetcher/connections/IConnection"; -import IProfilesByStop from "./dataStructure/IProfilesByStop"; +import IProfilesByStop from "./data-structure/IProfilesByStop"; +import IArrivalTimeByTransfers from "./data-structure/IArrivalTimeByTransfers"; /** * Shift a number vector to the right, @@ -8,7 +9,7 @@ import IProfilesByStop from "./dataStructure/IProfilesByStop"; * @param vector: [int] */ -export function shiftVector(vector: number[]): number[] { +export function shiftVector(vector: IArrivalTimeByTransfers): IArrivalTimeByTransfers { vector.unshift(Infinity); vector.pop(); return vector; @@ -20,12 +21,12 @@ export function shiftVector(vector: number[]): number[] { * @param vectors: [[int]] * @returns {Array} */ -export function minVector(...vectors: number[][]): number[] { +export function minVector(...vectors: IArrivalTimeByTransfers[]): IArrivalTimeByTransfers { if (!vectors || !vectors[0]) { return []; } - const result: number[] = vectors[0]; + const result: IArrivalTimeByTransfers = vectors[0]; for (let vectorIndex = 1 ; vectorIndex < vectors.length ; vectorIndex++) { for (let numberIndex = 0 ; numberIndex < vectors[vectorIndex].length ; numberIndex++) { if (vectors[vectorIndex][numberIndex] < result[numberIndex]) { diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts index 1ac80079..20afc3b8 100644 --- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts @@ -6,11 +6,12 @@ import ILocationResolver from "../../query-runner/ILocationResolver"; import IResolvedQuery from "../../query-runner/IResolvedQuery"; import TYPES from "../../types"; import IRoadPlanner from "../road/IRoadPlanner"; -import IEarliestArrival from "./CSA/dataStructure/EarliestArrival"; -import EarliestArrival from "./CSA/dataStructure/EarliestArrival"; -import IEarliestArrivalByTrip from "./CSA/dataStructure/IEarliestArrivalByTrip"; -import IProfilesByStop from "./CSA/dataStructure/IProfilesByStop"; -import Profile from "./CSA/dataStructure/Profile"; +import IEarliestArrival from "./CSA/data-structure/EarliestArrival"; +import EarliestArrival from "./CSA/data-structure/EarliestArrival"; +import IArrivalTimeByTransfers from "./CSA/data-structure/IArrivalTimeByTransfers"; +import IEarliestArrivalByTrip from "./CSA/data-structure/IEarliestArrivalByTrip"; +import IProfilesByStop from "./CSA/data-structure/IProfilesByStop"; +import Profile from "./CSA/data-structure/Profile"; import JourneyExtractor from "./CSA/JourneyExtractor"; import { evalProfile, minVector, shiftVector } from "./CSA/utils"; import IPublicTransportPlanner from "./IPublicTransportPlanner"; @@ -39,27 +40,48 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor this.connectionsFetcher = connectionsFetcher; this.roadPlanner = roadPlanner; this.locationResolver = locationResolver; + this.journeyExtractor = new JourneyExtractor(roadPlanner, locationResolver); + } + + public async plan(query: IResolvedQuery): Promise { + this.query = query; + + this.setConnectionsFetcherConfig(); + + return await this.calculateJourneys(); + } - const upperBoundDate = new Date(); - upperBoundDate.setHours(upperBoundDate.getHours() + 2 ); + private setConnectionsFetcherConfig() { + let upperBoundDate; + let lowerBoundDate; + + if (this.query.maximumArrivalTime) { + upperBoundDate = this.query.maximumArrivalTime; + } else { + upperBoundDate = new Date(); + upperBoundDate.setHours(upperBoundDate.getHours() + 2); + } + + if (this.query.minimumDepartureTime) { + lowerBoundDate = this.query.minimumDepartureTime; + } else { + lowerBoundDate = new Date(); + } + + this.query.maximumArrivalTime = upperBoundDate; + this.query.minimumDepartureTime = lowerBoundDate; this.connectionsFetcher.setConfig({ backward: true, - upperBoundDate, // TODO what's the upperBoundDate. + upperBoundDate, + lowerBoundDate, }); } - public async plan(query: IResolvedQuery): Promise { - return await this.calculateJourneys(query); - } - - private async calculateJourneys(query: IResolvedQuery): Promise { - this.query = query; - const earliestDepartureTime = new Date(); - + private async calculateJourneys(): Promise { for await (const connection of this.connectionsFetcher) { - if (connection.departureTime < earliestDepartureTime) { + if (connection.departureTime < this.query.minimumDepartureTime) { break; } @@ -69,15 +91,13 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor this.updateEarliestArrivalByTrip(connection, earliestArrivalTime); if (!this.isDominated(connection, earliestArrivalTime)) { - this.incorporateForFootpaths(connection, earliestArrivalTime); + this.getFootpathsForDepartureStop(connection, earliestArrivalTime); } } return await this.journeyExtractor.extractJourneys( this.profilesByStop, - query.from[0], - query.to[0], - earliestDepartureTime, + this.query, ); } @@ -93,7 +113,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor } } - private calculateEarliestArrivalTime(connection: IConnection): number[] { + private calculateEarliestArrivalTime(connection: IConnection): IArrivalTimeByTransfers { const t1 = this.walkToTarget(connection); const t2 = this.remainSeated(connection); const t3 = this.takeTransfer(connection); @@ -101,7 +121,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor return minVector(t1, t2, t3); } - private walkToTarget(connection: IConnection): number[] { + private walkToTarget(connection: IConnection): IArrivalTimeByTransfers { let walkingTimeToTarget = this.D[connection.arrivalStop]; // TODO get duration. if (connection.arrivalStop === this.query.to[0].id) { // not needed. @@ -115,43 +135,46 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor return Array(this.maxLegs).fill(connection.arrivalTime.getTime() + walkingTimeToTarget); } - private remainSeated(connection: IConnection): number[] { + private remainSeated(connection: IConnection): IArrivalTimeByTransfers { return this.earliestArrivalByTrip[connection["gtfs:trip"]].map((trip) => trip.arrivalTime); } - private takeTransfer(connection: IConnection): number[] { + private takeTransfer(connection: IConnection): IArrivalTimeByTransfers { return shiftVector(evalProfile(this.profilesByStop, connection, this.maxLegs)); } - private updateEarliestArrivalByTrip(connection: IConnection, currentArrivalTimeByTransfers: number[]): void { + private updateEarliestArrivalByTrip( + connection: IConnection, + currentArrivalTimeByTransfers: IArrivalTimeByTransfers, + ): void { const earliestArrivalByTransfers: IEarliestArrival[] = this.earliestArrivalByTrip[connection["gtfs:trip"]]; - this.earliestArrivalByTrip[connection["gtfs:trip"]] = earliestArrivalByTransfers.map( - (earliestArrival, transfer) => { - if (currentArrivalTimeByTransfers[transfer] < earliestArrival.arrivalTime) { - return {connection, arrivalTime: currentArrivalTimeByTransfers[transfer]}; - } - return earliestArrival; - }); + this.earliestArrivalByTrip[connection["gtfs:trip"]] = earliestArrivalByTransfers.map((earliestArrival, transfer) => + currentArrivalTimeByTransfers[transfer] < earliestArrival.arrivalTime ? + { connection, arrivalTime: currentArrivalTimeByTransfers[transfer] } : + earliestArrival, + ); } - private isDominated(connection: IConnection, currentArrivalTimeByTransfers: number[]): boolean { + private isDominated(connection: IConnection, currentArrivalTimeByTransfers: IArrivalTimeByTransfers): boolean { const depProfile = this.profilesByStop[connection.departureStop]; const earliestProfileEntry = depProfile[depProfile.length - 1]; return earliestProfileEntry.arrivalTimes.reduce((memo, arrivalTime, transfer) => - memo && arrivalTime <= currentArrivalTimeByTransfers[transfer] - , true); + memo && arrivalTime <= currentArrivalTimeByTransfers[transfer], true); } - private incorporateForFootpaths(connection: IConnection, currentArrivalTimeByTransfers: number[]): void { + private getFootpathsForDepartureStop( + connection: IConnection, + currentArrivalTimeByTransfers: IArrivalTimeByTransfers, + ): void { const depProfile: Profile[] = this.profilesByStop[connection.departureStop]; const earliestProfileEntry = depProfile[depProfile.length - 1]; const minVectorTimes = minVector(currentArrivalTimeByTransfers, earliestProfileEntry.arrivalTimes); // For all footpaths with f_arr_stop = c_dep_stop // TODO getInterstopDistance - const ISDs = [{stop1: connection.departureStop, stop2: connection.departureStop, duration: 60000}]; + const ISDs = [{ stop1: connection.departureStop, stop2: connection.departureStop, duration: 60000 }]; ISDs.forEach((ISD) => { // stop is f_dep_stop, the stop of the ISD that is _not_ connection.dep.stop let stop = ISD.stop1; @@ -160,61 +183,65 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor } // Incorporate (c_dep_time - f_dur, t_c) into profile of S[f_dep_stop] - this.incorporateInProfile(connection, ISD, stop, minVectorTimes); + this.incorporateInProfile(connection, ISD.duration, stop, minVectorTimes); }); } - private incorporateInProfile(connection: IConnection, ISD, stop: string, minVectorTimes: number[]) { - let ISDDepProfile = this.profilesByStop[stop]; // S[f_dep_stop] - // S[f_dep_stop] might be undefined (open world), in that case initialize its profile entry - if (ISDDepProfile === undefined) { - ISDDepProfile = this.profilesByStop[stop] = [new Profile(this.maxLegs)]; - } - const ISDDepEarliestEntry = ISDDepProfile[ISDDepProfile.length - 1]; // earliest dep time - // Enter and exit connections are journey pointers - const enterConnections = []; - const exitConnections = []; + private incorporateInProfile( + connection: IConnection, + duration: number, + stop: string, + minVectorTimes: IArrivalTimeByTransfers, + ) { + let profilesByDepartureStop = this.profilesByStop[stop]; - // For each amount of legs - for (let i = 0; i < this.maxLegs; i++) { - // If the new arrival time is better, update journey pointers - // Else, keep old journey pointers - if (minVectorTimes[i] < ISDDepEarliestEntry.arrivalTimes[i]) { - enterConnections[i] = connection; - exitConnections[i] = this.earliestArrivalByTrip[connection["gtfs:trip"]][i].connection; - if (exitConnections[i] === null) { - // This means the exit connection is the enter connection, - // and tripStructure[connection.tripId] hasn't been initialized properly yet. - exitConnections[i] = connection; - } - } else { - enterConnections[i] = ISDDepEarliestEntry.enterConnections[i]; - exitConnections[i] = ISDDepEarliestEntry.exitConnections[i]; - } + if (profilesByDepartureStop === undefined) { + profilesByDepartureStop = this.profilesByStop[stop] = [new Profile(this.maxLegs)]; } + const earliestDepTimeProfile = profilesByDepartureStop[profilesByDepartureStop.length - 1]; // If arrival times for all numbers of legs are equal to the earliest entry, this // entry is redundant let redundant = true; for (let i = 0; i < this.maxLegs; i++) { - redundant = redundant && minVectorTimes[i] >= ISDDepEarliestEntry.arrivalTimes[i]; + redundant = redundant && minVectorTimes[i] >= earliestDepTimeProfile.arrivalTimes[i]; } if (!redundant) { + const enterConnections = []; + const exitConnections = []; + + for (let transfers = 0; transfers < this.maxLegs; transfers++) { + // If the new arrival time is better, update journey pointers + // Else, keep old journey pointers + if (minVectorTimes[transfers] < earliestDepTimeProfile.arrivalTimes[transfers]) { + enterConnections[transfers] = connection; + exitConnections[transfers] = this.earliestArrivalByTrip[connection["gtfs:trip"]][transfers].connection; + if (exitConnections[transfers] === null) { + // This means the exit connection is the enter connection, + // and tripStructure[connection.tripId] hasn't been initialized properly yet. + exitConnections[transfers] = connection; + } + } else { + enterConnections[transfers] = earliestDepTimeProfile.enterConnections[transfers]; + exitConnections[transfers] = earliestDepTimeProfile.exitConnections[transfers]; + } + } + // If the new departure time is equal, update the profile entry // Else, insert a new entry - const departureTime = connection.departureTime.getTime() - ISD.duration; // Calculate c_dep_time - f_dur - const entry = { + const departureTime = connection.departureTime.getTime() - duration; // Calculate c_dep_time - f_dur + const newProfile = { departureTime, arrivalTimes: minVectorTimes, enterConnections, exitConnections, }; - if (ISDDepEarliestEntry.departureTime !== departureTime) { - ISDDepProfile.push(entry); + if (earliestDepTimeProfile.departureTime !== departureTime) { + profilesByDepartureStop.push(newProfile); } else { - ISDDepProfile[ISDDepProfile.length - 1] = entry; + profilesByDepartureStop[profilesByDepartureStop.length - 1] = newProfile; } } } From 7d5177a5d272344293aaaf0ea5aed80cfb8ba3b6 Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Mon, 5 Nov 2018 15:24:18 +0100 Subject: [PATCH 04/97] 1. added ISD to CSA using reachableStopsFinder. --- .../ld-fetch/ConnectionsFetcherLDFetch.ts | 1 - src/fetcher/stops/proxy/StopsFetcherProxy.ts | 3 +- src/inversify.config.ts | 4 + .../public-transport/CSA/JourneyExtractor.ts | 10 +-- .../CSA/{utils.ts => util/vectors.ts} | 6 +- .../PublicTransportPlannerCSAProfile.ts | 79 ++++++++++++------- .../stops/ReachableStopsFinderBirdsEye.ts | 3 +- src/types.ts | 1 + 8 files changed, 67 insertions(+), 40 deletions(-) rename src/planner/public-transport/CSA/{utils.ts => util/vectors.ts} (89%) diff --git a/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts b/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts index 8d73f3f0..89b8e153 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts @@ -4,7 +4,6 @@ import IConnection from "../IConnection"; import IConnectionsFetcher from "../IConnectionsFetcher"; import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig"; -(Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator"); @injectable() export default abstract class ConnectionsFetcherLDFetch implements IConnectionsFetcher { diff --git a/src/fetcher/stops/proxy/StopsFetcherProxy.ts b/src/fetcher/stops/proxy/StopsFetcherProxy.ts index 39cad306..0a438798 100644 --- a/src/fetcher/stops/proxy/StopsFetcherProxy.ts +++ b/src/fetcher/stops/proxy/StopsFetcherProxy.ts @@ -1,9 +1,10 @@ -import { multiInject } from "inversify"; +import { injectable, multiInject } from "inversify"; import TYPES from "../../../types"; import IStop from "../IStop"; import IStopsFetcher from "../IStopsFetcher"; import IStopsFetcherMediator from "../IStopsFetcherMediator"; +@injectable() export default class StopsFetcherProxy implements IStopsFetcherMediator { private readonly stopsFetchers: IStopsFetcher[]; diff --git a/src/inversify.config.ts b/src/inversify.config.ts index eb063108..ce506e07 100644 --- a/src/inversify.config.ts +++ b/src/inversify.config.ts @@ -13,6 +13,8 @@ import IPublicTransportPlanner from "./planner/public-transport/IPublicTransport import PublicTransportPlannerCSAProfile from "./planner/public-transport/PublicTransportPlannerCSAProfile"; import IRoadPlanner from "./planner/road/IRoadPlanner"; import RoadPlannerBirdsEye from "./planner/road/RoadPlannerBirdsEye"; +import IReachableStopsFinder from "./planner/stops/IReachableStopsFinder"; +import ReachableStopsFinderBirdsEye from "./planner/stops/ReachableStopsFinderBirdsEye"; import ILocationResolver from "./query-runner/ILocationResolver"; import IQueryRunner from "./query-runner/IQueryRunner"; import LocationResolverDefault from "./query-runner/LocationResolverDefault"; @@ -40,4 +42,6 @@ container.bind(TYPES.StopsFetcher).to(StopsFetcherNMBS); container.bind(TYPES.StopsFetcher).to(StopsFetcherDeLijn); container.bind(TYPES.StopsFetcherMediator).to(StopsFetcherProxy); +container.bind(TYPES.ReachableStopsFinder).to(ReachableStopsFinderBirdsEye); + export default container; diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index a3fce961..f31f2774 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -7,7 +7,7 @@ import IResolvedQuery from "../../../query-runner/IResolvedQuery"; import IRoadPlanner from "../../road/IRoadPlanner"; import IProfilesByStop from "./data-structure/IProfilesByStop"; import Profile from "./data-structure/Profile"; -import { filterInfinity } from "./utils"; +import { filterInfinity } from "./util/vectors"; export default class JourneyExtractor { public readonly roadPlanner: IRoadPlanner; @@ -93,8 +93,8 @@ export default class JourneyExtractor { const walkingResult = await this.roadPlanner.plan({ from: [from], to: [to], - minimumWalkingSpeed: query.minimumWalkingSpeed || 3, - maximumWalkingSpeed: query.maximumWalkingSpeed || 6, + minimumWalkingSpeed: query.minimumWalkingSpeed, + maximumWalkingSpeed: query.maximumWalkingSpeed, }); if (walkingResult && walkingResult[0] && walkingResult[0].steps[0] && @@ -154,8 +154,8 @@ export default class JourneyExtractor { const walkingResult = await this.roadPlanner.plan({ from: [fromLocation], to: [toLocation], - minimumWalkingSpeed: query.minimumWalkingSpeed || 3, - maximumWalkingSpeed: query.maximumWalkingSpeed || 6, + minimumWalkingSpeed: query.minimumWalkingSpeed, + maximumWalkingSpeed: query.maximumWalkingSpeed, }); if (walkingResult && walkingResult[0] && walkingResult[0].steps[0]) { diff --git a/src/planner/public-transport/CSA/utils.ts b/src/planner/public-transport/CSA/util/vectors.ts similarity index 89% rename from src/planner/public-transport/CSA/utils.ts rename to src/planner/public-transport/CSA/util/vectors.ts index b49b5bc0..e68798d4 100644 --- a/src/planner/public-transport/CSA/utils.ts +++ b/src/planner/public-transport/CSA/util/vectors.ts @@ -1,6 +1,6 @@ -import IConnection from "../../../fetcher/connections/IConnection"; -import IProfilesByStop from "./data-structure/IProfilesByStop"; -import IArrivalTimeByTransfers from "./data-structure/IArrivalTimeByTransfers"; +import IConnection from "../../../../fetcher/connections/IConnection"; +import IProfilesByStop from "../data-structure/IProfilesByStop"; +import IArrivalTimeByTransfers from "../data-structure/IArrivalTimeByTransfers"; /** * Shift a number vector to the right, diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts index 20afc3b8..9bf008d5 100644 --- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts @@ -1,34 +1,38 @@ import { inject, injectable } from "inversify"; import IConnection from "../../fetcher/connections/IConnection"; import IConnectionsFetcher from "../../fetcher/connections/IConnectionsFetcher"; +import IStop from "../../fetcher/stops/IStop"; import IPath from "../../interfaces/IPath"; import ILocationResolver from "../../query-runner/ILocationResolver"; import IResolvedQuery from "../../query-runner/IResolvedQuery"; import TYPES from "../../types"; import IRoadPlanner from "../road/IRoadPlanner"; -import IEarliestArrival from "./CSA/data-structure/EarliestArrival"; +import IReachableStopsFinder from "../stops/IReachableStopsFinder"; import EarliestArrival from "./CSA/data-structure/EarliestArrival"; +import IEarliestArrival from "./CSA/data-structure/EarliestArrival"; import IArrivalTimeByTransfers from "./CSA/data-structure/IArrivalTimeByTransfers"; import IEarliestArrivalByTrip from "./CSA/data-structure/IEarliestArrivalByTrip"; import IProfilesByStop from "./CSA/data-structure/IProfilesByStop"; import Profile from "./CSA/data-structure/Profile"; import JourneyExtractor from "./CSA/JourneyExtractor"; -import { evalProfile, minVector, shiftVector } from "./CSA/utils"; +import { evalProfile, minVector, shiftVector } from "./CSA/util/vectors"; import IPublicTransportPlanner from "./IPublicTransportPlanner"; @injectable() export default class PublicTransportPlannerCSAProfile implements IPublicTransportPlanner { - public readonly connectionsFetcher: IConnectionsFetcher; - public readonly roadPlanner: IRoadPlanner; - public readonly locationResolver: ILocationResolver; + private readonly connectionsFetcher: IConnectionsFetcher; + private readonly roadPlanner: IRoadPlanner; + private readonly locationResolver: ILocationResolver; + private readonly reachableStopsFinder: IReachableStopsFinder; private readonly journeyExtractor: JourneyExtractor; private profilesByStop: IProfilesByStop = {}; // S private earliestArrivalByTrip: IEarliestArrivalByTrip = {}; // T + private durationToTargetByStop: number[] = []; - private D: number[] = []; - private maxLegs: number = 8; + private maxLegs: number = 8; // TODO define max legs on query? + private maximumDuration: number = 0.4; // TODO define maximum duration (radius) on query? private query: IResolvedQuery; @@ -36,10 +40,12 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor @inject(TYPES.ConnectionsFetcher) connectionsFetcher: IConnectionsFetcher, @inject(TYPES.RoadPlanner) roadPlanner: IRoadPlanner, @inject(TYPES.LocationResolver) locationResolver: ILocationResolver, + @inject(TYPES.ReachableStopsFinder) reachableStopsFinder: IReachableStopsFinder, ) { this.connectionsFetcher = connectionsFetcher; this.roadPlanner = roadPlanner; this.locationResolver = locationResolver; + this.reachableStopsFinder = reachableStopsFinder; this.journeyExtractor = new JourneyExtractor(roadPlanner, locationResolver); } @@ -47,12 +53,16 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor public async plan(query: IResolvedQuery): Promise { this.query = query; - this.setConnectionsFetcherConfig(); + query.minimumWalkingSpeed = query.minimumWalkingSpeed || 3; + query.maximumWalkingSpeed = query.maximumWalkingSpeed || 6; + this.maximumDuration = 1; + + this.setBounds(); return await this.calculateJourneys(); } - private setConnectionsFetcherConfig() { + private setBounds() { let upperBoundDate; let lowerBoundDate; @@ -80,6 +90,8 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor } private async calculateJourneys(): Promise { + await this.initDurationToTargetByStop(); + for await (const connection of this.connectionsFetcher) { if (connection.departureTime < this.query.minimumDepartureTime) { break; @@ -91,7 +103,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor this.updateEarliestArrivalByTrip(connection, earliestArrivalTime); if (!this.isDominated(connection, earliestArrivalTime)) { - this.getFootpathsForDepartureStop(connection, earliestArrivalTime); + await this.getFootpathsForDepartureStop(connection, earliestArrivalTime); } } @@ -121,12 +133,22 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor return minVector(t1, t2, t3); } - private walkToTarget(connection: IConnection): IArrivalTimeByTransfers { - let walkingTimeToTarget = this.D[connection.arrivalStop]; // TODO get duration. + private async initDurationToTargetByStop(): Promise { + for (const arrivalStop of this.query.to) { + const reachableStops = await this.reachableStopsFinder.findReachableStops( + arrivalStop as IStop, + this.maximumDuration, + this.query.minimumWalkingSpeed, + ); - if (connection.arrivalStop === this.query.to[0].id) { // not needed. - walkingTimeToTarget = 0; + for (const stop of reachableStops) { + this.durationToTargetByStop[stop[0].id] = stop[1] * 3600000; + } } + } + + private walkToTarget(connection: IConnection): IArrivalTimeByTransfers { + const walkingTimeToTarget = this.durationToTargetByStop[connection.arrivalStop]; if (walkingTimeToTarget === undefined) { return Array(this.maxLegs).fill(Infinity); @@ -164,39 +186,38 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor memo && arrivalTime <= currentArrivalTimeByTransfers[transfer], true); } - private getFootpathsForDepartureStop( + private async getFootpathsForDepartureStop( connection: IConnection, currentArrivalTimeByTransfers: IArrivalTimeByTransfers, - ): void { + ): Promise { const depProfile: Profile[] = this.profilesByStop[connection.departureStop]; const earliestProfileEntry = depProfile[depProfile.length - 1]; const minVectorTimes = minVector(currentArrivalTimeByTransfers, earliestProfileEntry.arrivalTimes); - // For all footpaths with f_arr_stop = c_dep_stop - // TODO getInterstopDistance - const ISDs = [{ stop1: connection.departureStop, stop2: connection.departureStop, duration: 60000 }]; - ISDs.forEach((ISD) => { - // stop is f_dep_stop, the stop of the ISD that is _not_ connection.dep.stop - let stop = ISD.stop1; - if (stop === connection.departureStop) { - stop = ISD.stop2; - } + const departureStop = await this.locationResolver.resolve(connection.departureStop); + const reachableStops = await this.reachableStopsFinder.findReachableStops( + departureStop as IStop, + this.maximumDuration, + this.query.minimumWalkingSpeed, + ); + reachableStops.forEach((reachableStop) => { + const duration = reachableStop[1] * 3600000; // TODO use util function // Incorporate (c_dep_time - f_dur, t_c) into profile of S[f_dep_stop] - this.incorporateInProfile(connection, ISD.duration, stop, minVectorTimes); + this.incorporateInProfile(connection, duration, reachableStop[0], minVectorTimes); }); } private incorporateInProfile( connection: IConnection, duration: number, - stop: string, + stop: IStop, minVectorTimes: IArrivalTimeByTransfers, ) { - let profilesByDepartureStop = this.profilesByStop[stop]; + let profilesByDepartureStop = this.profilesByStop[stop.id]; if (profilesByDepartureStop === undefined) { - profilesByDepartureStop = this.profilesByStop[stop] = [new Profile(this.maxLegs)]; + profilesByDepartureStop = this.profilesByStop[stop.id] = [new Profile(this.maxLegs)]; } const earliestDepTimeProfile = profilesByDepartureStop[profilesByDepartureStop.length - 1]; diff --git a/src/planner/stops/ReachableStopsFinderBirdsEye.ts b/src/planner/stops/ReachableStopsFinderBirdsEye.ts index 1f855c49..ec375c99 100644 --- a/src/planner/stops/ReachableStopsFinderBirdsEye.ts +++ b/src/planner/stops/ReachableStopsFinderBirdsEye.ts @@ -1,10 +1,11 @@ -import { inject } from "inversify"; +import { inject, injectable } from "inversify"; import IStop from "../../fetcher/stops/IStop"; import IStopsFetcherMediator from "../../fetcher/stops/IStopsFetcherMediator"; import TYPES from "../../types"; import Geo from "../../util/Geo"; import IReachableStopsFinder, { IReachableStop } from "./IReachableStopsFinder"; +@injectable() export default class ReachableStopsFinderBirdsEye implements IReachableStopsFinder { private readonly stopsFetcherMediator: IStopsFetcherMediator; diff --git a/src/types.ts b/src/types.ts index 50086179..4fea41ba 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,6 +7,7 @@ const TYPES = { StopsFetcherMediator: Symbol("StopsFetcherMediator"), PublicTransportPlanner: Symbol("PublicTransportPlanner"), RoadPlanner: Symbol("RoadPlanner"), + ReachableStopsFinder: Symbol("ReachableStopsFinder"), }; export default TYPES; From b65d3a1cb606f502ea771bda5a751df558005d96 Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Mon, 5 Nov 2018 15:49:39 +0100 Subject: [PATCH 05/97] 1. Journey extraction: check best arrivalTime for all from-to pairs. --- .../public-transport/CSA/JourneyExtractor.ts | 24 +++++++++++++++---- .../public-transport/CSA/util/vectors.ts | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index f31f2774..bc238f31 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -10,10 +10,10 @@ import Profile from "./data-structure/Profile"; import { filterInfinity } from "./util/vectors"; export default class JourneyExtractor { - public readonly roadPlanner: IRoadPlanner; - public readonly locationResolver: ILocationResolver; + private readonly roadPlanner: IRoadPlanner; + private readonly locationResolver: ILocationResolver; - private bestArrivalTime: number = Infinity; + private bestArrivalTime: number[][] = []; constructor(roadPlanner: IRoadPlanner, locationResolver: ILocationResolver) { this.roadPlanner = roadPlanner; @@ -34,7 +34,9 @@ export default class JourneyExtractor { for (let transfers = 0; transfers < profile.arrivalTimes.length; transfers++) { for (const arrivalStop of query.to) { - if (profile.arrivalTimes[transfers] < this.bestArrivalTime) { // TODO check for all from - to pairs + if (this.checkBestArrivalTime(profile, transfers, departureStop, arrivalStop)) { + this.setBestArrivalTime(departureStop, arrivalStop, profile.arrivalTimes[transfers]); + const journey = await this.extractJourney( arrivalStop, profile, @@ -55,6 +57,19 @@ export default class JourneyExtractor { return journeys; } + private checkBestArrivalTime(profile: Profile, transfers: number, departureStop: ILocation, arrivalStop: ILocation) { + return profile.arrivalTimes[transfers] < Infinity && + (!this.bestArrivalTime[departureStop.id] || !this.bestArrivalTime[departureStop.id][arrivalStop.id] || + profile.arrivalTimes[transfers] < this.bestArrivalTime[departureStop.id][arrivalStop.id]); + } + + private setBestArrivalTime(departureStop: ILocation, arrivalStop: ILocation, arrivalTime: number) { + if (!this.bestArrivalTime[departureStop.id]) { + this.bestArrivalTime[departureStop.id] = []; + } + this.bestArrivalTime[departureStop.id][arrivalStop.id] = arrivalTime; + } + private async extractJourney( arrivalStop: ILocation, profile: Profile, @@ -64,7 +79,6 @@ export default class JourneyExtractor { ): Promise { // Extract journey for amount of transfers const journey: IPath = this.createJourney(profile, transfers); - this.bestArrivalTime = profile.arrivalTimes[transfers]; let currentEntry = profile; let remainingTransfers = transfers; diff --git a/src/planner/public-transport/CSA/util/vectors.ts b/src/planner/public-transport/CSA/util/vectors.ts index e68798d4..529d505f 100644 --- a/src/planner/public-transport/CSA/util/vectors.ts +++ b/src/planner/public-transport/CSA/util/vectors.ts @@ -1,6 +1,6 @@ import IConnection from "../../../../fetcher/connections/IConnection"; -import IProfilesByStop from "../data-structure/IProfilesByStop"; import IArrivalTimeByTransfers from "../data-structure/IArrivalTimeByTransfers"; +import IProfilesByStop from "../data-structure/IProfilesByStop"; /** * Shift a number vector to the right, From cfca63ad284f76be0d67c18d0fa88efa4331770f Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Mon, 5 Nov 2018 15:53:52 +0100 Subject: [PATCH 06/97] Implement dumb ReachableStopsFinderRoadPlanner (uses road planner to find reachable stops) --- src/fetcher/stops/proxy/StopsFetcherProxy.ts | 11 +-- .../ReachableStopsFinderRoadPlanner.test.ts | 22 ++++++ .../stops/ReachableStopsFinderRoadPlanner.ts | 72 +++++++++++++++++++ src/query-runner/ILocationResolver.ts | 3 +- src/query-runner/LocationResolverDefault.ts | 2 +- 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts diff --git a/src/fetcher/stops/proxy/StopsFetcherProxy.ts b/src/fetcher/stops/proxy/StopsFetcherProxy.ts index 39cad306..515be793 100644 --- a/src/fetcher/stops/proxy/StopsFetcherProxy.ts +++ b/src/fetcher/stops/proxy/StopsFetcherProxy.ts @@ -24,9 +24,7 @@ export default class StopsFetcherProxy implements IStopsFetcherMediator { public async getAllStops(): Promise { return Promise.all(this.stopsFetchers - .map((stopsFetcher: IStopsFetcher) => - stopsFetcher.getAllStops(), - ), + .map((stopsFetcher: IStopsFetcher) => stopsFetcher.getAllStops()), ).then((results: IStop[][]) => results.flat()); } @@ -35,10 +33,7 @@ export default class StopsFetcherProxy implements IStopsFetcherMediator { return null; } - for (const fetcher of this.stopsFetchers) { - if (stopId.indexOf(fetcher.prefix) === 0) { - return fetcher; - } - } + return this.stopsFetchers + .find((fetcher) => stopId.indexOf(fetcher.prefix) === 0); } } diff --git a/src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts b/src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts new file mode 100644 index 00000000..9370ada1 --- /dev/null +++ b/src/planner/stops/ReachableStopsFinderRoadPlanner.test.ts @@ -0,0 +1,22 @@ +import "jest"; +import StopsFetcherNMBS from "../../fetcher/stops/ld-fetch/StopsFetcherNMBS"; +import RoadPlannerBirdsEye from "../road/RoadPlannerBirdsEye"; +import ReachableStopsFinderRoadPlanner from "./ReachableStopsFinderRoadPlanner"; + +const stopsFetcher = new StopsFetcherNMBS(); +const roadPlanner = new RoadPlannerBirdsEye(); +const reachableStopsFinder = new ReachableStopsFinderRoadPlanner(stopsFetcher, roadPlanner); + +test("[ReachableStopsFinderRoadPlanner] reachable stops", async () => { + + const sourceStop = await stopsFetcher.getStopById("http://irail.be/stations/NMBS/008896008"); + + expect(sourceStop).toBeDefined(); + + // Get reachable stops in 50 km (10h at 5km/h) + const reachableStops = await reachableStopsFinder.findReachableStops(sourceStop, 10, 5); + + console.log(reachableStops); + + expect(reachableStops.length).toBeGreaterThan(1); +}); diff --git a/src/planner/stops/ReachableStopsFinderRoadPlanner.ts b/src/planner/stops/ReachableStopsFinderRoadPlanner.ts index e69de29b..c8082c3f 100644 --- a/src/planner/stops/ReachableStopsFinderRoadPlanner.ts +++ b/src/planner/stops/ReachableStopsFinderRoadPlanner.ts @@ -0,0 +1,72 @@ +import { inject } from "inversify"; +import IStop from "../../fetcher/stops/IStop"; +import IStopsFetcherMediator from "../../fetcher/stops/IStopsFetcherMediator"; +import ILocation from "../../interfaces/ILocation"; +import IPath from "../../interfaces/IPath"; +import IResolvedQuery from "../../query-runner/IResolvedQuery"; +import TYPES from "../../types"; +import IRoadPlanner from "../road/IRoadPlanner"; +import IReachableStopsFinder, { IReachableStop } from "./IReachableStopsFinder"; + +export default class ReachableStopsFinderRoadPlanner implements IReachableStopsFinder { + private readonly stopsFetcherMediator: IStopsFetcherMediator; + private readonly roadPlanner: IRoadPlanner; + + constructor( + @inject(TYPES.StopsFetcherMediator) stopsFetcherMediator: IStopsFetcherMediator, + @inject(TYPES.RoadPlanner) roadPlanner: IRoadPlanner, + ) { + this.stopsFetcherMediator = stopsFetcherMediator; + this.roadPlanner = roadPlanner; + } + + public async findReachableStops( + source: IStop, + maximumDuration: number, + minimumSpeed: number): Promise { + + const minimumDepartureTime = new Date(); + const maximumArrivalTime = new Date(minimumDepartureTime.getTime() + maximumDuration); + + const baseQuery: IResolvedQuery = { + from: [source as ILocation], + minimumDepartureTime, + maximumArrivalTime, + minimumWalkingSpeed: minimumSpeed, + }; + + const allStops = await this.stopsFetcherMediator.getAllStops(); + const reachableStops: IReachableStop[] = [[source, 0]]; + + await Promise.all(allStops.map(async (possibleTarget: IStop) => { + if (possibleTarget.id !== source.id) { + + const query = Object.assign({}, baseQuery, { to: [possibleTarget as ILocation] }); + const paths: IPath[] = await this.roadPlanner.plan(query); + + if (paths.length) { + + // tslint:disable-next-line:no-shadowed-variable + const shortestDuration = paths.reduce((shortestDuration: number, path: IPath) => { + // Minimum speed is passed so sum max duration over all steps + const duration = path.steps + .reduce((totalDuration: number, step) => totalDuration + step.duration.maximum, 0); + + if (duration < shortestDuration) { + return duration; + } + + return shortestDuration; + }, Number.POSITIVE_INFINITY); + + if (shortestDuration < maximumDuration) { + reachableStops.push([possibleTarget, shortestDuration]); + } + } + } + + })); + + return reachableStops; + } +} diff --git a/src/query-runner/ILocationResolver.ts b/src/query-runner/ILocationResolver.ts index a2221930..a4f4f93c 100644 --- a/src/query-runner/ILocationResolver.ts +++ b/src/query-runner/ILocationResolver.ts @@ -1,5 +1,6 @@ +import IStop from "../fetcher/stops/IStop"; import ILocation from "../interfaces/ILocation"; export default interface ILocationResolver { - resolve: (location: ILocation | string) => Promise; + resolve: (location: ILocation | IStop | string) => Promise; } diff --git a/src/query-runner/LocationResolverDefault.ts b/src/query-runner/LocationResolverDefault.ts index 78f08839..6be743ba 100644 --- a/src/query-runner/LocationResolverDefault.ts +++ b/src/query-runner/LocationResolverDefault.ts @@ -15,7 +15,7 @@ export default class LocationResolverDefault implements ILocationResolver { this.stopsFetcherMediator = stopsFetcherMediator; } - public async resolve(input: ILocation | string): Promise { + public async resolve(input: ILocation | IStop | string): Promise { if (typeof input === "string") { From da4e4c4ef84088140e4ed15ed2ea3bd8d08406f9 Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Mon, 5 Nov 2018 17:14:07 +0100 Subject: [PATCH 07/97] 1. added Unit types. 2. added Path and Step implementation. --- src/fetcher/connections/IConnection.ts | 6 ++- .../ld-fetch/ConnectionsIteratorLDFetch.ts | 5 ++ src/interfaces/IPath.ts | 2 +- src/interfaces/IProbabilisticValue.ts | 12 ++--- src/interfaces/IQuery.ts | 7 +-- src/interfaces/IStep.ts | 5 +- src/interfaces/units.ts | 6 +++ src/planner/IJourney.ts | 3 -- src/planner/Path.ts | 30 +++++++++++ src/planner/Step.ts | 46 ++++++++++++++++ .../public-transport/CSA/JourneyExtractor.ts | 52 +++++-------------- .../PublicTransportPlannerCSAProfile.ts | 17 +++--- src/planner/road/RoadPlannerBirdsEye.ts | 12 +++-- src/planner/stops/IReachableStopsFinder.ts | 12 ++++- .../ReachableStopsFinderBirdsEye.test.ts | 3 +- .../stops/ReachableStopsFinderBirdsEye.ts | 13 +++-- src/query-runner/IResolvedQuery.ts | 5 +- src/util/Geo.ts | 5 +- src/util/Units.test.ts | 49 +++++++++++++++++ src/util/Units.ts | 24 +++++++++ 20 files changed, 234 insertions(+), 80 deletions(-) create mode 100644 src/interfaces/units.ts delete mode 100644 src/planner/IJourney.ts create mode 100644 src/planner/Path.ts create mode 100644 src/planner/Step.ts create mode 100644 src/util/Units.test.ts create mode 100644 src/util/Units.ts diff --git a/src/fetcher/connections/IConnection.ts b/src/fetcher/connections/IConnection.ts index 361bebb7..bb8fe4ef 100644 --- a/src/fetcher/connections/IConnection.ts +++ b/src/fetcher/connections/IConnection.ts @@ -1,13 +1,15 @@ +import { DurationMs } from "../../interfaces/units"; + export default interface IConnection { id: string; arrivalTime: Date; arrivalStop: string; - arrivalDelay: number; + arrivalDelay: DurationMs; departureTime: Date; departureStop: string; - departureDelay: number; + departureDelay: DurationMs; "gtfs:trip": string; } diff --git a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts index 0f45d1a7..6fa8e577 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts @@ -5,6 +5,7 @@ import UriTemplate from "uritemplate"; import { matchesTriple, transformPredicate } from "../../helpers"; import IConnection from "../IConnection"; import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig"; +import Units from "../../../util/Units"; interface IEntity { } @@ -123,6 +124,10 @@ export default class ConnectionsIteratorLDFetch implements AsyncIterator { + median?: T; + average?: T; + minimum?: T; + maximum?: T; + percentiles?: {[percentile: string]: T}; } diff --git a/src/interfaces/IQuery.ts b/src/interfaces/IQuery.ts index a0fc5665..c254048a 100644 --- a/src/interfaces/IQuery.ts +++ b/src/interfaces/IQuery.ts @@ -1,4 +1,5 @@ import ILocation from "./ILocation"; +import { SpeedkmH } from "./units"; export default interface IQuery { from?: string | string[] | ILocation | ILocation[]; @@ -7,7 +8,7 @@ export default interface IQuery { maximumArrivalTime?: Date; roadOnly?: boolean; publicTransportOnly?: boolean; - walkingSpeed?: number; - minimumWalkingSpeed?: number; - maximumWalkingSpeed?: number; + walkingSpeed?: SpeedkmH; + minimumWalkingSpeed?: SpeedkmH; + maximumWalkingSpeed?: SpeedkmH; } diff --git a/src/interfaces/IStep.ts b/src/interfaces/IStep.ts index e952474f..884d8711 100644 --- a/src/interfaces/IStep.ts +++ b/src/interfaces/IStep.ts @@ -1,11 +1,12 @@ import ILocation from "./ILocation"; import IProbabilisticValue from "./IProbabilisticValue"; +import { DistanceM, DurationMs } from "./units"; export default interface IStep { startLocation: ILocation; stopLocation: ILocation; startTime?: Date; stopTime?: Date; - duration: IProbabilisticValue; - distance?: number; + duration: IProbabilisticValue; + distance?: DistanceM; } diff --git a/src/interfaces/units.ts b/src/interfaces/units.ts new file mode 100644 index 00000000..23f52d27 --- /dev/null +++ b/src/interfaces/units.ts @@ -0,0 +1,6 @@ +// duration ms +export type DurationMs = number; +// distance in m +export type DistanceM = number; +// speed in km/h +export type SpeedkmH = number; diff --git a/src/planner/IJourney.ts b/src/planner/IJourney.ts deleted file mode 100644 index 9a43fab4..00000000 --- a/src/planner/IJourney.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default interface IJourney { - distance: number; -} diff --git a/src/planner/Path.ts b/src/planner/Path.ts new file mode 100644 index 00000000..44a9ae42 --- /dev/null +++ b/src/planner/Path.ts @@ -0,0 +1,30 @@ +import IPath from "../interfaces/IPath"; +import IStep from "../interfaces/IStep"; + +export default class Path implements IPath { + + public static createFromProfile(profile, transfers): Path { + return new Path( + [], + new Date(profile.arrivalTimes[transfers]), + new Date(profile.departureTime), + transfers, + ); + } + + public arrivalTime: Date; + public departureTime: Date; + public steps: IStep[]; + public transfers: number; + + constructor(steps: IStep[], arrivalTime?: Date, departureTime?: Date, transfers?: number) { + this.arrivalTime = arrivalTime; + this.departureTime = departureTime; + this.steps = steps; + this.transfers = transfers; + } + + public addStep(step: IStep): void { + this.steps.push(step); + } +} diff --git a/src/planner/Step.ts b/src/planner/Step.ts new file mode 100644 index 00000000..1279064e --- /dev/null +++ b/src/planner/Step.ts @@ -0,0 +1,46 @@ +import IConnection from "../fetcher/connections/IConnection"; +import ILocation from "../interfaces/ILocation"; +import IProbabilisticValue from "../interfaces/IProbabilisticValue"; +import IStep from "../interfaces/IStep"; +import { DistanceM, DurationMs } from "../interfaces/units"; + +export default class Step implements IStep { + + public static createFromConnections(enterConnection: IConnection, exitConnection: IConnection): IStep { + return new Step( + {id: enterConnection.departureStop}, + {id: exitConnection.arrivalStop}, + { + minimum: ( + exitConnection.arrivalTime.getTime() - + enterConnection.departureTime.getTime() + ), + }, + enterConnection.departureTime, + exitConnection.arrivalTime, + ); + } + + public distance: DistanceM; + public duration: IProbabilisticValue; + public startLocation: ILocation; + public startTime: Date; + public stopLocation: ILocation; + public stopTime: Date; + + constructor( + startLocation: ILocation, + stopLocation: ILocation, + duration: IProbabilisticValue, + startTime?: Date, + stopTime?: Date, + distance?: DistanceM, + ) { + this.distance = distance; + this.duration = duration; + this.startLocation = startLocation; + this.startTime = startTime; + this.stopLocation = stopLocation; + this.stopTime = stopTime; + } +} diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index bc238f31..07fbafaf 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -4,7 +4,9 @@ import IPath from "../../../interfaces/IPath"; import IStep from "../../../interfaces/IStep"; import ILocationResolver from "../../../query-runner/ILocationResolver"; import IResolvedQuery from "../../../query-runner/IResolvedQuery"; +import Path from "../../Path"; import IRoadPlanner from "../../road/IRoadPlanner"; +import Step from "../../Step"; import IProfilesByStop from "./data-structure/IProfilesByStop"; import Profile from "./data-structure/Profile"; import { filterInfinity } from "./util/vectors"; @@ -78,7 +80,7 @@ export default class JourneyExtractor { query: IResolvedQuery, ): Promise { // Extract journey for amount of transfers - const journey: IPath = this.createJourney(profile, transfers); + const path: Path = Path.createFromProfile(profile, transfers); let currentEntry = profile; let remainingTransfers = transfers; @@ -88,8 +90,8 @@ export default class JourneyExtractor { const enterConnection: IConnection = currentEntry.enterConnections[remainingTransfers]; const exitConnection: IConnection = currentEntry.exitConnections[remainingTransfers]; - const step: IStep = this.createStep(enterConnection, exitConnection); - journey.steps.push(step); + const step: IStep = Step.createFromConnections(enterConnection, exitConnection); + path.addStep(step); remainingTransfers--; if (remainingTransfers >= 0) { @@ -113,10 +115,10 @@ export default class JourneyExtractor { if (walkingResult && walkingResult[0] && walkingResult[0].steps[0] && connection.departureTime.getTime() >= step.stopTime.getTime() + - walkingResult[0].steps[0].duration.average + walkingResult[0].steps[0].duration.minimum ) { found = true; - journey.steps.push(walkingResult[0].steps[0]); + path.addStep(walkingResult[0].steps[0]); currentEntry = nextProfile[i]; } i--; @@ -124,43 +126,15 @@ export default class JourneyExtractor { } } - if (journey.steps[journey.steps.length - 1].stopLocation.id !== arrivalStop.id) { - await this.addFinalFootpath(journey, arrivalStop, query); + if (path.steps[path.steps.length - 1].stopLocation.id !== arrivalStop.id) { + await this.addFinalFootpath(path, arrivalStop, query); } - return journey; + return path as IPath; } - private createJourney(profile: Profile, transfers: number): IPath { - return { - departureTime: new Date(profile.departureTime), - arrivalTime: new Date(profile.arrivalTimes[transfers]), - transfers, - steps: [], - }; - } - - private createStep(enterConnection: IConnection, exitConnection: IConnection): IStep { - return { - startLocation: { - id: enterConnection.departureStop, - }, - stopLocation: { - id: exitConnection.arrivalStop, - }, - startTime: exitConnection.departureTime, - stopTime: exitConnection.arrivalTime, - duration: { - average: ( - exitConnection.arrivalTime.getTime() - - enterConnection.departureTime.getTime() - ), - }, - }; - } - - private async addFinalFootpath(journey: IPath, arrivalStop: ILocation, query: IResolvedQuery): Promise { - const lastStep = journey.steps[journey.steps.length - 1]; + 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); @@ -173,7 +147,7 @@ export default class JourneyExtractor { }); if (walkingResult && walkingResult[0] && walkingResult[0].steps[0]) { - journey.steps.push(walkingResult[0].steps[0]); + path.addStep(walkingResult[0].steps[0]); } } } diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts index 9bf008d5..7bb040b9 100644 --- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts @@ -3,9 +3,11 @@ import IConnection from "../../fetcher/connections/IConnection"; import IConnectionsFetcher from "../../fetcher/connections/IConnectionsFetcher"; import IStop from "../../fetcher/stops/IStop"; 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 Units from "../../util/Units"; import IRoadPlanner from "../road/IRoadPlanner"; import IReachableStopsFinder from "../stops/IReachableStopsFinder"; import EarliestArrival from "./CSA/data-structure/EarliestArrival"; @@ -29,10 +31,10 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor private profilesByStop: IProfilesByStop = {}; // S private earliestArrivalByTrip: IEarliestArrivalByTrip = {}; // T - private durationToTargetByStop: number[] = []; + private durationToTargetByStop: DurationMs[] = []; private maxLegs: number = 8; // TODO define max legs on query? - private maximumDuration: number = 0.4; // TODO define maximum duration (radius) on query? + private maximumDuration: DurationMs = Units.fromHours(0.4); // TODO define maximum duration (radius) on query? private query: IResolvedQuery; @@ -141,8 +143,8 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor this.query.minimumWalkingSpeed, ); - for (const stop of reachableStops) { - this.durationToTargetByStop[stop[0].id] = stop[1] * 3600000; + for (const reachableStop of reachableStops) { + this.durationToTargetByStop[reachableStop.stop.id] = reachableStop.duration * 3600000; } } } @@ -202,15 +204,14 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor ); reachableStops.forEach((reachableStop) => { - const duration = reachableStop[1] * 3600000; // TODO use util function // Incorporate (c_dep_time - f_dur, t_c) into profile of S[f_dep_stop] - this.incorporateInProfile(connection, duration, reachableStop[0], minVectorTimes); + this.incorporateInProfile(connection, reachableStop.duration, reachableStop.stop, minVectorTimes); }); } private incorporateInProfile( connection: IConnection, - duration: number, + duration: DurationMs, stop: IStop, minVectorTimes: IArrivalTimeByTransfers, ) { @@ -259,7 +260,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor exitConnections, }; - if (earliestDepTimeProfile.departureTime !== departureTime) { + if (earliestDepTimeProfile.departureTime !== departureTime) { // TODO check order before inserting. profilesByDepartureStop.push(newProfile); } else { profilesByDepartureStop[profilesByDepartureStop.length - 1] = newProfile; diff --git a/src/planner/road/RoadPlannerBirdsEye.ts b/src/planner/road/RoadPlannerBirdsEye.ts index 57fa3488..992ed36a 100644 --- a/src/planner/road/RoadPlannerBirdsEye.ts +++ b/src/planner/road/RoadPlannerBirdsEye.ts @@ -5,6 +5,8 @@ import IProbabilisticValue from "../../interfaces/IProbabilisticValue"; import IResolvedQuery from "../../query-runner/IResolvedQuery"; import Geo from "../../util/Geo"; import IRoadPlanner from "./IRoadPlanner"; +import { DurationMs, SpeedkmH } from "../../interfaces/units"; +import Units from "../../util/Units"; @injectable() export default class RoadPlannerBirdsEye implements IRoadPlanner { @@ -29,15 +31,15 @@ export default class RoadPlannerBirdsEye implements IRoadPlanner { private getPathBetweenLocations( from: ILocation, to: ILocation, - minWalkingSpeed: number, - maxWalkingSpeed: number, + minWalkingSpeed: SpeedkmH, + maxWalkingSpeed: SpeedkmH, ): IPath { const distance = Geo.getDistanceBetweenLocations(from, to); - const minDuration = distance / maxWalkingSpeed; - const maxDuration = distance / minWalkingSpeed; + const minDuration = Units.toDuration(distance, maxWalkingSpeed); + const maxDuration = Units.toDuration(distance, minWalkingSpeed); - const duration: IProbabilisticValue = { + const duration: IProbabilisticValue = { minimum: minDuration, maximum: maxDuration, average: (minDuration + maxDuration) / 2, diff --git a/src/planner/stops/IReachableStopsFinder.ts b/src/planner/stops/IReachableStopsFinder.ts index 751bb7d4..cb96ae3f 100644 --- a/src/planner/stops/IReachableStopsFinder.ts +++ b/src/planner/stops/IReachableStopsFinder.ts @@ -1,7 +1,15 @@ import IStop from "../../fetcher/stops/IStop"; +import { DurationMs, SpeedkmH } from "../../interfaces/units"; export default interface IReachableStopsFinder { - findReachableStops: (sourceStop: IStop, maximumDuration: number, minimumSpeed: number) => Promise; + findReachableStops: ( + sourceStop: IStop, + maximumDuration: DurationMs, + minimumSpeed: SpeedkmH, + ) => Promise; } -export type IReachableStop = [IStop, number]; +export interface IReachableStop { + stop: IStop; + duration: DurationMs; +} diff --git a/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts b/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts index 5d7fbb8a..67cf5980 100644 --- a/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts +++ b/src/planner/stops/ReachableStopsFinderBirdsEye.test.ts @@ -1,5 +1,6 @@ import "jest"; import StopsFetcherDeLijn from "../../fetcher/stops/ld-fetch/StopsFetcherDeLijn"; +import Units from "../../util/Units"; import ReachableStopsFinderBirdsEye from "./ReachableStopsFinderBirdsEye"; const stopsFetcher = new StopsFetcherDeLijn(); @@ -12,7 +13,7 @@ test("[ReachableStopsFinderBirdsEye] reachable stops", async () => { expect(sourceStop).toBeDefined(); // Get reachable stops in 1 km (.25h at 4km/h) - const reachableStops = await reachableStopsFinder.findReachableStops(sourceStop, .25, 4); + const reachableStops = await reachableStopsFinder.findReachableStops(sourceStop, Units.fromHours(0.25), 4); expect(reachableStops.length).toBeGreaterThan(1); }); diff --git a/src/planner/stops/ReachableStopsFinderBirdsEye.ts b/src/planner/stops/ReachableStopsFinderBirdsEye.ts index ec375c99..d0294a78 100644 --- a/src/planner/stops/ReachableStopsFinderBirdsEye.ts +++ b/src/planner/stops/ReachableStopsFinderBirdsEye.ts @@ -1,8 +1,10 @@ import { inject, injectable } from "inversify"; import IStop from "../../fetcher/stops/IStop"; import IStopsFetcherMediator from "../../fetcher/stops/IStopsFetcherMediator"; +import { DurationMs, SpeedkmH } from "../../interfaces/units"; import TYPES from "../../types"; import Geo from "../../util/Geo"; +import Units from "../../util/Units"; import IReachableStopsFinder, { IReachableStop } from "./IReachableStopsFinder"; @injectable() @@ -17,21 +19,22 @@ export default class ReachableStopsFinderBirdsEye implements IReachableStopsFind public async findReachableStops( source: IStop, - maximumDuration: number, - minimumSpeed: number): Promise { + maximumDuration: DurationMs, + minimumSpeed: SpeedkmH, + ): Promise { const allStops = await this.stopsFetcherMediator.getAllStops(); return allStops.map((possibleTarget: IStop): IReachableStop => { if (possibleTarget.id === source.id) { - return [source, 0]; + return {stop: source, duration: 0}; } const distance = Geo.getDistanceBetweenStops(source, possibleTarget); - const duration = distance / minimumSpeed; + const duration = Units.toDuration(distance, minimumSpeed); if (duration <= maximumDuration) { - return [possibleTarget, duration]; + return {stop: possibleTarget, duration}; } }).filter((reachableStop) => !!reachableStop); } diff --git a/src/query-runner/IResolvedQuery.ts b/src/query-runner/IResolvedQuery.ts index 12b6f72a..478faf96 100644 --- a/src/query-runner/IResolvedQuery.ts +++ b/src/query-runner/IResolvedQuery.ts @@ -1,4 +1,5 @@ import ILocation from "../interfaces/ILocation"; +import { SpeedkmH } from "../interfaces/units"; export default interface IResolvedQuery { from?: ILocation[]; @@ -7,6 +8,6 @@ export default interface IResolvedQuery { maximumArrivalTime?: Date; roadOnly?: boolean; publicTransportOnly?: boolean; - minimumWalkingSpeed?: number; - maximumWalkingSpeed?: number; + minimumWalkingSpeed?: SpeedkmH; + maximumWalkingSpeed?: SpeedkmH; } diff --git a/src/util/Geo.ts b/src/util/Geo.ts index 25ee04ef..583cd194 100644 --- a/src/util/Geo.ts +++ b/src/util/Geo.ts @@ -1,9 +1,10 @@ import haversine from "haversine"; import IStop from "../fetcher/stops/IStop"; import ILocation from "../interfaces/ILocation"; +import { DistanceM } from "../interfaces/units"; export default class Geo { - public static getDistanceBetweenLocations(start: ILocation, stop: ILocation): number { + public static getDistanceBetweenLocations(start: ILocation, stop: ILocation): DistanceM { const { longitude: depLongitude, latitude: depLatitude } = start; const { longitude: arrLongitude, latitude: arrLatitude } = stop; @@ -18,6 +19,8 @@ export default class Geo { }, { latitude: arrLatitude, longitude: arrLongitude, + }, { + unit: "meter", }); } diff --git a/src/util/Units.test.ts b/src/util/Units.test.ts new file mode 100644 index 00000000..fc8ee6d1 --- /dev/null +++ b/src/util/Units.test.ts @@ -0,0 +1,49 @@ +import "jest"; +import { DistanceM, DurationMs, SpeedkmH } from "../interfaces/units"; +import Units from "./Units"; + +test("[Units] toSpeed", () => { + + const distance: DistanceM = 10000; + const duration: DurationMs = 3600000; + const speed: SpeedkmH = Units.toSpeed(distance, duration); + + expect(speed).toBeDefined(); + expect(speed).toEqual(10); +}); + +test("[Units] toDuration", () => { + + const distance: DistanceM = 10000; + const speed: SpeedkmH = 10; + const duration: DurationMs = Units.toDuration(distance, speed); + + expect(duration).toBeDefined(); + expect(duration).toEqual(3600000); +}); + +test("[Units] toDistance", () => { + + const duration: DurationMs = 3600000; + const speed: SpeedkmH = 10; + const distance: DistanceM = Units.toDistance(speed, duration); + + expect(distance).toBeDefined(); + expect(distance).toEqual(10000); +}); + +test("[Units] fromHour", () => { + + const duration: DurationMs = Units.fromHours(1); + + expect(duration).toBeDefined(); + expect(duration).toEqual(3600000); +}); + +test("[Units] fromSeconds", () => { + + const duration: DurationMs = Units.fromSeconds(2); + + expect(duration).toBeDefined(); + expect(duration).toEqual(2000); +}); diff --git a/src/util/Units.ts b/src/util/Units.ts new file mode 100644 index 00000000..87e57752 --- /dev/null +++ b/src/util/Units.ts @@ -0,0 +1,24 @@ +import { DistanceM, DurationMs, SpeedkmH } from "../interfaces/units"; + +export default class Units { + public static toSpeed(distance: DistanceM, duration: DurationMs): SpeedkmH { + return (distance / duration) * 3600; + } + + public static toDistance(duration: DurationMs, speed: SpeedkmH): DistanceM { + return (speed * duration) / 3600; + } + + public static toDuration(distance: DistanceM, speed: SpeedkmH): DurationMs { + return (distance / speed) * 3600; + } + + public static fromHours(hours: number): DurationMs { + return hours * 3600000; + } + + public static fromSeconds(seconds: number): DurationMs { + return seconds * 1000; + } + +} From 04d4a07f3f2c0aea8ec3434e86a0a0c55ed2e4ac Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Tue, 6 Nov 2018 09:21:54 +0100 Subject: [PATCH 08/97] 1. added vector util. --- .../public-transport/CSA/JourneyExtractor.ts | 4 +- .../public-transport/CSA/util/ProfileUtil.ts | 30 +++++++++ .../public-transport/CSA/util/vectors.ts | 63 ------------------- .../PublicTransportPlannerCSAProfile.ts | 11 ++-- src/util/Vectors.ts | 24 +++++++ 5 files changed, 62 insertions(+), 70 deletions(-) create mode 100644 src/planner/public-transport/CSA/util/ProfileUtil.ts delete mode 100644 src/planner/public-transport/CSA/util/vectors.ts create mode 100644 src/util/Vectors.ts diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index 07fbafaf..87e8fb99 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -9,7 +9,7 @@ import IRoadPlanner from "../../road/IRoadPlanner"; import Step from "../../Step"; import IProfilesByStop from "./data-structure/IProfilesByStop"; import Profile from "./data-structure/Profile"; -import { filterInfinity } from "./util/vectors"; +import ProfileUtil from "./util/ProfileUtil"; export default class JourneyExtractor { private readonly roadPlanner: IRoadPlanner; @@ -26,7 +26,7 @@ export default class JourneyExtractor { profilesByStop: IProfilesByStop, query: IResolvedQuery, ): Promise { - const filteredProfilesByStop: IProfilesByStop = filterInfinity(profilesByStop); + const filteredProfilesByStop: IProfilesByStop = ProfileUtil.filterInfinity(profilesByStop); const journeys = []; for (const departureStop of query.from) { diff --git a/src/planner/public-transport/CSA/util/ProfileUtil.ts b/src/planner/public-transport/CSA/util/ProfileUtil.ts new file mode 100644 index 00000000..22917092 --- /dev/null +++ b/src/planner/public-transport/CSA/util/ProfileUtil.ts @@ -0,0 +1,30 @@ +import IConnection from "../../../../fetcher/connections/IConnection"; +import IProfilesByStop from "../data-structure/IProfilesByStop"; + +export default class ProfileUtil { + + public static filterInfinity(profilesByStop: IProfilesByStop): IProfilesByStop { + const result = {}; + + for (const stop in profilesByStop) { + if (profilesByStop.hasOwnProperty(stop)) { + result[stop] = profilesByStop[stop].filter((profile) => + profile.departureTime !== Infinity, + ); + } + } + return result; + } + + public static evalProfile(profilesByStop: IProfilesByStop, connection: IConnection, maxLegs) { + const { arrivalStop, arrivalTime } = connection; + let i = profilesByStop[arrivalStop].length - 1; + while (i >= 0) { + if (profilesByStop[arrivalStop][i].departureTime >= arrivalTime.getTime()) { + return profilesByStop[arrivalStop][i].arrivalTimes.slice(); // Return a copy of the array + } + i--; + } + return Array(maxLegs).fill(Infinity); + } +} diff --git a/src/planner/public-transport/CSA/util/vectors.ts b/src/planner/public-transport/CSA/util/vectors.ts deleted file mode 100644 index 529d505f..00000000 --- a/src/planner/public-transport/CSA/util/vectors.ts +++ /dev/null @@ -1,63 +0,0 @@ -import IConnection from "../../../../fetcher/connections/IConnection"; -import IArrivalTimeByTransfers from "../data-structure/IArrivalTimeByTransfers"; -import IProfilesByStop from "../data-structure/IProfilesByStop"; - -/** - * Shift a number vector to the right, - * inserting Infinity on the left side and discarding - * the last number on the right side - * @param vector: [int] - */ - -export function shiftVector(vector: IArrivalTimeByTransfers): IArrivalTimeByTransfers { - vector.unshift(Infinity); - vector.pop(); - return vector; -} - -/** - * Calculate component-wise minimum of an array of vectors - * eg minVector([[3,8,9],[4,4,4],[5,5,1]]) = [3,4,1] - * @param vectors: [[int]] - * @returns {Array} - */ -export function minVector(...vectors: IArrivalTimeByTransfers[]): IArrivalTimeByTransfers { - if (!vectors || !vectors[0]) { - return []; - } - - const result: IArrivalTimeByTransfers = vectors[0]; - for (let vectorIndex = 1 ; vectorIndex < vectors.length ; vectorIndex++) { - for (let numberIndex = 0 ; numberIndex < vectors[vectorIndex].length ; numberIndex++) { - if (vectors[vectorIndex][numberIndex] < result[numberIndex]) { - result[numberIndex] = vectors[vectorIndex][numberIndex]; - } - } - } - return result; -} - -export function filterInfinity(profilesByStop: IProfilesByStop): IProfilesByStop { - const result = {}; - - for (const stop in profilesByStop) { - if (profilesByStop.hasOwnProperty(stop)) { - result[stop] = profilesByStop[stop].filter((profile) => - profile.departureTime !== Infinity, - ); - } - } - return result; -} - -export function evalProfile(profilesByStop: IProfilesByStop, connection: IConnection, maxLegs) { - const {arrivalStop, arrivalTime} = connection; - let i = profilesByStop[arrivalStop].length - 1; - while (i >= 0) { - if (profilesByStop[arrivalStop][i].departureTime >= arrivalTime.getTime()) { - return profilesByStop[arrivalStop][i].arrivalTimes.slice(); // Return a copy of the array - } - i--; - } - return Array(maxLegs).fill(Infinity); -} diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts index 7bb040b9..4a4f427e 100644 --- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts @@ -8,16 +8,17 @@ import ILocationResolver from "../../query-runner/ILocationResolver"; import IResolvedQuery from "../../query-runner/IResolvedQuery"; import TYPES from "../../types"; import Units from "../../util/Units"; +import Vectors from "../../util/Vectors"; import IRoadPlanner from "../road/IRoadPlanner"; import IReachableStopsFinder from "../stops/IReachableStopsFinder"; -import EarliestArrival from "./CSA/data-structure/EarliestArrival"; import IEarliestArrival from "./CSA/data-structure/EarliestArrival"; +import EarliestArrival from "./CSA/data-structure/EarliestArrival"; import IArrivalTimeByTransfers from "./CSA/data-structure/IArrivalTimeByTransfers"; import IEarliestArrivalByTrip from "./CSA/data-structure/IEarliestArrivalByTrip"; import IProfilesByStop from "./CSA/data-structure/IProfilesByStop"; import Profile from "./CSA/data-structure/Profile"; import JourneyExtractor from "./CSA/JourneyExtractor"; -import { evalProfile, minVector, shiftVector } from "./CSA/util/vectors"; +import ProfileUtil from "./CSA/util/ProfileUtil"; import IPublicTransportPlanner from "./IPublicTransportPlanner"; @injectable() @@ -132,7 +133,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor const t2 = this.remainSeated(connection); const t3 = this.takeTransfer(connection); - return minVector(t1, t2, t3); + return Vectors.minVector(t1, t2, t3); } private async initDurationToTargetByStop(): Promise { @@ -164,7 +165,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor } private takeTransfer(connection: IConnection): IArrivalTimeByTransfers { - return shiftVector(evalProfile(this.profilesByStop, connection, this.maxLegs)); + return Vectors.shiftVector(ProfileUtil.evalProfile(this.profilesByStop, connection, this.maxLegs)); } private updateEarliestArrivalByTrip( @@ -194,7 +195,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor ): Promise { const depProfile: Profile[] = this.profilesByStop[connection.departureStop]; const earliestProfileEntry = depProfile[depProfile.length - 1]; - const minVectorTimes = minVector(currentArrivalTimeByTransfers, earliestProfileEntry.arrivalTimes); + const minVectorTimes = Vectors.minVector(currentArrivalTimeByTransfers, earliestProfileEntry.arrivalTimes); const departureStop = await this.locationResolver.resolve(connection.departureStop); const reachableStops = await this.reachableStopsFinder.findReachableStops( diff --git a/src/util/Vectors.ts b/src/util/Vectors.ts new file mode 100644 index 00000000..3fe608cf --- /dev/null +++ b/src/util/Vectors.ts @@ -0,0 +1,24 @@ +export default class Vectors { + + public static shiftVector(vector: T): T { + vector.unshift(Infinity); + vector.pop(); + return vector; + } + + public static minVector(...vectors: T[]): T { + if (!vectors || !vectors[0]) { + return; + } + + const result: T = vectors[0]; + for (let vectorIndex = 1 ; vectorIndex < vectors.length ; vectorIndex++) { + for (let numberIndex = 0 ; numberIndex < vectors[vectorIndex].length ; numberIndex++) { + if (vectors[vectorIndex][numberIndex] < result[numberIndex]) { + result[numberIndex] = vectors[vectorIndex][numberIndex]; + } + } + } + return result; + } +} From d1415a2b3534441b373796b7a6b56a850dd9ac75 Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Tue, 6 Nov 2018 10:07:10 +0100 Subject: [PATCH 09/97] 1. hotfix csa + check departure order before inserting profile. --- .../PublicTransportPlannerCSAProfile.ts | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts index 4a4f427e..b975a78c 100644 --- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts @@ -69,19 +69,19 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor let upperBoundDate; let lowerBoundDate; - if (this.query.maximumArrivalTime) { - upperBoundDate = this.query.maximumArrivalTime; - } else { - upperBoundDate = new Date(); - upperBoundDate.setHours(upperBoundDate.getHours() + 2); - } - if (this.query.minimumDepartureTime) { lowerBoundDate = this.query.minimumDepartureTime; } else { lowerBoundDate = new Date(); } + if (this.query.maximumArrivalTime) { + upperBoundDate = this.query.maximumArrivalTime; + } else { + upperBoundDate = lowerBoundDate; + upperBoundDate.setHours(upperBoundDate.getHours() + 2); + } + this.query.maximumArrivalTime = upperBoundDate; this.query.minimumDepartureTime = lowerBoundDate; @@ -145,7 +145,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor ); for (const reachableStop of reachableStops) { - this.durationToTargetByStop[reachableStop.stop.id] = reachableStop.duration * 3600000; + this.durationToTargetByStop[reachableStop.stop.id] = reachableStop.duration; } } } @@ -165,7 +165,9 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor } private takeTransfer(connection: IConnection): IArrivalTimeByTransfers { - return Vectors.shiftVector(ProfileUtil.evalProfile(this.profilesByStop, connection, this.maxLegs)); + return Vectors.shiftVector( + ProfileUtil.evalProfile(this.profilesByStop, connection, this.maxLegs), + ); } private updateEarliestArrivalByTrip( @@ -253,7 +255,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor // If the new departure time is equal, update the profile entry // Else, insert a new entry - const departureTime = connection.departureTime.getTime() - duration; // Calculate c_dep_time - f_dur + const departureTime = connection.departureTime.getTime() - duration; const newProfile = { departureTime, arrivalTimes: minVectorTimes, @@ -261,11 +263,14 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor exitConnections, }; - if (earliestDepTimeProfile.departureTime !== departureTime) { // TODO check order before inserting. - profilesByDepartureStop.push(newProfile); - } else { - profilesByDepartureStop[profilesByDepartureStop.length - 1] = newProfile; + let i = profilesByDepartureStop.length - 1; + let earliestProfile = profilesByDepartureStop[i]; + while (i > 0 && earliestProfile.departureTime < departureTime) { + profilesByDepartureStop[i + 1] = earliestProfile; + i--; + earliestProfile = profilesByDepartureStop[i]; } + profilesByDepartureStop[i + 1] = newProfile; } } From 3ab0ccfb449ebcacac793e65f87d3ffebf9945e9 Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Tue, 6 Nov 2018 10:19:44 +0100 Subject: [PATCH 10/97] 1. hotfix csa upperBoundDate. --- .../public-transport/PublicTransportPlannerCSAProfile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts index b975a78c..230ef15c 100644 --- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts @@ -78,7 +78,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor if (this.query.maximumArrivalTime) { upperBoundDate = this.query.maximumArrivalTime; } else { - upperBoundDate = lowerBoundDate; + upperBoundDate = new Date(lowerBoundDate); upperBoundDate.setHours(upperBoundDate.getHours() + 2); } From aea4c1aa14be7e0815865b5d333f5da20bf15117 Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Tue, 6 Nov 2018 10:36:03 +0100 Subject: [PATCH 11/97] Added maximumTransferDuration and maximumLegs to IQuery --- src/Constants.ts | 4 +++ src/interfaces/IQuery.ts | 4 ++- .../PublicTransportPlannerCSAProfile.ts | 27 +++++++------------ src/planner/road/RoadPlannerBirdsEye.ts | 2 -- src/query-runner/IResolvedQuery.ts | 4 ++- src/query-runner/QueryRunnerDefault.ts | 15 +++++++++-- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/Constants.ts b/src/Constants.ts index 9f58ea90..765f4ec3 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -1,4 +1,8 @@ +import Units from "./util/Units"; + export default class Constants { public static readonly defaultMinimumWalkingSpeed = 3; public static readonly defaultMaximumWalkingSpeed = 6; + public static readonly defaultMaximumTransferDuration = Units.fromHours(.4); + public static readonly defaultMaximumLegs = 8; } diff --git a/src/interfaces/IQuery.ts b/src/interfaces/IQuery.ts index c254048a..d5acd039 100644 --- a/src/interfaces/IQuery.ts +++ b/src/interfaces/IQuery.ts @@ -1,5 +1,5 @@ import ILocation from "./ILocation"; -import { SpeedkmH } from "./units"; +import { DurationMs, SpeedkmH } from "./units"; export default interface IQuery { from?: string | string[] | ILocation | ILocation[]; @@ -11,4 +11,6 @@ export default interface IQuery { walkingSpeed?: SpeedkmH; minimumWalkingSpeed?: SpeedkmH; maximumWalkingSpeed?: SpeedkmH; + maximumTransferDuration?: DurationMs; + maximumLegs?: number; } diff --git a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts index cca7ff0f..e29da09c 100644 --- a/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.ts @@ -7,7 +7,6 @@ import { DurationMs } from "../../interfaces/units"; import ILocationResolver from "../../query-runner/ILocationResolver"; import IResolvedQuery from "../../query-runner/IResolvedQuery"; import TYPES from "../../types"; -import Units from "../../util/Units"; import Vectors from "../../util/Vectors"; import IRoadPlanner from "../road/IRoadPlanner"; import IReachableStopsFinder from "../stops/IReachableStopsFinder"; @@ -34,9 +33,6 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor private earliestArrivalByTrip: IEarliestArrivalByTrip = {}; // T private durationToTargetByStop: DurationMs[] = []; - private maxLegs: number = 8; // TODO define max legs on query? - private maximumDuration: DurationMs = Units.fromHours(0.4); // TODO define maximum duration (radius) on query? - private query: IResolvedQuery; constructor( @@ -55,9 +51,6 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor public async plan(query: IResolvedQuery): Promise { this.query = query; - - this.maximumDuration = 1; - this.setBounds(); return await this.calculateJourneys(); @@ -117,12 +110,12 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor private discoverConnection(connection: IConnection) { [connection.departureStop, connection.arrivalStop].forEach((stop) => { if (!this.profilesByStop[stop]) { - this.profilesByStop[stop] = [new Profile(this.maxLegs)]; + this.profilesByStop[stop] = [new Profile(this.query.maximumLegs)]; } }); if (!this.earliestArrivalByTrip[connection["gtfs:trip"]]) { - this.earliestArrivalByTrip[connection["gtfs:trip"]] = Array(this.maxLegs).fill(new EarliestArrival()); + this.earliestArrivalByTrip[connection["gtfs:trip"]] = Array(this.query.maximumLegs).fill(new EarliestArrival()); } } @@ -138,7 +131,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor for (const arrivalStop of this.query.to) { const reachableStops = await this.reachableStopsFinder.findReachableStops( arrivalStop as IStop, - this.maximumDuration, + this.query.maximumTransferDuration, this.query.minimumWalkingSpeed, ); @@ -152,10 +145,10 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor const walkingTimeToTarget = this.durationToTargetByStop[connection.arrivalStop]; if (walkingTimeToTarget === undefined) { - return Array(this.maxLegs).fill(Infinity); + return Array(this.query.maximumLegs).fill(Infinity); } - return Array(this.maxLegs).fill(connection.arrivalTime.getTime() + walkingTimeToTarget); + return Array(this.query.maximumLegs).fill(connection.arrivalTime.getTime() + walkingTimeToTarget); } private remainSeated(connection: IConnection): IArrivalTimeByTransfers { @@ -164,7 +157,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor private takeTransfer(connection: IConnection): IArrivalTimeByTransfers { return Vectors.shiftVector( - ProfileUtil.evalProfile(this.profilesByStop, connection, this.maxLegs), + ProfileUtil.evalProfile(this.profilesByStop, connection, this.query.maximumLegs), ); } @@ -200,7 +193,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor const departureStop = await this.locationResolver.resolve(connection.departureStop); const reachableStops = await this.reachableStopsFinder.findReachableStops( departureStop as IStop, - this.maximumDuration, + this.query.maximumTransferDuration, this.query.minimumWalkingSpeed, ); @@ -219,14 +212,14 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor let profilesByDepartureStop = this.profilesByStop[stop.id]; if (profilesByDepartureStop === undefined) { - profilesByDepartureStop = this.profilesByStop[stop.id] = [new Profile(this.maxLegs)]; + profilesByDepartureStop = this.profilesByStop[stop.id] = [new Profile(this.query.maximumLegs)]; } const earliestDepTimeProfile = profilesByDepartureStop[profilesByDepartureStop.length - 1]; // If arrival times for all numbers of legs are equal to the earliest entry, this // entry is redundant let redundant = true; - for (let i = 0; i < this.maxLegs; i++) { + for (let i = 0; i < this.query.maximumLegs; i++) { redundant = redundant && minVectorTimes[i] >= earliestDepTimeProfile.arrivalTimes[i]; } @@ -234,7 +227,7 @@ export default class PublicTransportPlannerCSAProfile implements IPublicTranspor const enterConnections = []; const exitConnections = []; - for (let transfers = 0; transfers < this.maxLegs; transfers++) { + for (let transfers = 0; transfers < this.query.maximumLegs; transfers++) { // If the new arrival time is better, update journey pointers // Else, keep old journey pointers if (minVectorTimes[transfers] < earliestDepTimeProfile.arrivalTimes[transfers]) { diff --git a/src/planner/road/RoadPlannerBirdsEye.ts b/src/planner/road/RoadPlannerBirdsEye.ts index 176d0575..e9f3d2ba 100644 --- a/src/planner/road/RoadPlannerBirdsEye.ts +++ b/src/planner/road/RoadPlannerBirdsEye.ts @@ -39,8 +39,6 @@ export default class RoadPlannerBirdsEye implements IRoadPlanner { const minDuration = Units.toDuration(distance, maxWalkingSpeed); const maxDuration = Units.toDuration(distance, minWalkingSpeed); - console.log(distance, minDuration, maxDuration); - const duration: IProbabilisticValue = { minimum: minDuration, maximum: maxDuration, diff --git a/src/query-runner/IResolvedQuery.ts b/src/query-runner/IResolvedQuery.ts index 478faf96..026d6d9e 100644 --- a/src/query-runner/IResolvedQuery.ts +++ b/src/query-runner/IResolvedQuery.ts @@ -1,5 +1,5 @@ import ILocation from "../interfaces/ILocation"; -import { SpeedkmH } from "../interfaces/units"; +import { DurationMs, SpeedkmH } from "../interfaces/units"; export default interface IResolvedQuery { from?: ILocation[]; @@ -10,4 +10,6 @@ export default interface IResolvedQuery { publicTransportOnly?: boolean; minimumWalkingSpeed?: SpeedkmH; maximumWalkingSpeed?: SpeedkmH; + maximumTransferDuration?: DurationMs; + maximumLegs?: number; } diff --git a/src/query-runner/QueryRunnerDefault.ts b/src/query-runner/QueryRunnerDefault.ts index 103309dd..2ef9bb5c 100644 --- a/src/query-runner/QueryRunnerDefault.ts +++ b/src/query-runner/QueryRunnerDefault.ts @@ -1,4 +1,5 @@ import { inject, injectable } from "inversify"; +import Constants from "../Constants"; import ILocation from "../interfaces/ILocation"; import IPath from "../interfaces/IPath"; import IQuery from "../interfaces/IQuery"; @@ -9,7 +10,6 @@ import TYPES from "../types"; import ILocationResolver from "./ILocationResolver"; import IQueryRunner from "./IQueryRunner"; import IResolvedQuery from "./IResolvedQuery"; -import Constants from "../Constants"; @injectable() export default class QueryRunnerDefault implements IQueryRunner { @@ -57,7 +57,15 @@ export default class QueryRunnerDefault implements IQueryRunner { } private async resolveQuery(query: IQuery): Promise { - const { from, to, minimumWalkingSpeed, maximumWalkingSpeed, walkingSpeed, ...other} = query; + // tslint:disable:trailing-comma + const { + from, to, + minimumWalkingSpeed, maximumWalkingSpeed, walkingSpeed, + maximumTransferDuration, maximumLegs, + ...other + } = query; + // tslint:enable:trailing-comma + const resolvedQuery: IResolvedQuery = Object.assign({}, other); resolvedQuery.from = await this.resolveEndpoint(from); @@ -65,6 +73,9 @@ export default class QueryRunnerDefault implements IQueryRunner { resolvedQuery.minimumWalkingSpeed = minimumWalkingSpeed || walkingSpeed || Constants.defaultMinimumWalkingSpeed; resolvedQuery.maximumWalkingSpeed = maximumWalkingSpeed || walkingSpeed || Constants.defaultMaximumWalkingSpeed; + resolvedQuery.maximumTransferDuration = maximumTransferDuration || Constants.defaultMaximumTransferDuration; + resolvedQuery.maximumLegs = maximumLegs || Constants.defaultMaximumTransferDuration; + return resolvedQuery; } From 283f593eb45981e49360c6ce397cea6cb3c9d902 Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Tue, 6 Nov 2018 11:38:15 +0100 Subject: [PATCH 12/97] Fixes and added debugging conf --- package.json | 3 ++- .../ld-fetch/ConnectionsFetcherLDFetch.ts | 2 +- src/index.ts | 12 ++++++++++-- src/query-runner/QueryRunnerDefault.ts | 2 +- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 78eefe01..b770ba5f 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc", - "test": "jest" + "test": "jest", + "debug": "ndb node dist/index.js --debug" }, "dependencies": { "haversine": "^1.1.0", diff --git a/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts b/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts index 89b8e153..f5268501 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsFetcherLDFetch.ts @@ -39,7 +39,7 @@ export default abstract class ConnectionsFetcherLDFetch implements IConnectionsF this.ldFetch.on("response", (url) => { httpResponseTimes[url] = (new Date()).getTime() - httpStartTimes[url].getTime(); - console.error(`HTTP GET - ${url} (${httpResponseTimes[url]}ms)`); + console.log(`HTTP GET - ${url} (${httpResponseTimes[url]}ms)`); }); } } diff --git a/src/index.ts b/src/index.ts index 0feae7cb..b26ac774 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,16 @@ import "isomorphic-fetch"; import "reflect-metadata"; import Planner from "./Planner"; +import Units from "./util/Units"; + +const isDebugging = process && process.argv.includes("--debug"); const planner = new Planner(); (async () => { + if (isDebugging) { // tslint:disable-next-line:no-debugger + debugger; + } const roadOnlyResult = await planner.query({ roadOnly: true, @@ -18,8 +24,10 @@ const planner = new Planner(); publicTransportOnly: true, from: "http://irail.be/stations/NMBS/008896925", // Ingelmunster to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters + minimumDepartureTime: new Date(), + maximumTransferDuration: Units.fromHours(.5), }); - console.log(JSON.stringify(publicTransportResult, null, " ")); - + console.log(publicTransportResult.paths.length); + console.log(publicTransportResult); })(); diff --git a/src/query-runner/QueryRunnerDefault.ts b/src/query-runner/QueryRunnerDefault.ts index 2ef9bb5c..922d4ecb 100644 --- a/src/query-runner/QueryRunnerDefault.ts +++ b/src/query-runner/QueryRunnerDefault.ts @@ -74,7 +74,7 @@ export default class QueryRunnerDefault implements IQueryRunner { resolvedQuery.maximumWalkingSpeed = maximumWalkingSpeed || walkingSpeed || Constants.defaultMaximumWalkingSpeed; resolvedQuery.maximumTransferDuration = maximumTransferDuration || Constants.defaultMaximumTransferDuration; - resolvedQuery.maximumLegs = maximumLegs || Constants.defaultMaximumTransferDuration; + resolvedQuery.maximumLegs = maximumLegs || Constants.defaultMaximumLegs; return resolvedQuery; } From 9753bd833ee9c193a24ad52e0d26cee463408633 Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Tue, 6 Nov 2018 12:25:41 +0100 Subject: [PATCH 13/97] Added cache versions of both ReachableStopsFinders --- src/index.ts | 3 + src/inversify.config.ts | 5 +- .../ReachableStopsFinderBirdsEyeCached.ts | 64 +++++++++++++ .../stops/ReachableStopsFinderRoadPlanner.ts | 3 +- .../ReachableStopsFinderRoadPlannerCached.ts | 94 +++++++++++++++++++ 5 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/planner/stops/ReachableStopsFinderBirdsEyeCached.ts create mode 100644 src/planner/stops/ReachableStopsFinderRoadPlannerCached.ts diff --git a/src/index.ts b/src/index.ts index b26ac774..cff93bec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,6 +20,8 @@ const planner = new Planner(); console.log(JSON.stringify(roadOnlyResult, null, " ")); + console.time("Public transport planner"); + const publicTransportResult = await planner.query({ publicTransportOnly: true, from: "http://irail.be/stations/NMBS/008896925", // Ingelmunster @@ -28,6 +30,7 @@ const planner = new Planner(); maximumTransferDuration: Units.fromHours(.5), }); + console.timeEnd("Public transport planner"); console.log(publicTransportResult.paths.length); console.log(publicTransportResult); })(); diff --git a/src/inversify.config.ts b/src/inversify.config.ts index ce506e07..1e3e1190 100644 --- a/src/inversify.config.ts +++ b/src/inversify.config.ts @@ -15,6 +15,9 @@ import IRoadPlanner from "./planner/road/IRoadPlanner"; import RoadPlannerBirdsEye from "./planner/road/RoadPlannerBirdsEye"; import IReachableStopsFinder from "./planner/stops/IReachableStopsFinder"; import ReachableStopsFinderBirdsEye from "./planner/stops/ReachableStopsFinderBirdsEye"; +import ReachableStopsFinderBirdsEyeCached from "./planner/stops/ReachableStopsFinderBirdsEyeCached"; +import ReachableStopsFinderRoadPlanner from "./planner/stops/ReachableStopsFinderRoadPlanner"; +import ReachableStopsFinderRoadPlannerCached from "./planner/stops/ReachableStopsFinderRoadPlannerCached"; import ILocationResolver from "./query-runner/ILocationResolver"; import IQueryRunner from "./query-runner/IQueryRunner"; import LocationResolverDefault from "./query-runner/LocationResolverDefault"; @@ -42,6 +45,6 @@ container.bind(TYPES.StopsFetcher).to(StopsFetcherNMBS); container.bind(TYPES.StopsFetcher).to(StopsFetcherDeLijn); container.bind(TYPES.StopsFetcherMediator).to(StopsFetcherProxy); -container.bind(TYPES.ReachableStopsFinder).to(ReachableStopsFinderBirdsEye); +container.bind(TYPES.ReachableStopsFinder).to(ReachableStopsFinderBirdsEyeCached); export default container; diff --git a/src/planner/stops/ReachableStopsFinderBirdsEyeCached.ts b/src/planner/stops/ReachableStopsFinderBirdsEyeCached.ts new file mode 100644 index 00000000..3110e8a0 --- /dev/null +++ b/src/planner/stops/ReachableStopsFinderBirdsEyeCached.ts @@ -0,0 +1,64 @@ +import { inject, injectable } from "inversify"; +import IStop from "../../fetcher/stops/IStop"; +import IStopsFetcherMediator from "../../fetcher/stops/IStopsFetcherMediator"; +import { DurationMs, SpeedkmH } from "../../interfaces/units"; +import TYPES from "../../types"; +import Geo from "../../util/Geo"; +import Units from "../../util/Units"; +import IReachableStopsFinder, { IReachableStop } from "./IReachableStopsFinder"; + +@injectable() +export default class ReachableStopsFinderBirdsEyeCached implements IReachableStopsFinder { + private readonly stopsFetcherMediator: IStopsFetcherMediator; + + private allStops: IStop[]; + private reachableStopsCache: {[cacheKey: string]: IReachableStop[]}; + + constructor( + @inject(TYPES.StopsFetcherMediator) stopsFetcherMediator: IStopsFetcherMediator, + ) { + this.stopsFetcherMediator = stopsFetcherMediator; + this.reachableStopsCache = {}; + } + + public async findReachableStops( + source: IStop, + maximumDuration: DurationMs, + minimumSpeed: SpeedkmH, + ): Promise { + + const cacheKey = `${source.id} ${maximumDuration} ${minimumSpeed}`; + const cacheItem = this.reachableStopsCache[cacheKey]; + + if (cacheItem) { + return cacheItem; + } + + const allStops = await this.getAllStops(); + + const reachableStops = allStops.map((possibleTarget: IStop): IReachableStop => { + if (possibleTarget.id === source.id) { + return {stop: source, duration: 0}; + } + + const distance = Geo.getDistanceBetweenStops(source, possibleTarget); + const duration = Units.toDuration(distance, minimumSpeed); + + if (duration <= maximumDuration) { + return {stop: possibleTarget, duration}; + } + }).filter((reachableStop) => !!reachableStop); + + this.reachableStopsCache[cacheKey] = reachableStops; + return reachableStops; + } + + private async getAllStops() { + if (!this.allStops) { + this.allStops = await this.stopsFetcherMediator.getAllStops(); + } + + return this.allStops; + + } +} diff --git a/src/planner/stops/ReachableStopsFinderRoadPlanner.ts b/src/planner/stops/ReachableStopsFinderRoadPlanner.ts index 7c7c48ea..ec6fbb1f 100644 --- a/src/planner/stops/ReachableStopsFinderRoadPlanner.ts +++ b/src/planner/stops/ReachableStopsFinderRoadPlanner.ts @@ -1,4 +1,4 @@ -import { inject } from "inversify"; +import { inject, injectable } from "inversify"; import IStop from "../../fetcher/stops/IStop"; import IStopsFetcherMediator from "../../fetcher/stops/IStopsFetcherMediator"; import ILocation from "../../interfaces/ILocation"; @@ -8,6 +8,7 @@ import TYPES from "../../types"; import IRoadPlanner from "../road/IRoadPlanner"; import IReachableStopsFinder, { IReachableStop } from "./IReachableStopsFinder"; +@injectable() export default class ReachableStopsFinderRoadPlanner implements IReachableStopsFinder { private readonly stopsFetcherMediator: IStopsFetcherMediator; private readonly roadPlanner: IRoadPlanner; diff --git a/src/planner/stops/ReachableStopsFinderRoadPlannerCached.ts b/src/planner/stops/ReachableStopsFinderRoadPlannerCached.ts new file mode 100644 index 00000000..b49656c5 --- /dev/null +++ b/src/planner/stops/ReachableStopsFinderRoadPlannerCached.ts @@ -0,0 +1,94 @@ +import { inject, injectable } from "inversify"; +import IStop from "../../fetcher/stops/IStop"; +import IStopsFetcherMediator from "../../fetcher/stops/IStopsFetcherMediator"; +import ILocation from "../../interfaces/ILocation"; +import IPath from "../../interfaces/IPath"; +import IResolvedQuery from "../../query-runner/IResolvedQuery"; +import TYPES from "../../types"; +import IRoadPlanner from "../road/IRoadPlanner"; +import IReachableStopsFinder, { IReachableStop } from "./IReachableStopsFinder"; + +@injectable() +export default class ReachableStopsFinderRoadPlannerCached implements IReachableStopsFinder { + private readonly stopsFetcherMediator: IStopsFetcherMediator; + private readonly roadPlanner: IRoadPlanner; + + private allStops: IStop[]; + private reachableStopsCache: {[cacheKey: string]: IReachableStop[]}; + + constructor( + @inject(TYPES.StopsFetcherMediator) stopsFetcherMediator: IStopsFetcherMediator, + @inject(TYPES.RoadPlanner) roadPlanner: IRoadPlanner, + ) { + this.stopsFetcherMediator = stopsFetcherMediator; + this.roadPlanner = roadPlanner; + this.reachableStopsCache = {}; + } + + public async findReachableStops( + source: IStop, + maximumDuration: number, + minimumSpeed: number): Promise { + + const cacheKey = `${source.id} ${maximumDuration} ${minimumSpeed}`; + const cacheItem = this.reachableStopsCache[cacheKey]; + + if (cacheItem) { + return cacheItem; + } + + const minimumDepartureTime = new Date(); + const maximumArrivalTime = new Date(minimumDepartureTime.getTime() + maximumDuration); + + const baseQuery: IResolvedQuery = { + from: [source as ILocation], + minimumDepartureTime, + maximumArrivalTime, + minimumWalkingSpeed: minimumSpeed, + }; + + const allStops = await this.getAllStops(); + const reachableStops: IReachableStop[] = [{stop: source, duration: 0}]; + + await Promise.all(allStops.map(async (possibleTarget: IStop) => { + if (possibleTarget.id !== source.id) { + + const query = Object.assign({}, baseQuery, { to: [possibleTarget as ILocation] }); + const paths: IPath[] = await this.roadPlanner.plan(query); + + if (paths.length) { + + // tslint:disable-next-line:no-shadowed-variable + const shortestDuration = paths.reduce((shortestDuration: number, path: IPath) => { + // Minimum speed is passed so sum max duration over all steps + const duration = path.steps + .reduce((totalDuration: number, step) => totalDuration + step.duration.maximum, 0); + + if (duration < shortestDuration) { + return duration; + } + + return shortestDuration; + }, Number.POSITIVE_INFINITY); + + if (shortestDuration < maximumDuration) { + reachableStops.push({stop: possibleTarget, duration: shortestDuration}); + } + } + } + + })); + + this.reachableStopsCache[cacheKey] = reachableStops; + return reachableStops; + } + + private async getAllStops() { + if (!this.allStops) { + this.allStops = await this.stopsFetcherMediator.getAllStops(); + } + + return this.allStops; + + } +} From 4241868e829c9d9fb82cfdbba2692dd34978a7af Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Tue, 6 Nov 2018 16:01:00 +0100 Subject: [PATCH 14/97] Added TravelMode enum --- src/TravelMode.ts | 7 +++++++ src/fetcher/connections/IConnection.ts | 2 ++ .../connections/ld-fetch/ConnectionsFetcherDeLijn.ts | 2 ++ .../connections/ld-fetch/ConnectionsFetcherNMBS.ts | 2 ++ .../ld-fetch/ConnectionsIteratorLDFetch.ts | 12 +++++++++++- 5 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/TravelMode.ts diff --git a/src/TravelMode.ts b/src/TravelMode.ts new file mode 100644 index 00000000..eba28087 --- /dev/null +++ b/src/TravelMode.ts @@ -0,0 +1,7 @@ +enum TravelMode { + Train = "train", + Bus = "bus", + Walking = "walking", +} + +export default TravelMode; diff --git a/src/fetcher/connections/IConnection.ts b/src/fetcher/connections/IConnection.ts index bb8fe4ef..c81e1e15 100644 --- a/src/fetcher/connections/IConnection.ts +++ b/src/fetcher/connections/IConnection.ts @@ -1,7 +1,9 @@ import { DurationMs } from "../../interfaces/units"; +import TravelMode from "../../TravelMode"; export default interface IConnection { id: string; + travelMode: TravelMode; arrivalTime: Date; arrivalStop: string; diff --git a/src/fetcher/connections/ld-fetch/ConnectionsFetcherDeLijn.ts b/src/fetcher/connections/ld-fetch/ConnectionsFetcherDeLijn.ts index fcd28bf8..f44d0898 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsFetcherDeLijn.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsFetcherDeLijn.ts @@ -2,6 +2,7 @@ import { injectable } from "inversify"; import IConnection from "../IConnection"; import ConnectionsFetcherLDFetch from "./ConnectionsFetcherLDFetch"; import ConnectionsIteratorLDFetch from "./ConnectionsIteratorLDFetch"; +import TravelMode from "../../../TravelMode"; const DE_LIJN_BASE_URL = "http://openplanner.ilabt.imec.be/delijn/West-Vlaanderen/connections"; @@ -11,6 +12,7 @@ export default class ConnectionsFetcherDeLijn extends ConnectionsFetcherLDFetch public fetch(): AsyncIterator { return new ConnectionsIteratorLDFetch( DE_LIJN_BASE_URL, + TravelMode.Bus, this.ldFetch, this.config, ); diff --git a/src/fetcher/connections/ld-fetch/ConnectionsFetcherNMBS.ts b/src/fetcher/connections/ld-fetch/ConnectionsFetcherNMBS.ts index 8ddf8de1..79e5dacb 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsFetcherNMBS.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsFetcherNMBS.ts @@ -2,6 +2,7 @@ import { injectable } from "inversify"; import IConnection from "../IConnection"; import ConnectionsFetcherLDFetch from "./ConnectionsFetcherLDFetch"; import ConnectionsIteratorLDFetch from "./ConnectionsIteratorLDFetch"; +import TravelMode from "../../../TravelMode"; const IRAIL_CONNECTIONS_BASE_URL = "https://graph.irail.be/sncb/connections"; @@ -11,6 +12,7 @@ export default class ConnectionsFetcherNMBS extends ConnectionsFetcherLDFetch { public fetch(): AsyncIterator { return new ConnectionsIteratorLDFetch( IRAIL_CONNECTIONS_BASE_URL, + TravelMode.Train, this.ldFetch, this.config, ); diff --git a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts index 6fa8e577..de74c3d3 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts @@ -2,10 +2,11 @@ import LdFetch from "ldfetch"; import { Util } from "n3"; import { Triple } from "rdf-js"; import UriTemplate from "uritemplate"; +import TravelMode from "../../../TravelMode"; +import Units from "../../../util/Units"; import { matchesTriple, transformPredicate } from "../../helpers"; import IConnection from "../IConnection"; import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig"; -import Units from "../../../util/Units"; interface IEntity { } @@ -16,6 +17,7 @@ interface IEntityMap { export default class ConnectionsIteratorLDFetch implements AsyncIterator { public readonly baseUrl: string; + private travelMode: TravelMode; private ldFetch: LdFetch; private connections: IConnection[]; @@ -26,10 +28,12 @@ export default class ConnectionsIteratorLDFetch implements AsyncIterator { + connection.travelMode = this.travelMode; + return connection; + }); + // Sort connections by departure time this.connections = connections.sort((connectionA, connectionB) => { return connectionA.departureTime.valueOf() - connectionB.departureTime.valueOf(); From b2dd01f8631a1abe13d5c21a885099b7806b3eaf Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Thu, 15 Nov 2018 09:58:14 +0100 Subject: [PATCH 15/97] 1. added csa tests. --- .../ld-fetch/ConnectionsIteratorLDFetch.ts | 22 +- .../tests/ConnectionsFetcherNMBSTest.ts | 60 +++++ .../connections/tests/connection-data.ts | 246 ++++++++++++++++++ src/interfaces/IPath.ts | 3 - src/interfaces/IStep.ts | 2 + src/planner/Path.ts | 13 +- src/planner/Step.ts | 5 + .../public-transport/CSA/JourneyExtractor.ts | 32 ++- .../PublicTransportPlannerCSAProfile.test.ts | 139 ++++++++++ src/planner/road/RoadPlannerBirdsEye.ts | 2 + 10 files changed, 501 insertions(+), 23 deletions(-) create mode 100644 src/fetcher/connections/tests/ConnectionsFetcherNMBSTest.ts create mode 100644 src/fetcher/connections/tests/connection-data.ts create mode 100644 src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts diff --git a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts index de74c3d3..2a81e31b 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.ts @@ -61,9 +61,25 @@ export default class ConnectionsIteratorLDFetch implements AsyncIterator 0 && value.arrivalTime > this.config.upperBoundDate) { + value = this.connections.pop(); + + if (!value) { + await this.loadPage(this.previousPageIri); + value = this.connections.pop(); + } + } + + if (!value) { + return {value, done: true}; + } + + } else { + value = this.connections.shift(); + } return { value, done: false }; diff --git a/src/fetcher/connections/tests/ConnectionsFetcherNMBSTest.ts b/src/fetcher/connections/tests/ConnectionsFetcherNMBSTest.ts new file mode 100644 index 00000000..a2de697f --- /dev/null +++ b/src/fetcher/connections/tests/ConnectionsFetcherNMBSTest.ts @@ -0,0 +1,60 @@ +import { injectable } from "inversify"; +import IConnection from "../IConnection"; +import IConnectionsFetcherConfig from "../IConnectionsFetcherConfig"; + +@injectable() +export default class ConnectionsFetcherNMBSTest implements AsyncIterator { + + private connections: Array> = []; + private config: IConnectionsFetcherConfig = {}; + private connectionIndex; + + constructor(connections: Array>) { + this.connections = connections; + } + + public [Symbol.asyncIterator](): AsyncIterator { + return this.fetch(); + } + + public setConfig(config: IConnectionsFetcherConfig): void { + this.config = config; + + if (!this.config.backward) { + this.connectionIndex = 0; + } else { + this.connectionIndex = this.connections.length - 1; + } + } + + public fetch(): AsyncIterator { + return this; + } + + public async next(): Promise> { + const nextConnection = this.connections[this.connectionIndex]; + if (!this.config.backward) { + if (this.connectionIndex === this.connections.length - 1) { + nextConnection.done = true; + } else { + this.connectionIndex++; + } + + } else { + if (this.connectionIndex === 0) { + nextConnection.done = true; + } else { + this.connectionIndex--; + } + } + return nextConnection; + } + + public async return(value?: any): Promise> { + return undefined; + } + + public async throw(e?: any): Promise> { + return undefined; + } +} diff --git a/src/fetcher/connections/tests/connection-data.ts b/src/fetcher/connections/tests/connection-data.ts new file mode 100644 index 00000000..b3f25eee --- /dev/null +++ b/src/fetcher/connections/tests/connection-data.ts @@ -0,0 +1,246 @@ +import TravelMode from "../../../TravelMode"; + +const connections = [ + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8891264/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008891314", + "arrivalTime": new Date("2018-11-06T10:30:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008891264", + "departureTime": new Date("2018-11-06T10:24:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8891314/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008892205", + "arrivalTime": new Date("2018-11-06T10:35:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008891314", + "departureTime": new Date("2018-11-06T10:31:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8892205/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008896800", + "arrivalTime": new Date("2018-11-06T10:43:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008892205", + "departureTime": new Date("2018-11-06T10:36:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8896800/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008896909", + "arrivalTime": new Date("2018-11-06T10:50:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008896800", + "departureTime": new Date("2018-11-06T10:44:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8896909/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008896925", + "arrivalTime": new Date("2018-11-06T10:56:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008896909", + "departureTime": new Date("2018-11-06T10:51:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8896925/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008896008", + "arrivalTime": new Date("2018-11-06T11:05:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008896925", + "departureTime": new Date("2018-11-06T10:56:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8896008/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008892601", + "arrivalTime": new Date("2018-11-06T11:29:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008896008", + "departureTime": new Date("2018-11-06T11:12:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8896008/20181106/IC412", + "arrivalStop": "http://irail.be/stations/NMBS/008896115", + "arrivalTime": new Date("2018-11-06T11:22:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008896008", + "departureTime": new Date("2018-11-06T11:17:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC412/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8896115/20181106/IC412", + "arrivalStop": "http://irail.be/stations/NMBS/008896149", + "arrivalTime": new Date("2018-11-06T11:29:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008896115", + "departureTime": new Date("2018-11-06T11:23:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC412/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8896149/20181106/IC412", + "arrivalStop": "http://irail.be/stations/NMBS/008892106", + "arrivalTime": new Date("2018-11-06T11:37:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008896149", + "departureTime": new Date("2018-11-06T11:30:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC412/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8892601/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008895208", + "arrivalTime": new Date("2018-11-06T11:44:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008892601", + "departureTime": new Date("2018-11-06T11:32:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8892106/20181106/IC412", + "arrivalStop": "http://irail.be/stations/NMBS/008892080", + "arrivalTime": new Date("2018-11-06T11:43:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008892106", + "departureTime": new Date("2018-11-06T11:38:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC412/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8892080/20181106/IC412", + "arrivalStop": "http://irail.be/stations/NMBS/008892007", + "arrivalTime": new Date("2018-11-06T11:51:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008892080", + "departureTime": new Date("2018-11-06T11:44:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC412/20181106", + }, + done: false, + }, + { + value: + { + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureDelay": 0, + "id": "http://irail.be/connections/8895208/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008895802", + "arrivalTime": new Date("2018-11-06T12:02:00.000Z"), + "departureStop": "http://irail.be/stations/NMBS/008895208", + "departureTime": new Date("2018-11-06T11:46:00.000Z"), + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "id": "http://irail.be/connections/8895802/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008814001", + "arrivalTime": new Date("2018-11-06T12:20:00.000Z"), + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureStop": "http://irail.be/stations/NMBS/008895802", + "departureTime": new Date("2018-11-06T12:05:00.000Z"), + "departureDelay": 0, + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, + { + value: + { + "id": "http://irail.be/connections/8814001/20181106/IC2310", + "arrivalStop": "http://irail.be/stations/NMBS/008813003", + "arrivalTime": new Date("2018-11-06T12:26:00.000Z"), + "arrivalDelay": 0, + "travelMode": TravelMode.Train, + "departureStop": "http://irail.be/stations/NMBS/008814001", + "departureTime": new Date("2018-11-06T12:23:00.000Z"), + "departureDelay": 0, + "gtfs:trip": "http://irail.be/vehicle/IC2310/20181106", + }, + done: false, + }, +]; + +export default connections; diff --git a/src/interfaces/IPath.ts b/src/interfaces/IPath.ts index d1688a56..efbfce40 100644 --- a/src/interfaces/IPath.ts +++ b/src/interfaces/IPath.ts @@ -2,7 +2,4 @@ import IStep from "./IStep"; export default interface IPath { steps: IStep[]; - departureTime?: Date; - arrivalTime?: Date; - transfers?: number; } diff --git a/src/interfaces/IStep.ts b/src/interfaces/IStep.ts index 884d8711..a5090cfd 100644 --- a/src/interfaces/IStep.ts +++ b/src/interfaces/IStep.ts @@ -1,3 +1,4 @@ +import TravelMode from "../TravelMode"; import ILocation from "./ILocation"; import IProbabilisticValue from "./IProbabilisticValue"; import { DistanceM, DurationMs } from "./units"; @@ -5,6 +6,7 @@ import { DistanceM, DurationMs } from "./units"; export default interface IStep { startLocation: ILocation; stopLocation: ILocation; + travelMode: TravelMode; startTime?: Date; stopTime?: Date; duration: IProbabilisticValue; diff --git a/src/planner/Path.ts b/src/planner/Path.ts index 44a9ae42..9b2970c1 100644 --- a/src/planner/Path.ts +++ b/src/planner/Path.ts @@ -3,25 +3,16 @@ import IStep from "../interfaces/IStep"; export default class Path implements IPath { - public static createFromProfile(profile, transfers): Path { + public static create(): Path { return new Path( [], - new Date(profile.arrivalTimes[transfers]), - new Date(profile.departureTime), - transfers, ); } - public arrivalTime: Date; - public departureTime: Date; public steps: IStep[]; - public transfers: number; - constructor(steps: IStep[], arrivalTime?: Date, departureTime?: Date, transfers?: number) { - this.arrivalTime = arrivalTime; - this.departureTime = departureTime; + constructor(steps: IStep[]) { this.steps = steps; - this.transfers = transfers; } public addStep(step: IStep): void { diff --git a/src/planner/Step.ts b/src/planner/Step.ts index 1279064e..305dc81d 100644 --- a/src/planner/Step.ts +++ b/src/planner/Step.ts @@ -3,6 +3,7 @@ import ILocation from "../interfaces/ILocation"; import IProbabilisticValue from "../interfaces/IProbabilisticValue"; import IStep from "../interfaces/IStep"; import { DistanceM, DurationMs } from "../interfaces/units"; +import TravelMode from "../TravelMode"; export default class Step implements IStep { @@ -10,6 +11,7 @@ export default class Step implements IStep { return new Step( {id: enterConnection.departureStop}, {id: exitConnection.arrivalStop}, + enterConnection.travelMode, { minimum: ( exitConnection.arrivalTime.getTime() - @@ -27,10 +29,12 @@ export default class Step implements IStep { public startTime: Date; public stopLocation: ILocation; public stopTime: Date; + public travelMode: TravelMode; constructor( startLocation: ILocation, stopLocation: ILocation, + travelMode: TravelMode, duration: IProbabilisticValue, startTime?: Date, stopTime?: Date, @@ -38,6 +42,7 @@ export default class Step implements IStep { ) { this.distance = distance; this.duration = duration; + this.travelMode = travelMode; this.startLocation = startLocation; this.startTime = startTime; this.stopLocation = stopLocation; diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index 87e8fb99..111c6e89 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -31,6 +31,11 @@ export default class JourneyExtractor { const journeys = []; for (const departureStop of query.from) { + 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()) { @@ -59,13 +64,18 @@ export default class JourneyExtractor { return journeys; } - private checkBestArrivalTime(profile: Profile, transfers: number, departureStop: ILocation, arrivalStop: ILocation) { + private checkBestArrivalTime( + profile: Profile, + transfers: number, + departureStop: ILocation, + arrivalStop: ILocation, + ): boolean { return profile.arrivalTimes[transfers] < Infinity && (!this.bestArrivalTime[departureStop.id] || !this.bestArrivalTime[departureStop.id][arrivalStop.id] || profile.arrivalTimes[transfers] < this.bestArrivalTime[departureStop.id][arrivalStop.id]); } - private setBestArrivalTime(departureStop: ILocation, arrivalStop: ILocation, arrivalTime: number) { + private setBestArrivalTime(departureStop: ILocation, arrivalStop: ILocation, arrivalTime: number): void { if (!this.bestArrivalTime[departureStop.id]) { this.bestArrivalTime[departureStop.id] = []; } @@ -79,8 +89,10 @@ export default class JourneyExtractor { profilesByStop: IProfilesByStop, query: IResolvedQuery, ): Promise { + let shouldReturn = true; + // Extract journey for amount of transfers - const path: Path = Path.createFromProfile(profile, transfers); + const path: Path = Path.create(); let currentEntry = profile; let remainingTransfers = transfers; @@ -127,13 +139,17 @@ export default class JourneyExtractor { } if (path.steps[path.steps.length - 1].stopLocation.id !== arrivalStop.id) { - await this.addFinalFootpath(path, arrivalStop, query); + shouldReturn = await this.addFinalFootpath(path, arrivalStop, query); + } + + if (!shouldReturn) { + return Promise.reject("Can't reach arrival stop."); } return path as IPath; } - private async addFinalFootpath(path: Path, arrivalStop: ILocation, query: IResolvedQuery): Promise { + 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 ); @@ -146,8 +162,12 @@ export default class JourneyExtractor { maximumWalkingSpeed: query.maximumWalkingSpeed, }); - if (walkingResult && walkingResult[0] && walkingResult[0].steps[0]) { + 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 new file mode 100644 index 00000000..ad7b18f2 --- /dev/null +++ b/src/planner/public-transport/PublicTransportPlannerCSAProfile.test.ts @@ -0,0 +1,139 @@ +import "jest"; +import ConnectionsFetcherNMBS from "../../fetcher/connections/ld-fetch/ConnectionsFetcherNMBS"; +import connections from "../../fetcher/connections/tests/connection-data"; +import ConnectionsFetcherNMBSTest from "../../fetcher/connections/tests/ConnectionsFetcherNMBSTest"; +import StopsFetcherNMBS from "../../fetcher/stops/ld-fetch/StopsFetcherNMBS"; +import IPath from "../../interfaces/IPath"; +import IQuery from "../../interfaces/IQuery"; +import IQueryResult from "../../interfaces/IQueryResult"; +import IResolvedQuery from "../../query-runner/IResolvedQuery"; +import LocationResolverDefault from "../../query-runner/LocationResolverDefault"; +import QueryRunnerDefault from "../../query-runner/QueryRunnerDefault"; +import TravelMode from "../../TravelMode"; +import RoadPlannerBirdsEye from "../road/RoadPlannerBirdsEye"; +import ReachableStopsFinderBirdsEye from "../stops/ReachableStopsFinderBirdsEye"; +import PublicTransportPlannerCSAProfile from "./PublicTransportPlannerCSAProfile"; + +describe("[PublicTransportPlannerCSAProfile]", () => { + describe("mock data", () => { + let result: IPath []; + + 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}], + minimumDepartureTime: new Date("2018-11-06T09:00:00.000Z"), + maximumArrivalTime: new Date("2018-11-06T19:00:00.000Z"), + maximumLegs: 8, + }; + + beforeAll(async () => { + const connectionFetcher = new ConnectionsFetcherNMBSTest(connections); + connectionFetcher.setConfig({backward: true}); + + const roadPlanner = new RoadPlannerBirdsEye(); + const stopsFetcher = new StopsFetcherNMBS(); + const locationResolver = new LocationResolverDefault(stopsFetcher); + const reachableStopsFinder = new ReachableStopsFinderBirdsEye(stopsFetcher); + const CSA = new PublicTransportPlannerCSAProfile( + connectionFetcher, + roadPlanner, + locationResolver, + reachableStopsFinder, + ); + + result = await CSA.plan(query); + }); + + it("Correct departure and arrival stop", async () => { + expect(result).toBeDefined(); + + for (const path of result) { + expect(path.steps).toBeDefined(); + expect(path.steps[0]).toBeDefined(); + expect(query.from.map((from) => from.id)).toContain(path.steps[0].startLocation.id); + expect(query.to.map((to) => to.id)).toContain(path.steps[path.steps.length - 1].stopLocation.id); + } + }); + }); + + describe("real-time data", () => { + jest.setTimeout(90000); + + const now = new Date(); + const nowPlusTwoHours = new Date(); + nowPlusTwoHours.setHours(nowPlusTwoHours.getHours() + 4); + + const query: IQuery = { + publicTransportOnly: true, + from: [ + "http://irail.be/stations/NMBS/008896925", // Ingelmunster + ], + to: [ + "http://irail.be/stations/NMBS/008821006", // Antwerpen-Centraal + ], + maximumArrivalTime: nowPlusTwoHours, + }; + let result: IQueryResult; + + beforeAll(async () => { + const connectionFetcher = new ConnectionsFetcherNMBS(); + const roadPlanner = new RoadPlannerBirdsEye(); + const stopsFetcher = new StopsFetcherNMBS(); + const locationResolver = new LocationResolverDefault(stopsFetcher); + const reachableStopsFinder = new ReachableStopsFinderBirdsEye(stopsFetcher); + const CSA = new PublicTransportPlannerCSAProfile( + connectionFetcher, + roadPlanner, + locationResolver, + reachableStopsFinder, + ); + + const queryRunner = new QueryRunnerDefault(locationResolver, CSA, roadPlanner); + result = await queryRunner.run(query); + }); + + it("Correct departure and arrival stop", () => { + expect(result).toBeDefined(); + expect(result.paths).toBeDefined(); + + for (const path of result.paths) { + expect(path.steps).toBeDefined(); + + expect(path.steps.length).toBeGreaterThanOrEqual(0); + expect(path.steps[0]).toBeDefined(); + expect(path.steps[path.steps.length - 1]).toBeDefined(); + + expect(query.from).toContain(path.steps[0].startLocation.id); + expect(query.to).toContain(path.steps[path.steps.length - 1].stopLocation.id); + } + }); + + it("Correct departure and arrival time", () => { + expect(result).toBeDefined(); + expect(result.paths).toBeDefined(); + + for (const path of result.paths) { + expect(path.steps).toBeDefined(); + + expect(path.steps.length).toBeGreaterThanOrEqual(0); + expect(path.steps[0]).toBeDefined(); + expect(path.steps[path.steps.length - 1]).toBeDefined(); + + let i = path.steps.length - 1; + while (i >= 0 && path.steps[i].travelMode !== TravelMode.Train) { + i--; + } + + let arrivalTime = path.steps[i].stopTime.getTime(); + for (let j = i + 1 ; j < path.steps.length ; j++) { + arrivalTime += path.steps[j].duration.minimum; + } + + expect(path.steps[0].startTime.getTime()).toBeGreaterThanOrEqual(now.getTime()); + expect(arrivalTime).toBeLessThanOrEqual(nowPlusTwoHours.getTime()); + } + }); + }); + +}); diff --git a/src/planner/road/RoadPlannerBirdsEye.ts b/src/planner/road/RoadPlannerBirdsEye.ts index e9f3d2ba..bedae5b6 100644 --- a/src/planner/road/RoadPlannerBirdsEye.ts +++ b/src/planner/road/RoadPlannerBirdsEye.ts @@ -4,6 +4,7 @@ import IPath from "../../interfaces/IPath"; import IProbabilisticValue from "../../interfaces/IProbabilisticValue"; import { DurationMs, SpeedkmH } from "../../interfaces/units"; import IResolvedQuery from "../../query-runner/IResolvedQuery"; +import TravelMode from "../../TravelMode"; import Geo from "../../util/Geo"; import Units from "../../util/Units"; import IRoadPlanner from "./IRoadPlanner"; @@ -51,6 +52,7 @@ export default class RoadPlannerBirdsEye implements IRoadPlanner { stopLocation: to, duration, distance, + travelMode: TravelMode.Walking, }], }; } From 0d70cc696560036918b1be26d393d604518b0bd0 Mon Sep 17 00:00:00 2001 From: Maxim Martin Date: Thu, 15 Nov 2018 10:03:20 +0100 Subject: [PATCH 16/97] 1. catch bad paths. --- .../public-transport/CSA/JourneyExtractor.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/planner/public-transport/CSA/JourneyExtractor.ts b/src/planner/public-transport/CSA/JourneyExtractor.ts index 111c6e89..f5378487 100644 --- a/src/planner/public-transport/CSA/JourneyExtractor.ts +++ b/src/planner/public-transport/CSA/JourneyExtractor.ts @@ -44,15 +44,19 @@ export default class JourneyExtractor { if (this.checkBestArrivalTime(profile, transfers, departureStop, arrivalStop)) { this.setBestArrivalTime(departureStop, arrivalStop, profile.arrivalTimes[transfers]); - const journey = await this.extractJourney( - arrivalStop, - profile, - transfers, - filteredProfilesByStop, - query, - ); - - journeys.push(journey); + try { + const journey = await this.extractJourney( + arrivalStop, + profile, + transfers, + filteredProfilesByStop, + query, + ); + + journeys.push(journey); + } catch (e) { + console.warn(e); + } } } } From cbf7e6bafcb91a03120ef81120cd28992095ff0d Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Thu, 15 Nov 2018 10:13:01 +0100 Subject: [PATCH 17/97] Fix test --- .../ld-fetch/ConnectionsIteratorLDFetch.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.test.ts b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.test.ts index b66dca01..14cff7ab 100644 --- a/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.test.ts +++ b/src/fetcher/connections/ld-fetch/ConnectionsIteratorLDFetch.test.ts @@ -1,5 +1,6 @@ import "jest"; import LdFetch from "ldfetch"; +import TravelMode from "../../../TravelMode"; import ConnectionsIteratorLDFetch from "./ConnectionsIteratorLDFetch"; const CONNECTIONS_TO_LOAD = 200; // Should be more than contained on first page @@ -10,7 +11,12 @@ test("[ConnectionsIteratorLDFetch] iterate forwards", async () => { backward: false, lowerBoundDate: new Date(2018, 10, 2, 10), }; - const iterator = new ConnectionsIteratorLDFetch("https://graph.irail.be/sncb/connections", new LdFetch(), config); + const iterator = new ConnectionsIteratorLDFetch( + "https://graph.irail.be/sncb/connections", + TravelMode.Train, + new LdFetch(), + config, + ); const dummyIterable = { [Symbol.asyncIterator]: () => iterator, From 1adf2071f253caa54bc6261d8cb5f39c007fd44f Mon Sep 17 00:00:00 2001 From: Simon Vanneste Date: Thu, 15 Nov 2018 13:00:25 +0100 Subject: [PATCH 18/97] Add basic webpack config --- .gitignore | 3 +++ package.json | 11 ++++++++--- src/demo.ts | 34 ++++++++++++++++++++++++++++++++++ src/index.ts | 33 +-------------------------------- webpack.config.js | 22 ++++++++++++++++++++++ 5 files changed, 68 insertions(+), 35 deletions(-) create mode 100644 src/demo.ts create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore index 69caa028..70efd824 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ node_modules/* .idea/* dist docs +bundle.js +bundle.js.map +stats.json diff --git a/package.json b/package.json index b770ba5f..8d7ae20c 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,10 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc", + "webpack": "webpack --config webpack.config.js --mode=production", + "webpack-stats": "npm run webpack -- --display-modules --json > stats.json", "test": "jest", - "debug": "ndb node dist/index.js --debug" + "debug": "ndb node dist/demo.js --debug", }, "dependencies": { "haversine": "^1.1.0", @@ -16,7 +18,6 @@ "ldfetch": "^1.1.1-alpha", "n3": "0.11.2", "reflect-metadata": "^0.1.12", - "typescript": "^3.1.3", "uritemplate": "^0.3.4" }, "devDependencies": { @@ -26,6 +27,10 @@ "jest": "^23.6.0", "prettier": "1.14.3", "ts-jest": "^23.10.4", - "tslint": "^5.11.0" + "ts-loader": "^5.3.0", + "tslint": "^5.11.0", + "typescript": "^3.1.6", + "webpack": "^4.25.1", + "webpack-cli": "^3.1.2" } } diff --git a/src/demo.ts b/src/demo.ts new file mode 100644 index 00000000..24861302 --- /dev/null +++ b/src/demo.ts @@ -0,0 +1,34 @@ +import Planner from "./Planner"; +import Units from "./util/Units"; + +const isDebugging = process && process.argv.includes("--debug"); + +const planner = new Planner(); + +(async () => { + if (isDebugging) { // tslint:disable-next-line:no-debugger + debugger; + } + + 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 + }); + + console.log(JSON.stringify(roadOnlyResult, null, " ")); + + console.time("Public transport planner"); + + 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 + minimumDepartureTime: new Date(), + maximumTransferDuration: Units.fromHours(.5), + }); + + console.timeEnd("Public transport planner"); + console.log(publicTransportResult.paths.length); + console.log(publicTransportResult); +})(); diff --git a/src/index.ts b/src/index.ts index cff93bec..99ba7489 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,36 +1,5 @@ import "isomorphic-fetch"; import "reflect-metadata"; import Planner from "./Planner"; -import Units from "./util/Units"; -const isDebugging = process && process.argv.includes("--debug"); - -const planner = new Planner(); - -(async () => { - if (isDebugging) { // tslint:disable-next-line:no-debugger - debugger; - } - - 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 - }); - - console.log(JSON.stringify(roadOnlyResult, null, " ")); - - console.time("Public transport planner"); - - 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 - minimumDepartureTime: new Date(), - maximumTransferDuration: Units.fromHours(.5), - }); - - console.timeEnd("Public transport planner"); - console.log(publicTransportResult.paths.length); - console.log(publicTransportResult); -})(); +export default Planner; diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..e88063b9 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,22 @@ +const path = require("path"); + +module.exports = { + entry: "./src/index.ts", + devtool: 'cheap-module-source-map', + module: { + rules: [ + { + test: /\.ts$/, + use: "ts-loader", + exclude: /node_modules/ + } + ] + }, + resolve: { + extensions: [".ts", ".js"] + }, + output: { + filename: "bundle.js", + path: path.resolve(__dirname) + } +}; From 627705ff601ef3e4f78347d6e27a860017a4eb5d Mon Sep 17 00:00:00 2001 From: Pieter Colpaert Date: Thu, 15 Nov 2018 13:17:33 +0100 Subject: [PATCH 19/97] Added documentation with typedoc --- .gitignore | 1 - README.md | 9 +- docs/code/assets/css/main.css | 865 ++++++ docs/code/assets/css/main.css.map | 7 + docs/code/assets/images/icons.png | Bin 0 -> 9487 bytes docs/code/assets/images/icons@2x.png | Bin 0 -> 27740 bytes docs/code/assets/images/widgets.png | Bin 0 -> 480 bytes docs/code/assets/images/widgets@2x.png | Bin 0 -> 855 bytes docs/code/assets/js/main.js | 5 + docs/code/assets/js/search.js | 3 + docs/code/classes/_constants_.constants.html | 245 ++ docs/code/classes/_context_.context.html | 259 ++ ...rcomunica_.connectionsfetchercomunica.html | 290 ++ ...tcherdelijn_.connectionsfetcherdelijn.html | 344 +++ ...herldfetch_.connectionsfetcherldfetch.html | 359 +++ ...nsfetchernmbs_.connectionsfetchernmbs.html | 344 +++ ...orldfetch_.connectionsiteratorldfetch.html | 729 +++++ ...fetchermerge_.connectionsfetchermerge.html | 315 +++ ...eratormerge_.connectionsiteratormerge.html | 487 ++++ ...rnmbstest_.connectionsfetchernmbstest.html | 424 +++ ...fetchernmbsjson_.stopsfetchernmbsjson.html | 350 +++ ...topsfetcherdelijn_.stopsfetcherdelijn.html | 308 +++ ...psfetcherldfetch_.stopsfetcherldfetch.html | 449 +++ ...ch_stopsfetchernmbs_.stopsfetchernmbs.html | 307 +++ ..._stopsfetcherproxy_.stopsfetcherproxy.html | 327 +++ docs/code/classes/_planner_.planner.html | 287 ++ docs/code/classes/_planner_path_.path.html | 301 ++ ...ture_earliestarrival_.earliestarrival.html | 217 ++ ...t_csa_data_structure_profile_.profile.html | 280 ++ ...sa_journeyextractor_.journeyextractor.html | 448 +++ ...ort_csa_util_profileutil_.profileutil.html | 249 ++ ...ile_.publictransportplannercsaprofile.html | 724 +++++ ...plannereat_.publictransportplannereat.html | 231 ++ ...dplannerbirdseye_.roadplannerbirdseye.html | 258 ++ docs/code/classes/_planner_step_.step.html | 391 +++ ...irdseye_.reachablestopsfinderbirdseye.html | 285 ++ ...d_.reachablestopsfinderbirdseyecached.html | 342 +++ ...nner_.reachablestopsfinderroadplanner.html | 302 ++ ...reachablestopsfinderroadplannercached.html | 359 +++ ...olverdefault_.locationresolverdefault.html | 333 +++ ...ueryrunnerdefault_.queryrunnerdefault.html | 422 +++ docs/code/classes/_util_geo_.geo.html | 249 ++ docs/code/classes/_util_units_.units.html | 333 +++ docs/code/classes/_util_vectors_.vectors.html | 255 ++ docs/code/enums/_travelmode_.travelmode.html | 223 ++ docs/code/globals.html | 448 +++ docs/code/index.html | 372 +++ ..._connections_iconnection_.iconnection.html | 315 +++ ...nnectionsfetcher_.iconnectionsfetcher.html | 269 ++ ...cherconfig_.iconnectionsfetcherconfig.html | 231 ++ ...h_connectionsiteratorldfetch_.ientity.html | 178 ++ ...onnectionsiteratorldfetch_.ientitymap.html | 182 ++ .../_fetcher_stops_istop_.istop.html | 245 ++ ...er_stops_istopsfetcher_.istopsfetcher.html | 284 ++ ...etchermediator_.istopsfetchermediator.html | 264 ++ ...s_json_stopsfetchernmbsjson_.istopmap.html | 182 ++ ..._stopsfetcherldfetch_.ipartialstopmap.html | 182 ++ ...d_fetch_stopsfetcherldfetch_.istopmap.html | 182 ++ .../_interfaces_ilocation_.ilocation.html | 231 ++ .../interfaces/_interfaces_ipath_.ipath.html | 209 ++ ...obabilisticvalue_.iprobabilisticvalue.html | 275 ++ .../_interfaces_iquery_.iquery.html | 343 +++ ...interfaces_iqueryresult_.iqueryresult.html | 217 ++ .../interfaces/_interfaces_istep_.istep.html | 293 ++ .../_planner_iplanner_.iplanner.html | 232 ++ ...ebytransfers_.iarrivaltimebytransfers.html | 2450 +++++++++++++++++ ...arrivalbytrip_.iearliestarrivalbytrip.html | 176 ++ ...ture_iprofilesbystop_.iprofilesbystop.html | 176 ++ ...sportplanner_.ipublictransportplanner.html | 237 ++ ...anner_road_iroadplanner_.iroadplanner.html | 236 ++ ...ireachablestopsfinder_.ireachablestop.html | 220 ++ ...blestopsfinder_.ireachablestopsfinder.html | 242 ++ ..._ilocationresolver_.ilocationresolver.html | 230 ++ ...ery_runner_iqueryrunner_.iqueryrunner.html | 222 ++ ...runner_iresolvedquery_.iresolvedquery.html | 329 +++ docs/code/modules/_constants_.html | 170 ++ docs/code/modules/_context_.html | 170 ++ ..._comunica_connectionsfetchercomunica_.html | 170 ++ .../_fetcher_connections_iconnection_.html | 170 ++ ...cher_connections_iconnectionsfetcher_.html | 170 ++ ...onnections_iconnectionsfetcherconfig_.html | 170 ++ ...ns_ld_fetch_connectionsfetcherdelijn_.html | 192 ++ ...s_ld_fetch_connectionsfetcherldfetch_.html | 170 ++ ...ions_ld_fetch_connectionsfetchernmbs_.html | 192 ++ ..._ld_fetch_connectionsiteratorldfetch_.html | 183 ++ ...etch_connectionsiteratorldfetch_test_.html | 183 ++ ...ctions_merge_connectionsfetchermerge_.html | 170 ++ ...tions_merge_connectionsiteratormerge_.html | 170 ++ ...er_connections_tests_connection_data_.html | 183 ++ ...ons_tests_connectionsfetchernmbstest_.html | 170 ++ docs/code/modules/_fetcher_helpers_.html | 264 ++ docs/code/modules/_fetcher_stops_istop_.html | 170 ++ .../_fetcher_stops_istopsfetcher_.html | 170 ++ ..._fetcher_stops_istopsfetchermediator_.html | 170 ++ ...cher_stops_json_stopsfetchernmbsjson_.html | 201 ++ ...er_stops_ld_fetch_stopsfetcherdelijn_.html | 192 ++ ...ops_ld_fetch_stopsfetcherdelijn_test_.html | 183 ++ ...r_stops_ld_fetch_stopsfetcherldfetch_.html | 183 ++ ...cher_stops_ld_fetch_stopsfetchernmbs_.html | 192 ++ ...stops_ld_fetch_stopsfetchernmbs_test_.html | 154 ++ ...etcher_stops_proxy_stopsfetcherproxy_.html | 170 ++ docs/code/modules/_index_.html | 197 ++ docs/code/modules/_interfaces_ilocation_.html | 170 ++ docs/code/modules/_interfaces_ipath_.html | 170 ++ .../_interfaces_iprobabilisticvalue_.html | 170 ++ docs/code/modules/_interfaces_iquery_.html | 170 ++ .../modules/_interfaces_iqueryresult_.html | 170 ++ docs/code/modules/_interfaces_istep_.html | 170 ++ docs/code/modules/_interfaces_units_.html | 211 ++ docs/code/modules/_inversify_config_.html | 183 ++ docs/code/modules/_planner_.html | 170 ++ docs/code/modules/_planner_iplanner_.html | 170 ++ docs/code/modules/_planner_path_.html | 170 ++ ...t_csa_data_structure_earliestarrival_.html | 170 ++ ...ta_structure_iarrivaltimebytransfers_.html | 170 ++ ...ata_structure_iearliestarrivalbytrip_.html | 170 ++ ...t_csa_data_structure_iprofilesbystop_.html | 170 ++ ...transport_csa_data_structure_profile_.html | 170 ++ ...ublic_transport_csa_journeyextractor_.html | 170 ++ ...ublic_transport_csa_util_profileutil_.html | 170 ++ ...ic_transport_ipublictransportplanner_.html | 170 ++ ...ort_publictransportplannercsaprofile_.html | 170 ++ ...ublictransportplannercsaprofile_test_.html | 154 ++ ..._transport_publictransportplannereat_.html | 170 ++ .../modules/_planner_road_iroadplanner_.html | 170 ++ .../_planner_road_roadplannerbirdseye_.html | 170 ++ ...lanner_road_roadplannerbirdseye_test_.html | 197 ++ docs/code/modules/_planner_step_.html | 170 ++ ..._planner_stops_ireachablestopsfinder_.html | 174 ++ ...r_stops_reachablestopsfinderbirdseye_.html | 170 ++ ...ps_reachablestopsfinderbirdseye_test_.html | 197 ++ ...s_reachablestopsfinderbirdseyecached_.html | 170 ++ ...tops_reachablestopsfinderroadplanner_.html | 170 ++ ...reachablestopsfinderroadplanner_test_.html | 211 ++ ...eachablestopsfinderroadplannercached_.html | 170 ++ .../_query_runner_ilocationresolver_.html | 170 ++ .../modules/_query_runner_iqueryrunner_.html | 170 ++ .../_query_runner_iresolvedquery_.html | 170 ++ ...query_runner_locationresolverdefault_.html | 170 ++ ..._runner_locationresolverdefault_test_.html | 183 ++ .../_query_runner_queryrunnerdefault_.html | 170 ++ docs/code/modules/_travelmode_.html | 170 ++ docs/code/modules/_types_.html | 273 ++ docs/code/modules/_util_geo_.html | 170 ++ docs/code/modules/_util_units_.html | 170 ++ docs/code/modules/_util_units_test_.html | 154 ++ docs/code/modules/_util_vectors_.html | 170 ++ package.json | 8 +- 148 files changed, 35336 insertions(+), 6 deletions(-) create mode 100644 docs/code/assets/css/main.css create mode 100644 docs/code/assets/css/main.css.map create mode 100644 docs/code/assets/images/icons.png create mode 100644 docs/code/assets/images/icons@2x.png create mode 100644 docs/code/assets/images/widgets.png create mode 100644 docs/code/assets/images/widgets@2x.png create mode 100644 docs/code/assets/js/main.js create mode 100644 docs/code/assets/js/search.js create mode 100644 docs/code/classes/_constants_.constants.html create mode 100644 docs/code/classes/_context_.context.html create mode 100644 docs/code/classes/_fetcher_connections_comunica_connectionsfetchercomunica_.connectionsfetchercomunica.html create mode 100644 docs/code/classes/_fetcher_connections_ld_fetch_connectionsfetcherdelijn_.connectionsfetcherdelijn.html create mode 100644 docs/code/classes/_fetcher_connections_ld_fetch_connectionsfetcherldfetch_.connectionsfetcherldfetch.html create mode 100644 docs/code/classes/_fetcher_connections_ld_fetch_connectionsfetchernmbs_.connectionsfetchernmbs.html create mode 100644 docs/code/classes/_fetcher_connections_ld_fetch_connectionsiteratorldfetch_.connectionsiteratorldfetch.html create mode 100644 docs/code/classes/_fetcher_connections_merge_connectionsfetchermerge_.connectionsfetchermerge.html create mode 100644 docs/code/classes/_fetcher_connections_merge_connectionsiteratormerge_.connectionsiteratormerge.html create mode 100644 docs/code/classes/_fetcher_connections_tests_connectionsfetchernmbstest_.connectionsfetchernmbstest.html create mode 100644 docs/code/classes/_fetcher_stops_json_stopsfetchernmbsjson_.stopsfetchernmbsjson.html create mode 100644 docs/code/classes/_fetcher_stops_ld_fetch_stopsfetcherdelijn_.stopsfetcherdelijn.html create mode 100644 docs/code/classes/_fetcher_stops_ld_fetch_stopsfetcherldfetch_.stopsfetcherldfetch.html create mode 100644 docs/code/classes/_fetcher_stops_ld_fetch_stopsfetchernmbs_.stopsfetchernmbs.html create mode 100644 docs/code/classes/_fetcher_stops_proxy_stopsfetcherproxy_.stopsfetcherproxy.html create mode 100644 docs/code/classes/_planner_.planner.html create mode 100644 docs/code/classes/_planner_path_.path.html create mode 100644 docs/code/classes/_planner_public_transport_csa_data_structure_earliestarrival_.earliestarrival.html create mode 100644 docs/code/classes/_planner_public_transport_csa_data_structure_profile_.profile.html create mode 100644 docs/code/classes/_planner_public_transport_csa_journeyextractor_.journeyextractor.html create mode 100644 docs/code/classes/_planner_public_transport_csa_util_profileutil_.profileutil.html create mode 100644 docs/code/classes/_planner_public_transport_publictransportplannercsaprofile_.publictransportplannercsaprofile.html create mode 100644 docs/code/classes/_planner_public_transport_publictransportplannereat_.publictransportplannereat.html create mode 100644 docs/code/classes/_planner_road_roadplannerbirdseye_.roadplannerbirdseye.html create mode 100644 docs/code/classes/_planner_step_.step.html create mode 100644 docs/code/classes/_planner_stops_reachablestopsfinderbirdseye_.reachablestopsfinderbirdseye.html create mode 100644 docs/code/classes/_planner_stops_reachablestopsfinderbirdseyecached_.reachablestopsfinderbirdseyecached.html create mode 100644 docs/code/classes/_planner_stops_reachablestopsfinderroadplanner_.reachablestopsfinderroadplanner.html create mode 100644 docs/code/classes/_planner_stops_reachablestopsfinderroadplannercached_.reachablestopsfinderroadplannercached.html create mode 100644 docs/code/classes/_query_runner_locationresolverdefault_.locationresolverdefault.html create mode 100644 docs/code/classes/_query_runner_queryrunnerdefault_.queryrunnerdefault.html create mode 100644 docs/code/classes/_util_geo_.geo.html create mode 100644 docs/code/classes/_util_units_.units.html create mode 100644 docs/code/classes/_util_vectors_.vectors.html create mode 100644 docs/code/enums/_travelmode_.travelmode.html create mode 100644 docs/code/globals.html create mode 100644 docs/code/index.html create mode 100644 docs/code/interfaces/_fetcher_connections_iconnection_.iconnection.html create mode 100644 docs/code/interfaces/_fetcher_connections_iconnectionsfetcher_.iconnectionsfetcher.html create mode 100644 docs/code/interfaces/_fetcher_connections_iconnectionsfetcherconfig_.iconnectionsfetcherconfig.html create mode 100644 docs/code/interfaces/_fetcher_connections_ld_fetch_connectionsiteratorldfetch_.ientity.html create mode 100644 docs/code/interfaces/_fetcher_connections_ld_fetch_connectionsiteratorldfetch_.ientitymap.html create mode 100644 docs/code/interfaces/_fetcher_stops_istop_.istop.html create mode 100644 docs/code/interfaces/_fetcher_stops_istopsfetcher_.istopsfetcher.html create mode 100644 docs/code/interfaces/_fetcher_stops_istopsfetchermediator_.istopsfetchermediator.html create mode 100644 docs/code/interfaces/_fetcher_stops_json_stopsfetchernmbsjson_.istopmap.html create mode 100644 docs/code/interfaces/_fetcher_stops_ld_fetch_stopsfetcherldfetch_.ipartialstopmap.html create mode 100644 docs/code/interfaces/_fetcher_stops_ld_fetch_stopsfetcherldfetch_.istopmap.html create mode 100644 docs/code/interfaces/_interfaces_ilocation_.ilocation.html create mode 100644 docs/code/interfaces/_interfaces_ipath_.ipath.html create mode 100644 docs/code/interfaces/_interfaces_iprobabilisticvalue_.iprobabilisticvalue.html create mode 100644 docs/code/interfaces/_interfaces_iquery_.iquery.html create mode 100644 docs/code/interfaces/_interfaces_iqueryresult_.iqueryresult.html create mode 100644 docs/code/interfaces/_interfaces_istep_.istep.html create mode 100644 docs/code/interfaces/_planner_iplanner_.iplanner.html create mode 100644 docs/code/interfaces/_planner_public_transport_csa_data_structure_iarrivaltimebytransfers_.iarrivaltimebytransfers.html create mode 100644 docs/code/interfaces/_planner_public_transport_csa_data_structure_iearliestarrivalbytrip_.iearliestarrivalbytrip.html create mode 100644 docs/code/interfaces/_planner_public_transport_csa_data_structure_iprofilesbystop_.iprofilesbystop.html create mode 100644 docs/code/interfaces/_planner_public_transport_ipublictransportplanner_.ipublictransportplanner.html create mode 100644 docs/code/interfaces/_planner_road_iroadplanner_.iroadplanner.html create mode 100644 docs/code/interfaces/_planner_stops_ireachablestopsfinder_.ireachablestop.html create mode 100644 docs/code/interfaces/_planner_stops_ireachablestopsfinder_.ireachablestopsfinder.html create mode 100644 docs/code/interfaces/_query_runner_ilocationresolver_.ilocationresolver.html create mode 100644 docs/code/interfaces/_query_runner_iqueryrunner_.iqueryrunner.html create mode 100644 docs/code/interfaces/_query_runner_iresolvedquery_.iresolvedquery.html create mode 100644 docs/code/modules/_constants_.html create mode 100644 docs/code/modules/_context_.html create mode 100644 docs/code/modules/_fetcher_connections_comunica_connectionsfetchercomunica_.html create mode 100644 docs/code/modules/_fetcher_connections_iconnection_.html create mode 100644 docs/code/modules/_fetcher_connections_iconnectionsfetcher_.html create mode 100644 docs/code/modules/_fetcher_connections_iconnectionsfetcherconfig_.html create mode 100644 docs/code/modules/_fetcher_connections_ld_fetch_connectionsfetcherdelijn_.html create mode 100644 docs/code/modules/_fetcher_connections_ld_fetch_connectionsfetcherldfetch_.html create mode 100644 docs/code/modules/_fetcher_connections_ld_fetch_connectionsfetchernmbs_.html create mode 100644 docs/code/modules/_fetcher_connections_ld_fetch_connectionsiteratorldfetch_.html create mode 100644 docs/code/modules/_fetcher_connections_ld_fetch_connectionsiteratorldfetch_test_.html create mode 100644 docs/code/modules/_fetcher_connections_merge_connectionsfetchermerge_.html create mode 100644 docs/code/modules/_fetcher_connections_merge_connectionsiteratormerge_.html create mode 100644 docs/code/modules/_fetcher_connections_tests_connection_data_.html create mode 100644 docs/code/modules/_fetcher_connections_tests_connectionsfetchernmbstest_.html create mode 100644 docs/code/modules/_fetcher_helpers_.html create mode 100644 docs/code/modules/_fetcher_stops_istop_.html create mode 100644 docs/code/modules/_fetcher_stops_istopsfetcher_.html create mode 100644 docs/code/modules/_fetcher_stops_istopsfetchermediator_.html create mode 100644 docs/code/modules/_fetcher_stops_json_stopsfetchernmbsjson_.html create mode 100644 docs/code/modules/_fetcher_stops_ld_fetch_stopsfetcherdelijn_.html create mode 100644 docs/code/modules/_fetcher_stops_ld_fetch_stopsfetcherdelijn_test_.html create mode 100644 docs/code/modules/_fetcher_stops_ld_fetch_stopsfetcherldfetch_.html create mode 100644 docs/code/modules/_fetcher_stops_ld_fetch_stopsfetchernmbs_.html create mode 100644 docs/code/modules/_fetcher_stops_ld_fetch_stopsfetchernmbs_test_.html create mode 100644 docs/code/modules/_fetcher_stops_proxy_stopsfetcherproxy_.html create mode 100644 docs/code/modules/_index_.html create mode 100644 docs/code/modules/_interfaces_ilocation_.html create mode 100644 docs/code/modules/_interfaces_ipath_.html create mode 100644 docs/code/modules/_interfaces_iprobabilisticvalue_.html create mode 100644 docs/code/modules/_interfaces_iquery_.html create mode 100644 docs/code/modules/_interfaces_iqueryresult_.html create mode 100644 docs/code/modules/_interfaces_istep_.html create mode 100644 docs/code/modules/_interfaces_units_.html create mode 100644 docs/code/modules/_inversify_config_.html create mode 100644 docs/code/modules/_planner_.html create mode 100644 docs/code/modules/_planner_iplanner_.html create mode 100644 docs/code/modules/_planner_path_.html create mode 100644 docs/code/modules/_planner_public_transport_csa_data_structure_earliestarrival_.html create mode 100644 docs/code/modules/_planner_public_transport_csa_data_structure_iarrivaltimebytransfers_.html create mode 100644 docs/code/modules/_planner_public_transport_csa_data_structure_iearliestarrivalbytrip_.html create mode 100644 docs/code/modules/_planner_public_transport_csa_data_structure_iprofilesbystop_.html create mode 100644 docs/code/modules/_planner_public_transport_csa_data_structure_profile_.html create mode 100644 docs/code/modules/_planner_public_transport_csa_journeyextractor_.html create mode 100644 docs/code/modules/_planner_public_transport_csa_util_profileutil_.html create mode 100644 docs/code/modules/_planner_public_transport_ipublictransportplanner_.html create mode 100644 docs/code/modules/_planner_public_transport_publictransportplannercsaprofile_.html create mode 100644 docs/code/modules/_planner_public_transport_publictransportplannercsaprofile_test_.html create mode 100644 docs/code/modules/_planner_public_transport_publictransportplannereat_.html create mode 100644 docs/code/modules/_planner_road_iroadplanner_.html create mode 100644 docs/code/modules/_planner_road_roadplannerbirdseye_.html create mode 100644 docs/code/modules/_planner_road_roadplannerbirdseye_test_.html create mode 100644 docs/code/modules/_planner_step_.html create mode 100644 docs/code/modules/_planner_stops_ireachablestopsfinder_.html create mode 100644 docs/code/modules/_planner_stops_reachablestopsfinderbirdseye_.html create mode 100644 docs/code/modules/_planner_stops_reachablestopsfinderbirdseye_test_.html create mode 100644 docs/code/modules/_planner_stops_reachablestopsfinderbirdseyecached_.html create mode 100644 docs/code/modules/_planner_stops_reachablestopsfinderroadplanner_.html create mode 100644 docs/code/modules/_planner_stops_reachablestopsfinderroadplanner_test_.html create mode 100644 docs/code/modules/_planner_stops_reachablestopsfinderroadplannercached_.html create mode 100644 docs/code/modules/_query_runner_ilocationresolver_.html create mode 100644 docs/code/modules/_query_runner_iqueryrunner_.html create mode 100644 docs/code/modules/_query_runner_iresolvedquery_.html create mode 100644 docs/code/modules/_query_runner_locationresolverdefault_.html create mode 100644 docs/code/modules/_query_runner_locationresolverdefault_test_.html create mode 100644 docs/code/modules/_query_runner_queryrunnerdefault_.html create mode 100644 docs/code/modules/_travelmode_.html create mode 100644 docs/code/modules/_types_.html create mode 100644 docs/code/modules/_util_geo_.html create mode 100644 docs/code/modules/_util_units_.html create mode 100644 docs/code/modules/_util_units_test_.html create mode 100644 docs/code/modules/_util_vectors_.html diff --git a/.gitignore b/.gitignore index 69caa028..36b79881 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ node_modules/* .idea/* dist -docs diff --git a/README.md b/README.md index d37c9cbc..25699897 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ # 🛸 Planner.js +_TODO: Travis CI, NPM badge, gitter channel, license badge_ + The JavaScript library for journey planning. CDN versions: _coming in February_ For now: clone the repository, run `npm install` and use your [typescript compiler](https://www.typescriptlang.org/) (`tsc`) to create the files. -Next, use it as follows: +## Documentation -_TODO_ +Follow the documentation at https://openplannerteam.github.io/planner.js/ +## Developing + * Building the docs with typedoc: `npm run doc` + * Testing with jest: `npm test` diff --git a/docs/code/assets/css/main.css b/docs/code/assets/css/main.css new file mode 100644 index 00000000..48b3645c --- /dev/null +++ b/docs/code/assets/css/main.css @@ -0,0 +1,865 @@ +/*! normalize.css v1.1.3 | MIT License | git.io/normalize */ +/* ========================================================================== HTML5 display definitions ========================================================================== */ +/** Correct `block` display not defined in IE 6/7/8/9 and Firefox 3. */ +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } + +/** Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. */ +audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } + +/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ +audio:not([controls]) { display: none; height: 0; } + +/** Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. Known issue: no IE 6 support. */ +[hidden] { display: none; } + +/* ========================================================================== Base ========================================================================== */ +/** 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using `em` units. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ +html { font-size: 100%; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ font-family: sans-serif; } + +/** Address `font-family` inconsistency between `textarea` and other form elements. */ +button, input, select, textarea { font-family: sans-serif; } + +/** Address margins handled incorrectly in IE 6/7. */ +body { margin: 0; } + +/* ========================================================================== Links ========================================================================== */ +/** Address `outline` inconsistency between Chrome and other browsers. */ +a:focus { outline: thin dotted; } +a:active, a:hover { outline: 0; } + +/** Improve readability when focused and also mouse hovered in all browsers. */ +/* ========================================================================== Typography ========================================================================== */ +/** Address font sizes and margins set differently in IE 6/7. Address font sizes within `section` and `article` in Firefox 4+, Safari 5, and Chrome. */ +h1 { font-size: 2em; margin: 0.67em 0; } + +h2 { font-size: 1.5em; margin: 0.83em 0; } + +h3 { font-size: 1.17em; margin: 1em 0; } + +h4, .tsd-index-panel h3 { font-size: 1em; margin: 1.33em 0; } + +h5 { font-size: 0.83em; margin: 1.67em 0; } + +h6 { font-size: 0.67em; margin: 2.33em 0; } + +/** Address styling not present in IE 7/8/9, Safari 5, and Chrome. */ +abbr[title] { border-bottom: 1px dotted; } + +/** Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. */ +b, strong { font-weight: bold; } + +blockquote { margin: 1em 40px; } + +/** Address styling not present in Safari 5 and Chrome. */ +dfn { font-style: italic; } + +/** Address differences between Firefox and other browsers. Known issue: no IE 6/7 normalization. */ +hr { box-sizing: content-box; height: 0; } + +/** Address styling not present in IE 6/7/8/9. */ +mark { background: #ff0; color: #000; } + +/** Address margins set differently in IE 6/7. */ +p, pre { margin: 1em 0; } + +/** Correct font family set oddly in IE 6, Safari 4/5, and Chrome. */ +code, kbd, pre, samp { font-family: monospace, serif; _font-family: "courier new", monospace; font-size: 1em; } + +/** Improve readability of pre-formatted text in all browsers. */ +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } + +/** Address CSS quotes not supported in IE 6/7. */ +q { quotes: none; } +q:before, q:after { content: ""; content: none; } + +/** Address `quotes` property not supported in Safari 4. */ +/** Address inconsistent and variable font size in all browsers. */ +small { font-size: 80%; } + +/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ +sub { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } + +sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; top: -0.5em; } + +sub { bottom: -0.25em; } + +/* ========================================================================== Lists ========================================================================== */ +/** Address margins set differently in IE 6/7. */ +dl, menu, ol, ul { margin: 1em 0; } + +dd { margin: 0 0 0 40px; } + +/** Address paddings set differently in IE 6/7. */ +menu, ol, ul { padding: 0 0 0 40px; } + +/** Correct list images handled incorrectly in IE 7. */ +nav ul, nav ol { list-style: none; list-style-image: none; } + +/* ========================================================================== Embedded content ========================================================================== */ +/** 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 2. Improve image quality when scaled in IE 7. */ +img { border: 0; /* 1 */ -ms-interpolation-mode: bicubic; } + +/* 2 */ +/** Correct overflow displayed oddly in IE 9. */ +svg:not(:root) { overflow: hidden; } + +/* ========================================================================== Figures ========================================================================== */ +/** Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. */ +figure, form { margin: 0; } + +/* ========================================================================== Forms ========================================================================== */ +/** Correct margin displayed oddly in IE 6/7. */ +/** Define consistent border, margin, and padding. */ +fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } + +/** 1. Correct color not being inherited in IE 6/7/8/9. 2. Correct text not wrapping in Firefox 3. 3. Correct alignment displayed oddly in IE 6/7. */ +legend { border: 0; /* 1 */ padding: 0; white-space: normal; /* 2 */ *margin-left: -7px; } + +/* 3 */ +/** 1. Correct font size not being inherited in all browsers. 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, and Chrome. 3. Improve appearance and consistency in all browsers. */ +button, input, select, textarea { font-size: 100%; /* 1 */ margin: 0; /* 2 */ vertical-align: baseline; /* 3 */ *vertical-align: middle; } + +/* 3 */ +/** Address Firefox 3+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ +button, input { line-height: normal; } + +/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. Correct `select` style inheritance in Firefox 4+ and Opera. */ +button, select { text-transform: none; } + +/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. 4. Remove inner spacing in IE 7 without affecting normal text inputs. Known issue: inner spacing remains in IE 6. */ +button, html input[type="button"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; } + +/* 4 */ +input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ *overflow: visible; } + +/* 4 */ +/** Re-set default cursor for disabled elements. */ +button[disabled], html input[disabled] { cursor: default; } + +/** 1. Address box sizing set to content-box in IE 8/9. 2. Remove excess padding in IE 8/9. 3. Remove excess padding in IE 7. Known issue: excess padding remains in IE 6. */ +input { /* 3 */ } +input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ *height: 13px; /* 3 */ *width: 13px; } +input[type="search"] { -webkit-appearance: textfield; /* 1 */ /* 2 */ box-sizing: content-box; } +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } + +/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ +/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ +/** Remove inner padding and border in Firefox 3+. */ +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } + +/** 1. Remove default vertical scrollbar in IE 6/7/8/9. 2. Improve readability and alignment in all browsers. */ +textarea { overflow: auto; /* 1 */ vertical-align: top; } + +/* 2 */ +/* ========================================================================== Tables ========================================================================== */ +/** Remove most spacing between table cells. */ +table { border-collapse: collapse; border-spacing: 0; } + +/* Visual Studio-like style based on original C# coloring by Jason Diamond */ +.hljs { display: inline-block; padding: 0.5em; background: white; color: black; } + +.hljs-comment, .hljs-annotation, .hljs-template_comment, .diff .hljs-header, .hljs-chunk, .apache .hljs-cbracket { color: #008000; } + +.hljs-keyword, .hljs-id, .hljs-built_in, .css .smalltalk .hljs-class, .hljs-winutils, .bash .hljs-variable, .tex .hljs-command, .hljs-request, .hljs-status, .nginx .hljs-title { color: #00f; } + +.xml .hljs-tag { color: #00f; } +.xml .hljs-tag .hljs-value { color: #00f; } + +.hljs-string, .hljs-title, .hljs-parent, .hljs-tag .hljs-value, .hljs-rules .hljs-value { color: #a31515; } + +.ruby .hljs-symbol { color: #a31515; } +.ruby .hljs-symbol .hljs-string { color: #a31515; } + +.hljs-template_tag, .django .hljs-variable, .hljs-addition, .hljs-flow, .hljs-stream, .apache .hljs-tag, .hljs-date, .tex .hljs-formula, .coffeescript .hljs-attribute { color: #a31515; } + +.ruby .hljs-string, .hljs-decorator, .hljs-filter .hljs-argument, .hljs-localvars, .hljs-array, .hljs-attr_selector, .hljs-pseudo, .hljs-pi, .hljs-doctype, .hljs-deletion, .hljs-envvar, .hljs-shebang, .hljs-preprocessor, .hljs-pragma, .userType, .apache .hljs-sqbracket, .nginx .hljs-built_in, .tex .hljs-special, .hljs-prompt { color: #2b91af; } + +.hljs-phpdoc, .hljs-javadoc, .hljs-xmlDocTag { color: #808080; } + +.vhdl .hljs-typename { font-weight: bold; } +.vhdl .hljs-string { color: #666666; } +.vhdl .hljs-literal { color: #a31515; } +.vhdl .hljs-attribute { color: #00b0e8; } + +.xml .hljs-attribute { color: #f00; } + +.col > :first-child, .col-1 > :first-child, .col-2 > :first-child, .col-3 > :first-child, .col-4 > :first-child, .col-5 > :first-child, .col-6 > :first-child, .col-7 > :first-child, .col-8 > :first-child, .col-9 > :first-child, .col-10 > :first-child, .col-11 > :first-child, .tsd-panel > :first-child, ul.tsd-descriptions > li > :first-child, .col > :first-child > :first-child, .col-1 > :first-child > :first-child, .col-2 > :first-child > :first-child, .col-3 > :first-child > :first-child, .col-4 > :first-child > :first-child, .col-5 > :first-child > :first-child, .col-6 > :first-child > :first-child, .col-7 > :first-child > :first-child, .col-8 > :first-child > :first-child, .col-9 > :first-child > :first-child, .col-10 > :first-child > :first-child, .col-11 > :first-child > :first-child, .tsd-panel > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child, .col > :first-child > :first-child > :first-child, .col-1 > :first-child > :first-child > :first-child, .col-2 > :first-child > :first-child > :first-child, .col-3 > :first-child > :first-child > :first-child, .col-4 > :first-child > :first-child > :first-child, .col-5 > :first-child > :first-child > :first-child, .col-6 > :first-child > :first-child > :first-child, .col-7 > :first-child > :first-child > :first-child, .col-8 > :first-child > :first-child > :first-child, .col-9 > :first-child > :first-child > :first-child, .col-10 > :first-child > :first-child > :first-child, .col-11 > :first-child > :first-child > :first-child, .tsd-panel > :first-child > :first-child > :first-child, ul.tsd-descriptions > li > :first-child > :first-child > :first-child { margin-top: 0; } +.col > :last-child, .col-1 > :last-child, .col-2 > :last-child, .col-3 > :last-child, .col-4 > :last-child, .col-5 > :last-child, .col-6 > :last-child, .col-7 > :last-child, .col-8 > :last-child, .col-9 > :last-child, .col-10 > :last-child, .col-11 > :last-child, .tsd-panel > :last-child, ul.tsd-descriptions > li > :last-child, .col > :last-child > :last-child, .col-1 > :last-child > :last-child, .col-2 > :last-child > :last-child, .col-3 > :last-child > :last-child, .col-4 > :last-child > :last-child, .col-5 > :last-child > :last-child, .col-6 > :last-child > :last-child, .col-7 > :last-child > :last-child, .col-8 > :last-child > :last-child, .col-9 > :last-child > :last-child, .col-10 > :last-child > :last-child, .col-11 > :last-child > :last-child, .tsd-panel > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child, .col > :last-child > :last-child > :last-child, .col-1 > :last-child > :last-child > :last-child, .col-2 > :last-child > :last-child > :last-child, .col-3 > :last-child > :last-child > :last-child, .col-4 > :last-child > :last-child > :last-child, .col-5 > :last-child > :last-child > :last-child, .col-6 > :last-child > :last-child > :last-child, .col-7 > :last-child > :last-child > :last-child, .col-8 > :last-child > :last-child > :last-child, .col-9 > :last-child > :last-child > :last-child, .col-10 > :last-child > :last-child > :last-child, .col-11 > :last-child > :last-child > :last-child, .tsd-panel > :last-child > :last-child > :last-child, ul.tsd-descriptions > li > :last-child > :last-child > :last-child { margin-bottom: 0; } + +.container { max-width: 1200px; margin: 0 auto; padding: 0 40px; } +@media (max-width: 640px) { .container { padding: 0 20px; } } + +.container-main { padding-bottom: 200px; } + +.row { position: relative; margin: 0 -10px; } +.row:after { visibility: hidden; display: block; content: ""; clear: both; height: 0; } + +.col, .col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11 { box-sizing: border-box; float: left; padding: 0 10px; } + +.col-1 { width: 8.33333%; } + +.offset-1 { margin-left: 8.33333%; } + +.col-2 { width: 16.66667%; } + +.offset-2 { margin-left: 16.66667%; } + +.col-3 { width: 25%; } + +.offset-3 { margin-left: 25%; } + +.col-4 { width: 33.33333%; } + +.offset-4 { margin-left: 33.33333%; } + +.col-5 { width: 41.66667%; } + +.offset-5 { margin-left: 41.66667%; } + +.col-6 { width: 50%; } + +.offset-6 { margin-left: 50%; } + +.col-7 { width: 58.33333%; } + +.offset-7 { margin-left: 58.33333%; } + +.col-8 { width: 66.66667%; } + +.offset-8 { margin-left: 66.66667%; } + +.col-9 { width: 75%; } + +.offset-9 { margin-left: 75%; } + +.col-10 { width: 83.33333%; } + +.offset-10 { margin-left: 83.33333%; } + +.col-11 { width: 91.66667%; } + +.offset-11 { margin-left: 91.66667%; } + +.tsd-kind-icon { display: block; position: relative; padding-left: 20px; text-indent: -20px; } +.tsd-kind-icon:before { content: ''; display: inline-block; vertical-align: middle; width: 17px; height: 17px; margin: 0 3px 2px 0; background-image: url(../images/icons.png); } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-kind-icon:before { background-image: url(../images/icons@2x.png); background-size: 238px 204px; } } + +.tsd-signature.tsd-kind-icon:before { background-position: 0 -153px; } + +.tsd-kind-object-literal > .tsd-kind-icon:before { background-position: 0px -17px; } +.tsd-kind-object-literal.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -17px; } +.tsd-kind-object-literal.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -17px; } + +.tsd-kind-class > .tsd-kind-icon:before { background-position: 0px -34px; } +.tsd-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -34px; } +.tsd-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -34px; } + +.tsd-kind-class.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -51px; } +.tsd-kind-class.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -51px; } + +.tsd-kind-interface > .tsd-kind-icon:before { background-position: 0px -68px; } +.tsd-kind-interface.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -68px; } +.tsd-kind-interface.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -68px; } + +.tsd-kind-interface.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: 0px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -85px; } +.tsd-kind-interface.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -85px; } + +.tsd-kind-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-external-module > .tsd-kind-icon:before { background-position: 0px -102px; } +.tsd-kind-external-module.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -102px; } +.tsd-kind-external-module.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -102px; } + +.tsd-kind-enum > .tsd-kind-icon:before { background-position: 0px -119px; } +.tsd-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -119px; } +.tsd-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -119px; } + +.tsd-kind-enum-member > .tsd-kind-icon:before { background-position: 0px -136px; } +.tsd-kind-enum-member.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -136px; } +.tsd-kind-enum-member.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -136px; } + +.tsd-kind-signature > .tsd-kind-icon:before { background-position: 0px -153px; } +.tsd-kind-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -153px; } +.tsd-kind-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -153px; } + +.tsd-kind-type-alias > .tsd-kind-icon:before { background-position: 0px -170px; } +.tsd-kind-type-alias.tsd-is-protected > .tsd-kind-icon:before { background-position: -17px -170px; } +.tsd-kind-type-alias.tsd-is-private > .tsd-kind-icon:before { background-position: -34px -170px; } + +.tsd-kind-variable > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-variable.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-variable.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-variable.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-variable.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-variable.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-property > .tsd-kind-icon:before { background-position: -136px -0px; } +.tsd-kind-property.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -0px; } +.tsd-kind-property.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -0px; } +.tsd-kind-property.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -0px; } +.tsd-kind-property.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -0px; } +.tsd-kind-property.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -0px; } +.tsd-kind-property.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -0px; } + +.tsd-kind-get-signature > .tsd-kind-icon:before { background-position: -136px -17px; } +.tsd-kind-get-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -17px; } +.tsd-kind-get-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -17px; } +.tsd-kind-get-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -17px; } + +.tsd-kind-set-signature > .tsd-kind-icon:before { background-position: -136px -34px; } +.tsd-kind-set-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -34px; } +.tsd-kind-set-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -34px; } +.tsd-kind-set-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -34px; } + +.tsd-kind-accessor > .tsd-kind-icon:before { background-position: -136px -51px; } +.tsd-kind-accessor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -51px; } +.tsd-kind-accessor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -51px; } +.tsd-kind-accessor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -51px; } +.tsd-kind-accessor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -51px; } +.tsd-kind-accessor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -51px; } + +.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -68px; } +.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -68px; } +.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -68px; } +.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -68px; } + +.tsd-kind-function.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-function.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-method.tsd-has-type-parameter > .tsd-kind-icon:before { background-position: -136px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -85px; } +.tsd-kind-method.tsd-has-type-parameter.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -85px; } + +.tsd-kind-constructor > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-constructor-signature > .tsd-kind-icon:before { background-position: -136px -102px; } +.tsd-kind-constructor-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -102px; } +.tsd-kind-constructor-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -102px; } +.tsd-kind-constructor-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -102px; } + +.tsd-kind-index-signature > .tsd-kind-icon:before { background-position: -136px -119px; } +.tsd-kind-index-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -119px; } +.tsd-kind-index-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -119px; } +.tsd-kind-index-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -119px; } + +.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -136px; } +.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -136px; } +.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -136px; } +.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -136px; } +.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -136px; } +.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -136px; } +.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -136px; } + +.tsd-is-static > .tsd-kind-icon:before { background-position: -136px -153px; } +.tsd-is-static.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -153px; } +.tsd-is-static.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -153px; } +.tsd-is-static.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -153px; } +.tsd-is-static.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -153px; } +.tsd-is-static.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -153px; } +.tsd-is-static.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -153px; } + +.tsd-is-static.tsd-kind-function > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-function.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-function.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-method > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-method.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-method.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-call-signature > .tsd-kind-icon:before { background-position: -136px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -170px; } +.tsd-is-static.tsd-kind-call-signature.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -170px; } + +.tsd-is-static.tsd-kind-event > .tsd-kind-icon:before { background-position: -136px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-protected > .tsd-kind-icon:before { background-position: -153px -187px; } +.tsd-is-static.tsd-kind-event.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class > .tsd-kind-icon:before { background-position: -51px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-inherited > .tsd-kind-icon:before { background-position: -68px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected > .tsd-kind-icon:before { background-position: -85px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-protected.tsd-is-inherited > .tsd-kind-icon:before { background-position: -102px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-class.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum > .tsd-kind-icon:before { background-position: -170px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-protected > .tsd-kind-icon:before { background-position: -187px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-enum.tsd-is-private > .tsd-kind-icon:before { background-position: -119px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface > .tsd-kind-icon:before { background-position: -204px -187px; } +.tsd-is-static.tsd-kind-event.tsd-parent-kind-interface.tsd-is-inherited > .tsd-kind-icon:before { background-position: -221px -187px; } + +.no-transition { transition: none !important; } + +@-webkit-keyframes fade-in { from { opacity: 0; } + to { opacity: 1; } } + +@keyframes fade-in { from { opacity: 0; } + to { opacity: 1; } } +@-webkit-keyframes fade-out { from { opacity: 1; visibility: visible; } + to { opacity: 0; } } +@keyframes fade-out { from { opacity: 1; visibility: visible; } + to { opacity: 0; } } +@-webkit-keyframes fade-in-delayed { 0% { opacity: 0; } + 33% { opacity: 0; } + 100% { opacity: 1; } } +@keyframes fade-in-delayed { 0% { opacity: 0; } + 33% { opacity: 0; } + 100% { opacity: 1; } } +@-webkit-keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; } + 66% { opacity: 0; } + 100% { opacity: 0; } } +@keyframes fade-out-delayed { 0% { opacity: 1; visibility: visible; } + 66% { opacity: 0; } + 100% { opacity: 0; } } +@-webkit-keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); } + to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } } +@keyframes shift-to-left { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); } + to { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } } +@-webkit-keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@keyframes unshift-to-left { from { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@-webkit-keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@keyframes pop-in-from-right { from { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + to { -webkit-transform: translate(0, 0); transform: translate(0, 0); } } +@-webkit-keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; } + to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } } +@keyframes pop-out-to-right { from { -webkit-transform: translate(0, 0); transform: translate(0, 0); visibility: visible; } + to { -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } } +body { background: #fdfdfd; font-family: "Segoe UI", sans-serif; font-size: 16px; color: #222; } + +a { color: #4da6ff; text-decoration: none; } +a:hover { text-decoration: underline; } + +code, pre { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 0.2em; margin: 0; font-size: 14px; background-color: rgba(0, 0, 0, 0.04); } + +pre { padding: 10px; } +pre code { padding: 0; font-size: 100%; background-color: transparent; } + +.tsd-typography { line-height: 1.333em; } +.tsd-typography ul { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-typography h4, .tsd-typography .tsd-index-panel h3, .tsd-index-panel .tsd-typography h3, .tsd-typography h5, .tsd-typography h6 { font-size: 1em; margin: 0; } +.tsd-typography h5, .tsd-typography h6 { font-weight: normal; } +.tsd-typography p, .tsd-typography ul, .tsd-typography ol { margin: 1em 0; } + +@media (min-width: 901px) and (max-width: 1024px) { html.default .col-content { width: 72%; } + html.default .col-menu { width: 28%; } + html.default .tsd-navigation { padding-left: 10px; } } +@media (max-width: 900px) { html.default .col-content { float: none; width: 100%; } + html.default .col-menu { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; z-index: 1024; top: 0 !important; bottom: 0 !important; left: auto !important; right: 0 !important; width: 100%; padding: 20px 20px 0 0; max-width: 450px; visibility: hidden; background-color: #fff; -webkit-transform: translate(100%, 0); transform: translate(100%, 0); } + html.default .col-menu > *:last-child { padding-bottom: 20px; } + html.default .overlay { content: ""; display: block; position: fixed; z-index: 1023; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.75); visibility: hidden; } + html.default.to-has-menu .overlay { -webkit-animation: fade-in 0.4s; animation: fade-in 0.4s; } + html.default.to-has-menu header, html.default.to-has-menu footer, html.default.to-has-menu .col-content { -webkit-animation: shift-to-left 0.4s; animation: shift-to-left 0.4s; } + html.default.to-has-menu .col-menu { -webkit-animation: pop-in-from-right 0.4s; animation: pop-in-from-right 0.4s; } + html.default.from-has-menu .overlay { -webkit-animation: fade-out 0.4s; animation: fade-out 0.4s; } + html.default.from-has-menu header, html.default.from-has-menu footer, html.default.from-has-menu .col-content { -webkit-animation: unshift-to-left 0.4s; animation: unshift-to-left 0.4s; } + html.default.from-has-menu .col-menu { -webkit-animation: pop-out-to-right 0.4s; animation: pop-out-to-right 0.4s; } + html.default.has-menu body { overflow: hidden; } + html.default.has-menu .overlay { visibility: visible; } + html.default.has-menu header, html.default.has-menu footer, html.default.has-menu .col-content { -webkit-transform: translate(-25%, 0); transform: translate(-25%, 0); } + html.default.has-menu .col-menu { visibility: visible; -webkit-transform: translate(0, 0); transform: translate(0, 0); } } + +.tsd-page-title { padding: 70px 0 20px 0; margin: 0 0 40px 0; background: #fff; box-shadow: 0 0 5px rgba(0, 0, 0, 0.35); } +.tsd-page-title h1 { margin: 0; } + +.tsd-breadcrumb { margin: 0; padding: 0; color: #808080; } +.tsd-breadcrumb a { color: #808080; text-decoration: none; } +.tsd-breadcrumb a:hover { text-decoration: underline; } +.tsd-breadcrumb li { display: inline; } +.tsd-breadcrumb li:after { content: " / "; } + +html.minimal .container { margin: 0; } +html.minimal .container-main { padding-top: 50px; padding-bottom: 0; } +html.minimal .content-wrap { padding-left: 300px; } +html.minimal .tsd-navigation { position: fixed !important; overflow: auto; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; box-sizing: border-box; z-index: 1; left: 0; top: 40px; bottom: 0; width: 300px; padding: 20px; margin: 0; } +html.minimal .tsd-member .tsd-member { margin-left: 0; } +html.minimal .tsd-page-toolbar { position: fixed; z-index: 2; } +html.minimal #tsd-filter .tsd-filter-group { right: 0; -webkit-transform: none; transform: none; } +html.minimal footer { background-color: transparent; } +html.minimal footer .container { padding: 0; } +html.minimal .tsd-generator { padding: 0; } +@media (max-width: 900px) { html.minimal .tsd-navigation { display: none; } + html.minimal .content-wrap { padding-left: 0; } } + +dl.tsd-comment-tags { overflow: hidden; } +dl.tsd-comment-tags dt { clear: both; float: left; padding: 1px 5px; margin: 0 10px 0 0; border-radius: 4px; border: 1px solid #808080; color: #808080; font-size: 0.8em; font-weight: normal; } +dl.tsd-comment-tags dd { margin: 0 0 10px 0; } +dl.tsd-comment-tags p { margin: 0; } + +.tsd-panel.tsd-comment .lead { font-size: 1.1em; line-height: 1.333em; margin-bottom: 2em; } +.tsd-panel.tsd-comment .lead:last-child { margin-bottom: 0; } + +.toggle-protected .tsd-is-private { display: none; } + +.toggle-public .tsd-is-private, .toggle-public .tsd-is-protected, .toggle-public .tsd-is-private-protected { display: none; } + +.toggle-inherited .tsd-is-inherited { display: none; } + +.toggle-only-exported .tsd-is-not-exported { display: none; } + +.toggle-externals .tsd-is-external { display: none; } + +#tsd-filter { position: relative; display: inline-block; height: 40px; vertical-align: bottom; } +.no-filter #tsd-filter { display: none; } +#tsd-filter .tsd-filter-group { display: inline-block; height: 40px; vertical-align: bottom; white-space: nowrap; } +#tsd-filter input { display: none; } +@media (max-width: 900px) { #tsd-filter .tsd-filter-group { display: block; position: absolute; top: 40px; right: 20px; height: auto; background-color: #fff; visibility: hidden; -webkit-transform: translate(50%, 0); transform: translate(50%, 0); box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } + .has-options #tsd-filter .tsd-filter-group { visibility: visible; } + .to-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-in 0.2s; animation: fade-in 0.2s; } + .from-has-options #tsd-filter .tsd-filter-group { -webkit-animation: fade-out 0.2s; animation: fade-out 0.2s; } + #tsd-filter label, #tsd-filter .tsd-select { display: block; padding-right: 20px; } } + +footer { border-top: 1px solid #eee; background-color: #fff; } +footer.with-border-bottom { border-bottom: 1px solid #eee; } +footer .tsd-legend-group { font-size: 0; } +footer .tsd-legend { display: inline-block; width: 25%; padding: 0; font-size: 16px; list-style: none; line-height: 1.333em; vertical-align: top; } +@media (max-width: 900px) { footer .tsd-legend { width: 50%; } } + +.tsd-hierarchy { list-style: square; padding: 0 0 0 20px; margin: 0; } +.tsd-hierarchy .target { font-weight: bold; } + +.tsd-index-panel .tsd-index-content { margin-bottom: -30px !important; } +.tsd-index-panel .tsd-index-section { margin-bottom: 30px !important; } +.tsd-index-panel h3 { margin: 0 -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-index-panel ul.tsd-index-list { -webkit-column-count: 3; -moz-column-count: 3; -ms-column-count: 3; -o-column-count: 3; column-count: 3; -webkit-column-gap: 20px; -moz-column-gap: 20px; -ms-column-gap: 20px; -o-column-gap: 20px; column-gap: 20px; padding: 0; list-style: none; line-height: 1.333em; } +@media (max-width: 900px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 1; -moz-column-count: 1; -ms-column-count: 1; -o-column-count: 1; column-count: 1; } } +@media (min-width: 901px) and (max-width: 1024px) { .tsd-index-panel ul.tsd-index-list { -webkit-column-count: 2; -moz-column-count: 2; -ms-column-count: 2; -o-column-count: 2; column-count: 2; } } +.tsd-index-panel ul.tsd-index-list li { -webkit-column-break-inside: avoid; -moz-column-break-inside: avoid; -ms-column-break-inside: avoid; -o-column-break-inside: avoid; column-break-inside: avoid; -webkit-page-break-inside: avoid; -moz-page-break-inside: avoid; -ms-page-break-inside: avoid; -o-page-break-inside: avoid; page-break-inside: avoid; } +.tsd-index-panel a, .tsd-index-panel .tsd-parent-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-parent-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-parent-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-parent-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-kind-module a { color: #9600ff; } +.tsd-index-panel .tsd-kind-interface a { color: #7da01f; } +.tsd-index-panel .tsd-kind-enum a { color: #cc9900; } +.tsd-index-panel .tsd-kind-class a { color: #4da6ff; } +.tsd-index-panel .tsd-is-private a { color: #808080; } + +.tsd-flag { display: inline-block; padding: 1px 5px; border-radius: 4px; color: #fff; background-color: #808080; text-indent: 0; font-size: 14px; font-weight: normal; } + +.tsd-anchor { position: absolute; top: -100px; } + +.tsd-member { position: relative; } +.tsd-member .tsd-anchor + h3 { margin-top: 0; margin-bottom: 0; border-bottom: none; } + +.tsd-navigation { padding: 0 0 0 40px; } +.tsd-navigation a { display: block; padding-top: 2px; padding-bottom: 2px; border-left: 2px solid transparent; color: #222; text-decoration: none; transition: border-left-color 0.1s; } +.tsd-navigation a:hover { text-decoration: underline; } +.tsd-navigation ul { margin: 0; padding: 0; list-style: none; } +.tsd-navigation li { padding: 0; } + +.tsd-navigation.primary { padding-bottom: 40px; } +.tsd-navigation.primary a { display: block; padding-top: 6px; padding-bottom: 6px; } +.tsd-navigation.primary ul li a { padding-left: 5px; } +.tsd-navigation.primary ul li li a { padding-left: 25px; } +.tsd-navigation.primary ul li li li a { padding-left: 45px; } +.tsd-navigation.primary ul li li li li a { padding-left: 65px; } +.tsd-navigation.primary ul li li li li li a { padding-left: 85px; } +.tsd-navigation.primary ul li li li li li li a { padding-left: 105px; } +.tsd-navigation.primary > ul { border-bottom: 1px solid #eee; } +.tsd-navigation.primary li { border-top: 1px solid #eee; } +.tsd-navigation.primary li.current > a { font-weight: bold; } +.tsd-navigation.primary li.label span { display: block; padding: 20px 0 6px 5px; color: #808080; } +.tsd-navigation.primary li.globals + li > span, .tsd-navigation.primary li.globals + li > a { padding-top: 20px; } + +.tsd-navigation.secondary ul { transition: opacity 0.2s; } +.tsd-navigation.secondary ul li a { padding-left: 25px; } +.tsd-navigation.secondary ul li li a { padding-left: 45px; } +.tsd-navigation.secondary ul li li li a { padding-left: 65px; } +.tsd-navigation.secondary ul li li li li a { padding-left: 85px; } +.tsd-navigation.secondary ul li li li li li a { padding-left: 105px; } +.tsd-navigation.secondary ul li li li li li li a { padding-left: 125px; } +.tsd-navigation.secondary ul.current a { border-left-color: #eee; } +.tsd-navigation.secondary li.focus > a, .tsd-navigation.secondary ul.current li.focus > a { border-left-color: #000; } +.tsd-navigation.secondary li.current { margin-top: 20px; margin-bottom: 20px; border-left-color: #eee; } +.tsd-navigation.secondary li.current > a { font-weight: bold; } + +@media (min-width: 901px) { .menu-sticky-wrap { position: static; } + .no-csspositionsticky .menu-sticky-wrap.sticky { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current { position: fixed; } + .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.before-current, .no-csspositionsticky .menu-sticky-wrap.sticky-current ul.after-current { opacity: 0; } + .no-csspositionsticky .menu-sticky-wrap.sticky-bottom { position: absolute; top: auto !important; left: auto !important; bottom: 0; right: 0; } + .csspositionsticky .menu-sticky-wrap.sticky { position: -webkit-sticky; position: sticky; } + .csspositionsticky .menu-sticky-wrap.sticky-current { position: -webkit-sticky; position: sticky; } } + +.tsd-panel { margin: 20px 0; padding: 20px; background-color: #fff; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +.tsd-panel:empty { display: none; } +.tsd-panel > h1, .tsd-panel > h2, .tsd-panel > h3 { margin: 1.5em -20px 10px -20px; padding: 0 20px 10px 20px; border-bottom: 1px solid #eee; } +.tsd-panel > h1.tsd-before-signature, .tsd-panel > h2.tsd-before-signature, .tsd-panel > h3.tsd-before-signature { margin-bottom: 0; border-bottom: 0; } +.tsd-panel table { display: block; width: 100%; overflow: auto; margin-top: 10px; word-break: normal; word-break: keep-all; } +.tsd-panel table th { font-weight: bold; } +.tsd-panel table th, .tsd-panel table td { padding: 6px 13px; border: 1px solid #ddd; } +.tsd-panel table tr { background-color: #fff; border-top: 1px solid #ccc; } +.tsd-panel table tr:nth-child(2n) { background-color: #f8f8f8; } + +.tsd-panel-group { margin: 60px 0; } +.tsd-panel-group > h1, .tsd-panel-group > h2, .tsd-panel-group > h3 { padding-left: 20px; padding-right: 20px; } + +#tsd-search { transition: background-color 0.2s; } +#tsd-search .title { position: relative; z-index: 2; } +#tsd-search .field { position: absolute; left: 0; top: 0; right: 40px; height: 40px; } +#tsd-search .field input { box-sizing: border-box; position: relative; top: -50px; z-index: 1; width: 100%; padding: 0 10px; opacity: 0; outline: 0; border: 0; background: transparent; color: #222; } +#tsd-search .field label { position: absolute; overflow: hidden; right: -40px; } +#tsd-search .field input, #tsd-search .title { transition: opacity 0.2s; } +#tsd-search .results { position: absolute; visibility: hidden; top: 40px; width: 100%; margin: 0; padding: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); } +#tsd-search .results li { padding: 0 10px; background-color: #fdfdfd; } +#tsd-search .results li:nth-child(even) { background-color: #fff; } +#tsd-search .results li.state { display: none; } +#tsd-search .results li.current, #tsd-search .results li:hover { background-color: #eee; } +#tsd-search .results a { display: block; } +#tsd-search .results a:before { top: 10px; } +#tsd-search .results span.parent { color: #808080; font-weight: normal; } +#tsd-search.has-focus { background-color: #eee; } +#tsd-search.has-focus .field input { top: 0; opacity: 1; } +#tsd-search.has-focus .title { z-index: 0; opacity: 0; } +#tsd-search.has-focus .results { visibility: visible; } +#tsd-search.loading .results li.state.loading { display: block; } +#tsd-search.failure .results li.state.failure { display: block; } + +.tsd-signature { margin: 0 0 1em 0; padding: 10px; border: 1px solid #eee; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; font-size: 14px; } +.tsd-signature.tsd-kind-icon { padding-left: 30px; } +.tsd-signature.tsd-kind-icon:before { top: 10px; left: 10px; } +.tsd-panel > .tsd-signature { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signature.tsd-kind-icon:before { left: 20px; } + +.tsd-signature-symbol { color: #808080; font-weight: normal; } + +.tsd-signature-type { font-style: italic; font-weight: normal; } + +.tsd-signatures { padding: 0; margin: 0 0 1em 0; border: 1px solid #eee; } +.tsd-signatures .tsd-signature { margin: 0; border-width: 1px 0 0 0; transition: background-color 0.1s; } +.tsd-signatures .tsd-signature:first-child { border-top-width: 0; } +.tsd-signatures .tsd-signature.current { background-color: #eee; } +.tsd-signatures.active > .tsd-signature { cursor: pointer; } +.tsd-panel > .tsd-signatures { margin-left: -20px; margin-right: -20px; border-width: 1px 0; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon { padding-left: 40px; } +.tsd-panel > .tsd-signatures .tsd-signature.tsd-kind-icon:before { left: 20px; } +.tsd-panel > a.anchor + .tsd-signatures { border-top-width: 0; margin-top: -20px; } + +ul.tsd-descriptions { position: relative; overflow: hidden; transition: height 0.3s; padding: 0; list-style: none; } +ul.tsd-descriptions.active > .tsd-description { display: none; } +ul.tsd-descriptions.active > .tsd-description.current { display: block; } +ul.tsd-descriptions.active > .tsd-description.fade-in { -webkit-animation: fade-in-delayed 0.3s; animation: fade-in-delayed 0.3s; } +ul.tsd-descriptions.active > .tsd-description.fade-out { -webkit-animation: fade-out-delayed 0.3s; animation: fade-out-delayed 0.3s; position: absolute; display: block; top: 0; left: 0; right: 0; opacity: 0; visibility: hidden; } +ul.tsd-descriptions h4, ul.tsd-descriptions .tsd-index-panel h3, .tsd-index-panel ul.tsd-descriptions h3 { font-size: 16px; margin: 1em 0 0.5em 0; } + +ul.tsd-parameters, ul.tsd-type-parameters { list-style: square; margin: 0; padding-left: 20px; } +ul.tsd-parameters > li.tsd-parameter-siganture, ul.tsd-type-parameters > li.tsd-parameter-siganture { list-style: none; margin-left: -20px; } +ul.tsd-parameters h5, ul.tsd-type-parameters h5 { font-size: 16px; margin: 1em 0 0.5em 0; } +ul.tsd-parameters .tsd-comment, ul.tsd-type-parameters .tsd-comment { margin-top: -0.5em; } + +.tsd-sources { font-size: 14px; color: #808080; margin: 0 0 1em 0; } +.tsd-sources a { color: #808080; text-decoration: underline; } +.tsd-sources ul, .tsd-sources p { margin: 0 !important; } +.tsd-sources ul { list-style: none; padding: 0; } + +.tsd-page-toolbar { position: absolute; z-index: 1; top: 0; left: 0; width: 100%; height: 40px; color: #333; background: #fff; border-bottom: 1px solid #eee; } +.tsd-page-toolbar a { color: #333; text-decoration: none; } +.tsd-page-toolbar a.title { font-weight: bold; } +.tsd-page-toolbar a.title:hover { text-decoration: underline; } +.tsd-page-toolbar .table-wrap { display: table; width: 100%; height: 40px; } +.tsd-page-toolbar .table-cell { display: table-cell; position: relative; white-space: nowrap; line-height: 40px; } +.tsd-page-toolbar .table-cell:first-child { width: 100%; } + +.tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { content: ""; display: inline-block; width: 40px; height: 40px; margin: 0 -8px 0 0; background-image: url(../images/widgets.png); background-repeat: no-repeat; text-indent: -1024px; vertical-align: bottom; } +@media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { .tsd-widget:before, .tsd-select .tsd-select-label:before, .tsd-select .tsd-select-list li:before { background-image: url(../images/widgets@2x.png); background-size: 320px 40px; } } + +.tsd-widget { display: inline-block; overflow: hidden; opacity: 0.6; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-widget:hover { opacity: 0.8; } +.tsd-widget.active { opacity: 1; background-color: #eee; } +.tsd-widget.no-caption { width: 40px; } +.tsd-widget.no-caption:before { margin: 0; } +.tsd-widget.search:before { background-position: 0 0; } +.tsd-widget.menu:before { background-position: -40px 0; } +.tsd-widget.options:before { background-position: -80px 0; } +.tsd-widget.options, .tsd-widget.menu { display: none; } +@media (max-width: 900px) { .tsd-widget.options, .tsd-widget.menu { display: inline-block; } } +input[type=checkbox] + .tsd-widget:before { background-position: -120px 0; } +input[type=checkbox]:checked + .tsd-widget:before { background-position: -160px 0; } + +.tsd-select { position: relative; display: inline-block; height: 40px; transition: opacity 0.1s, background-color 0.2s; vertical-align: bottom; cursor: pointer; } +.tsd-select .tsd-select-label { opacity: 0.6; transition: opacity 0.2s; } +.tsd-select .tsd-select-label:before { background-position: -240px 0; } +.tsd-select.active .tsd-select-label { opacity: 0.8; } +.tsd-select.active .tsd-select-list { visibility: visible; opacity: 1; transition-delay: 0s; } +.tsd-select .tsd-select-list { position: absolute; visibility: hidden; top: 40px; left: 0; margin: 0; padding: 0; opacity: 0; list-style: none; box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); transition: visibility 0s 0.2s, opacity 0.2s; } +.tsd-select .tsd-select-list li { padding: 0 20px 0 0; background-color: #fdfdfd; } +.tsd-select .tsd-select-list li:before { background-position: 40px 0; } +.tsd-select .tsd-select-list li:nth-child(even) { background-color: #fff; } +.tsd-select .tsd-select-list li:hover { background-color: #eee; } +.tsd-select .tsd-select-list li.selected:before { background-position: -200px 0; } +@media (max-width: 900px) { .tsd-select .tsd-select-list { top: 0; left: auto; right: 100%; margin-right: -5px; } + .tsd-select .tsd-select-label:before { background-position: -280px 0; } } + +img { max-width: 100%; } diff --git a/docs/code/assets/css/main.css.map b/docs/code/assets/css/main.css.map new file mode 100644 index 00000000..bc17fe48 --- /dev/null +++ b/docs/code/assets/css/main.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "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", +"sources": ["../../../../src/default/assets/css/vendors/_normalize.sass","../../../../src/default/assets/css/vendors/_highlight.js.sass","../../../../src/default/assets/css/setup/_mixins.sass","../../../../src/default/assets/css/setup/_grid.sass","../../../../src/default/assets/css/setup/_icons.scss","../../../../src/default/assets/css/setup/_animations.sass","../../../../src/default/assets/css/setup/_typography.sass","../../../../src/default/assets/css/_constants.sass","../../../../src/default/assets/css/layouts/_default.sass","../../../../src/default/assets/css/layouts/_minimal.sass","../../../../src/default/assets/css/elements/_comment.sass","../../../../src/default/assets/css/elements/_filter.sass","../../../../src/default/assets/css/elements/_footer.sass","../../../../src/default/assets/css/elements/_hierarchy.sass","../../../../src/default/assets/css/elements/_index.sass","../../../../src/default/assets/css/elements/_member.sass","../../../../src/default/assets/css/elements/_navigation.sass","../../../../src/default/assets/css/elements/_panel.sass","../../../../src/default/assets/css/elements/_search.sass","../../../../src/default/assets/css/elements/_signatures.sass","../../../../src/default/assets/css/elements/_sources.sass","../../../../src/default/assets/css/elements/_toolbar.sass","../../../../src/default/assets/css/elements/_images.sass"], +"names": [], +"file": "main.css" +} diff --git a/docs/code/assets/images/icons.png b/docs/code/assets/images/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2d11573b9ade711ee30a2bd4f38e6f9ea82281 GIT binary patch literal 9487 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 JrMIm@{|8^qBMkrm literal 0 HcmV?d00001 diff --git a/docs/code/assets/images/icons@2x.png b/docs/code/assets/images/icons@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8932ba20ffa431194b8cebc977c731bec3ee23e0 GIT binary patch literal 27740 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 U&wLp6h6BX&boFyt=akR{0G%$)mH+?% literal 0 HcmV?d00001 diff --git a/docs/code/assets/images/widgets@2x.png b/docs/code/assets/images/widgets@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4bbbd57272f3b28f47527d4951ad10f950b8ad43 GIT binary patch literal 855 zcmeAS@N?(olHy`uVBq!ia0y~yU}^xe12~w0Jcmn z@(X6T|9^jgLcx21{)7exgY)a>N6m2F0<`Rqr;B4q1>>88jUdw-7W`c)zLE*mq8W2H z-<&Jl_Hco5BuC5n@AbF5GD82~-e8-v=#zCyUX0F-o}8pPfAv`!GN$ff+TL<~@kgt} z62eO?_|&+>xBmM$@p|z`tIKEdpPf8%qI>4r7@jn<=eta*{3~?g(zz{Ke9zc-G^gr? z-7foa?LcS!hmbwzru}ICvbWLlW8;+l-}!^=c32!^nV`+`C*;0-*Y%l94pC;Cb3GXz zzSf%a!{gVr{Y_lVuUj+a)*Ca+!-Hu%xmP&&X-2CuANY8^i{D7Kg6qzP zXz_ps9+lN8ESH{K4`yu&b~I>N9xGlE&;2u*b?+Go!AhN?m-bxlLvtC#MzDF2kFzfHJ1W7ybqdefSqVhbOykd*Yi%EDuhs z4wF{ft^bv2+DDnKb8gj1FuvcV`M}luS>lO<^)8x>y1#R;a=-ZKwWTQQb)ioBbi;zh zD!f5V)8581to1LL7c9!l^PSC$NBPYif!_vAZhmL4)v4U)4UsrLYiH_9rmQDd?)(e5 z^pcH>qvBg*i0dus2r*mp4;zKvu=P#s-ti;2obl`NjjwoYd>e(oo#j_uyRb<7Pv^If zzZ|mGHmV)8^tbO%^>eqMw(@7(&3g{jEp-Najo7V75xI_ZHK*FA`elF{r5}E*d7+j_R literal 0 HcmV?d00001 diff --git a/docs/code/assets/js/main.js b/docs/code/assets/js/main.js new file mode 100644 index 00000000..528a3b02 --- /dev/null +++ b/docs/code/assets/js/main.js @@ -0,0 +1,5 @@ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function s(a){var b=a.length,c=n.type(a);return"function"!==c&&!n.isWindow(a)&&(!(1!==a.nodeType||!b)||("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a))}function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}function D(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),"string"==typeof(c=a.getAttribute(d))){try{c="true"===c||"false"!==c&&("null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c)}catch(e){}M.set(a,b,c)}else c=void 0;return c}function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("