diff --git a/package-lock.json b/package-lock.json index 64c34e46..3fb50a6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "plannerjs", - "version": "0.2.1", + "version": "0.2.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1524,7 +1524,8 @@ "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true }, "binary-extensions": { "version": "1.13.1", @@ -1533,9 +1534,9 @@ "dev": true }, "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, "bn.js": { @@ -1825,9 +1826,9 @@ }, "dependencies": { "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -1839,9 +1840,9 @@ } }, "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", "dev": true }, "lru-cache": { @@ -1854,9 +1855,9 @@ } }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true } } @@ -4099,9 +4100,9 @@ "dev": true }, "handlebars": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.3.1.tgz", - "integrity": "sha512-c0HoNHzDiHpBt4Kqe99N8tdLPKAnGCQ73gYMPWtAYM4PwGnf7xl8PBUHJqh9ijlzt2uQKaSRxbXRt+rZ7M2/kA==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -7766,9 +7767,9 @@ } }, "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", "dev": true }, "serve-index": { @@ -8488,9 +8489,9 @@ "dev": true }, "terser": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.2.tgz", - "integrity": "sha512-obxk4x19Zlzj9zY4QeXj9iPCb5W8YGn4v3pn4/fHj0Nw8+R7N02Kvwvz9VpOItCZZD8RC+vnYCDL0gP6FAJ7Xg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", + "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -8499,16 +8500,16 @@ } }, "terser-webpack-plugin": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", - "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "dev": true, "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", @@ -9151,14 +9152,23 @@ "dev": true }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz", + "integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==", "dev": true, "optional": true, "requires": { - "commander": "~2.20.0", + "commander": "~2.20.3", "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "optional": true + } } }, "underscore": { @@ -9270,6 +9280,11 @@ "punycode": "^2.1.0" } }, + "uri-templates": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/uri-templates/-/uri-templates-0.2.0.tgz", + "integrity": "sha1-K1eEURzJCYaHMekjPCaAl9ELSZ8=" + }, "uritemplate": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/uritemplate/-/uritemplate-0.3.4.tgz", diff --git a/package.json b/package.json index d1affdc8..2086390c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "plannerjs", - "version": "0.2.1", + "version": "0.2.2", "description": "The JavaScript framework for journey planning.", "main": "lib/index.js", "license": "MIT", @@ -44,6 +44,7 @@ "reflect-metadata": "^0.1.13", "tiles-in-bbox": "^1.0.2", "tinyqueue": "^2.0.2", + "uri-templates": "^0.2.0", "uritemplate": "^0.3.4" }, "pre-commit": [ diff --git a/src/analytics/footpaths/main.ts b/src/analytics/footpaths/main.ts index a7181bfc..dc371aef 100644 --- a/src/analytics/footpaths/main.ts +++ b/src/analytics/footpaths/main.ts @@ -3,12 +3,11 @@ import { Delaunay } from "d3-delaunay"; import fs = require("fs"); import "isomorphic-fetch"; import "reflect-metadata"; - +import defaultContainer from "../../configs/default"; import IStopsProvider from "../../fetcher/stops/IStopsProvider"; import ILocation from "../../interfaces/ILocation"; import IPath from "../../interfaces/IPath"; import { DistanceM } from "../../interfaces/units"; -import defaultContainer from "../../inversify.config"; import IRoadPlanner from "../../planner/road/IRoadPlanner"; import TYPES from "../../types"; import Geo from "../../util/Geo"; diff --git a/src/analytics/isochrones/main.ts b/src/analytics/isochrones/main.ts index d9999d0e..778290c0 100644 --- a/src/analytics/isochrones/main.ts +++ b/src/analytics/isochrones/main.ts @@ -2,6 +2,7 @@ import { EventEmitter } from "events"; import "isomorphic-fetch"; import "reflect-metadata"; import inBBox from "tiles-in-bbox"; +import defaultContainer from "../../configs/default"; import Profile from "../../entities/profile/Profile"; import { RoutableTileCoordinate } from "../../entities/tiles/coordinate"; import RoutableTileRegistry from "../../entities/tiles/registry"; @@ -11,7 +12,6 @@ import EventType from "../../events/EventType"; import ProfileProvider from "../../fetcher/profiles/ProfileProviderDefault"; import IRoutableTileProvider from "../../fetcher/tiles/IRoutableTileProvider"; import ILocation from "../../interfaces/ILocation"; -import defaultContainer from "../../inversify.config"; import { IPathTree } from "../../pathfinding/pathfinder"; import PathfinderProvider from "../../pathfinding/PathfinderProvider"; import TYPES from "../../types"; @@ -41,7 +41,7 @@ export default class IsochroneGenerator { RoutingPhase.Base, ); this.pathfinderProvider = container.get(TYPES.PathfinderProvider); - this.registry = container.get(TYPES.RoutableTileRegistry); + this.registry = RoutableTileRegistry.getInstance(); this.profileProvider = container.get(TYPES.ProfileProvider); this.eventBus = EventBus.getInstance(); this.reachedTiles = new Set(); diff --git a/src/analytics/traffic/demo.html b/src/analytics/traffic/demo.html new file mode 100644 index 00000000..46bf052c --- /dev/null +++ b/src/analytics/traffic/demo.html @@ -0,0 +1,243 @@ + + + + + + + Isochrone demo + + + + + + + +
+
+

+

+ +

+

+ Latitude:
+
+ Longitude:
+

+ +

+

+ +

+

+ +

+
+
+
+ +
+
+ + + + + + \ No newline at end of file diff --git a/src/analytics/traffic/main.ts b/src/analytics/traffic/main.ts new file mode 100644 index 00000000..22224780 --- /dev/null +++ b/src/analytics/traffic/main.ts @@ -0,0 +1,207 @@ +import { AsyncIterator } from "asynciterator"; +import { EventEmitter } from "events"; +import "isomorphic-fetch"; +import "reflect-metadata"; +import inBBox from "tiles-in-bbox"; +import defaultContainer from "../../configs/default"; +import Profile from "../../entities/profile/Profile"; +import { RoutableTileCoordinate } from "../../entities/tiles/coordinate"; +import RoutableTileRegistry from "../../entities/tiles/registry"; +import { RoutableTile } from "../../entities/tiles/tile"; +import RoutingPhase from "../../enums/RoutingPhase"; +import EventBus from "../../events/EventBus"; +import EventType from "../../events/EventType"; +import ProfileProvider from "../../fetcher/profiles/ProfileProviderDefault"; +import IRoutableTileProvider from "../../fetcher/tiles/IRoutableTileProvider"; +import ILocation from "../../interfaces/ILocation"; +import { IPathTree } from "../../pathfinding/pathfinder"; +import PathfinderProvider from "../../pathfinding/PathfinderProvider"; +import TYPES from "../../types"; +import Geo from "../../util/Geo"; +import { toTileCoordinate } from "../../util/Tiles"; + +export default class TrafficEstimator { + private pathfinderProvider: PathfinderProvider; + private baseTileProvider: IRoutableTileProvider; + private transitTileProvider: IRoutableTileProvider; + private reachedTiles: Set; + private startPoint: ILocation; + private registry: RoutableTileRegistry; + private profileProvider: ProfileProvider; + private eventBus: EventEmitter; + + private activeProfile: Promise; + private loaded: Promise; + private embedded: boolean; + private showIncremental: boolean; + + constructor(point: ILocation, container = defaultContainer) { + this.baseTileProvider = container.getTagged( + TYPES.RoutableTileProvider, + "phase", + RoutingPhase.Base, + ); + this.transitTileProvider = container.getTagged( + TYPES.RoutableTileProvider, + "phase", + RoutingPhase.Transit, + ); + this.pathfinderProvider = container.get(TYPES.PathfinderProvider); + this.registry = RoutableTileRegistry.getInstance(); + this.profileProvider = container.get(TYPES.ProfileProvider); + this.eventBus = EventBus.getInstance(); + this.reachedTiles = new Set(); + this.startPoint = point; + this.showIncremental = false; + this.embedded = false; + + this.setProfileID("http://hdelva.be/profile/car"); + } + + public enableIncrementalResults() { + this.showIncremental = true; + } + + public async setDevelopmentProfile(blob: object) { + const id = await this.profileProvider.parseDevelopmentProfile(blob); + this.setProfileID(id); + } + + public async setProfileID(profileID: string) { + this.activeProfile = this.profileProvider.getProfile(profileID); + this.embedded = false; + } + + public async * startSimulation(nodes: Set, timeM: number) { + const data = Array.from(nodes); + + const profile = await this.activeProfile; + console.log(`Using the ${profile.getID()} profile`); + + const pathfinder = this.pathfinderProvider.getShortestPathAlgorithm(profile); + + while (true) { + const index1 = data[Math.floor(Math.random() * data.length)]; + + /* + const pathfinder = this.pathfinderProvider.getShortestPathTreeAlgorithm(profile); + + const pathTree: IPathTree = await pathfinder.start(index1, timeM * 60 * 1000); + yield this.pruneTree(pathTree); + */ + + const index2 = data[Math.floor(Math.random() * data.length)]; + + if (index1 !== index2) { + const path = await pathfinder.queryPath(index1, index2, 90 * 1000 / 60 * timeM); + yield path; + } + + } + } + + public async * getAreaTree(maxDuration: number, steps: number) { + if (!this.embedded) { + await this.embedBeginPoint(this.startPoint); + this.embedded = true; + } + + await this.loaded; + const profile = await this.activeProfile; + + console.log(`Using the ${profile.getID()} profile`); + const pathfinder = this.pathfinderProvider.getShortestPathTreeAlgorithm(profile); + + let pathTree: IPathTree = await pathfinder.start(Geo.getId(this.startPoint), maxDuration / steps); + yield this.pruneTree(pathTree); + + for (let i = 1; i <= steps; i++) { + console.log(maxDuration / steps * i); + pathTree = await pathfinder.continue(maxDuration / steps * i); + yield this.pruneTree(pathTree); + } + } + + private pruneTree(tree: IPathTree): IPathTree { + const result = {}; + for (const [id, branch] of Object.entries(tree)) { + if (branch.previousNode) { + result[id] = branch; + } + } + return result; + } + + private async embedBeginPoint(from: ILocation) { + const zoom = 14; + const padding = 0.005; + + const fromBBox = { + top: from.latitude + padding, + bottom: from.latitude - padding, + left: from.longitude - padding, + right: from.longitude + padding, + }; + + const fromTileCoords = inBBox.tilesInBbox(fromBBox, zoom).map((obj) => { + const coordinate = new RoutableTileCoordinate(zoom, obj.x, obj.y); + this.fetchTile(coordinate, true); + return coordinate; + }); + + // this won't download anything new + // but we need the tile data to embed the starting location + const fromTileset = await this.baseTileProvider.getMultipleByTileCoords(fromTileCoords); + await this.pathfinderProvider.embedLocation(from, fromTileset); + } + + private async fetchTile(coordinate: RoutableTileCoordinate, base) { + let tileId: string; + if (base) { + tileId = this.baseTileProvider.getIdForTileCoords(coordinate); + } else { + tileId = this.transitTileProvider.getIdForTileCoords(coordinate); + } + if (!this.reachedTiles.has(tileId)) { + this.eventBus.emit(EventType.FetchTile, coordinate); + + const profile = await this.activeProfile; + const pathfinder = this.pathfinderProvider.getShortestPathTreeAlgorithm(profile); + let tile: RoutableTile; + if (base) { + tile = await this.baseTileProvider.getByTileCoords(coordinate); + } else { + tile = await this.transitTileProvider.getByTileCoords(coordinate); + } + this.reachedTiles.add(tileId); + const boundaryNodes: Set = new Set(); + + for (const nodeId of tile.getNodes()) { + pathfinder.removeBreakPoint(nodeId); + const node = this.registry.getNode(nodeId); + if (!tile.contains(node)) { + boundaryNodes.add(nodeId); + } + + if (this.showIncremental) { + pathfinder.setBreakPoint(nodeId, async (on: string) => { + const innerNode = self.registry.getNode(on); + if (innerNode) { + self.eventBus.emit(EventType.PointReached, innerNode); + } + }); + } + } + + const self = this; + for (const nodeId of boundaryNodes) { + const node = self.registry.getNode(nodeId); + const boundaryTileCoordinate = toTileCoordinate(node.latitude, node.longitude); + + pathfinder.setBreakPoint(nodeId, async (on: string) => { + await self.fetchTile(boundaryTileCoordinate, false); + }); + } + } + } +} diff --git a/src/configs/basic_train.ts b/src/configs/basic_train.ts index a9730c7b..341cd9c8 100644 --- a/src/configs/basic_train.ts +++ b/src/configs/basic_train.ts @@ -10,6 +10,8 @@ import ConnectionsFetcherRaw from "../fetcher/connections/ConnectionsFetcherRaw" import ConnectionsProviderDefault from "../fetcher/connections/ConnectionsProviderDefault"; import IConnectionsFetcher from "../fetcher/connections/IConnectionsFetcher"; import IConnectionsProvider from "../fetcher/connections/IConnectionsProvider"; +import HydraTemplateFetcherDefault from "../fetcher/hydra/HydraTemplateFetcherDefault"; +import IHydraTemplateFetcher from "../fetcher/hydra/IHydraTemplateFetcher"; import LDFetch from "../fetcher/LDFetch"; import IProfileFetcher from "../fetcher/profiles/IProfileFetcher"; import IProfileProvider from "../fetcher/profiles/IProfileProvider"; @@ -24,7 +26,6 @@ import IRoutableTileProvider from "../fetcher/tiles/IRoutableTileProvider"; import RoutableTileFetcherRaw from "../fetcher/tiles/RoutableTileFetcherRaw"; import RoutableTileProviderDefault from "../fetcher/tiles/RoutableTileProviderDefault"; import RoutableTileProviderTransit from "../fetcher/tiles/RoutableTileProviderTransit"; - import { LDLoader } from "../loader/ldloader"; import { BidirDijkstra } from "../pathfinding/bidirdijkstra/BidirDijkstra"; import DijkstraTree from "../pathfinding/dijkstra-tree/DijkstraTree"; @@ -50,6 +51,8 @@ container.bind(TYPES.Context).to(Context).inSingletonScope(); container.bind(TYPES.QueryRunner).to(QueryRunnerExponential); container.bind(TYPES.LocationResolver).to(LocationResolverConvenience); +container.bind(TYPES.HydraTemplateFetcher).to(HydraTemplateFetcherDefault).inSingletonScope(); + // TODO, make this a fixed property of the planner itself container.bind(TYPES.JourneyExtractor) .to(JourneyExtractorProfile); @@ -101,7 +104,6 @@ container.bind>(TYPES.StopsFetcherFactory) }, ); -container.bind(TYPES.RoutableTileRegistry).to(RoutableTileRegistry).inSingletonScope(); container.bind(TYPES.RoutableTileFetcher).to(RoutableTileFetcherRaw).inSingletonScope(); container.bind(TYPES.RoutableTileProvider) .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Base); diff --git a/src/configs/bus_train.ts b/src/configs/bus_train.ts index 6144e58d..e5253556 100644 --- a/src/configs/bus_train.ts +++ b/src/configs/bus_train.ts @@ -1,19 +1,19 @@ import { Container, interfaces } from "inversify"; import Catalog from "../Catalog"; -import catalogOVl from "../catalog.delijn.oostvlaanderen"; -import catalogMivb from "../catalog.mivb"; +import catalogDeLijn from "../catalog.delijn"; import catalogNmbs from "../catalog.nmbs"; import Context from "../Context"; -import RoutableTileRegistry from "../entities/tiles/registry"; import ReachableStopsSearchPhase from "../enums/ReachableStopsSearchPhase"; import RoutingPhase from "../enums/RoutingPhase"; import TravelMode from "../enums/TravelMode"; import ConnectionsFetcherRaw from "../fetcher/connections/ConnectionsFetcherRaw"; -import ConnectionsProviderMerge from "../fetcher/connections/ConnectionsProviderMerge"; +import ConnectionsProviderMerge from "../fetcher/connections/ConnectionsProviderDefault"; import IConnectionsFetcher from "../fetcher/connections/IConnectionsFetcher"; import IConnectionsProvider from "../fetcher/connections/IConnectionsProvider"; import FootpathsProviderRaw from "../fetcher/footpaths/FootpathsProviderRaw"; import IFootpathsFetcher from "../fetcher/footpaths/IFootpathsProvider"; +import HydraTemplateFetcherDefault from "../fetcher/hydra/HydraTemplateFetcherDefault"; +import IHydraTemplateFetcher from "../fetcher/hydra/IHydraTemplateFetcher"; import LDFetch from "../fetcher/LDFetch"; import IProfileFetcher from "../fetcher/profiles/IProfileFetcher"; import IProfileProvider from "../fetcher/profiles/IProfileProvider"; @@ -54,6 +54,8 @@ container.bind(TYPES.Context).to(Context).inSingletonScope(); container.bind(TYPES.QueryRunner).to(QueryRunnerDefault); container.bind(TYPES.LocationResolver).to(LocationResolverConvenience); +container.bind(TYPES.HydraTemplateFetcher).to(HydraTemplateFetcherDefault).inSingletonScope(); + // TODO, make this a fixed property of the planner itself container.bind(TYPES.JourneyExtractor) .to(JourneyExtractorProfile); @@ -107,7 +109,6 @@ container.bind>(TYPES.StopsFetcherFactory) }, ); -container.bind(TYPES.RoutableTileRegistry).to(RoutableTileRegistry).inSingletonScope(); container.bind(TYPES.RoutableTileFetcher).to(RoutableTileFetcherRaw).inSingletonScope(); container.bind(TYPES.RoutableTileProvider) .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Base); @@ -115,7 +116,7 @@ container.bind(TYPES.RoutableTileProvider) .to(RoutableTileProviderTransit).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Transit); // Bind catalog -const combined = Catalog.combine(catalogNmbs, catalogOVl); +const combined = Catalog.combine(catalogNmbs, catalogDeLijn); container.bind(TYPES.Catalog).toConstantValue(combined); // Init LDFetch diff --git a/src/inversify.config.ts b/src/configs/default.ts similarity index 50% rename from src/inversify.config.ts rename to src/configs/default.ts index 395482e1..4bf5af34 100644 --- a/src/inversify.config.ts +++ b/src/configs/default.ts @@ -1,63 +1,60 @@ -import { EventEmitter } from "events"; import { Container, interfaces } from "inversify"; -import Catalog from "./Catalog"; -import catalogDeLijn from "./catalog.delijn"; -import catalogMivb from "./catalog.mivb"; -import catalogNmbs from "./catalog.nmbs"; -import catalogTec from "./catalog.tec"; -import Context from "./Context"; -import RoutableTileRegistry from "./entities/tiles/registry"; -import ReachableStopsSearchPhase from "./enums/ReachableStopsSearchPhase"; -import RoutingPhase from "./enums/RoutingPhase"; -import TravelMode from "./enums/TravelMode"; -import ConnectionsFetcherRaw from "./fetcher/connections/ConnectionsFetcherRaw"; -import ConnectionsProviderDefault from "./fetcher/connections/ConnectionsProviderDefault"; -import IConnectionsFetcher from "./fetcher/connections/IConnectionsFetcher"; -import IConnectionsProvider from "./fetcher/connections/IConnectionsProvider"; -import FootpathsProviderDefault from "./fetcher/footpaths/FootpathsProviderDefault"; -import IFootpathsFetcher from "./fetcher/footpaths/IFootpathsProvider"; -import LDFetch from "./fetcher/LDFetch"; -import IProfileFetcher from "./fetcher/profiles/IProfileFetcher"; -import IProfileProvider from "./fetcher/profiles/IProfileProvider"; -import ProfileFetcherDefault from "./fetcher/profiles/ProfileFetcherDefault"; -import ProfileProviderDefault from "./fetcher/profiles/ProfileProviderDefault"; -import IStopsFetcher from "./fetcher/stops/IStopsFetcher"; -import IStopsProvider from "./fetcher/stops/IStopsProvider"; -import StopsFetcherLDFetch from "./fetcher/stops/ld-fetch/StopsFetcherLDFetch"; -import StopsProviderDefault from "./fetcher/stops/StopsProviderDefault"; -import IRoutableTileFetcher from "./fetcher/tiles/IRoutableTileFetcher"; -import IRoutableTileProvider from "./fetcher/tiles/IRoutableTileProvider"; -import RoutableTileFetcherDefault from "./fetcher/tiles/RoutableTileFetcherDefault"; -import RoutableTileFetcherExtended from "./fetcher/tiles/RoutableTileFetcherExtended"; -import RoutableTileFetcherRaw from "./fetcher/tiles/RoutableTileFetcherRaw"; -import RoutableTileProviderDefault from "./fetcher/tiles/RoutableTileProviderDefault"; -import RoutableTileProviderTransit from "./fetcher/tiles/RoutableTileProviderTransit"; - -import { LDLoader } from "./loader/ldloader"; -import DijkstraTree from "./pathfinding/dijkstra-tree/DijkstraTree"; -import { Dijkstra } from "./pathfinding/dijkstra/Dijkstra"; -import { IShortestPathAlgorithm, IShortestPathTreeAlgorithm } from "./pathfinding/pathfinder"; -import PathfinderProvider from "./pathfinding/PathfinderProvider"; -import CSAEarliestArrival from "./planner/public-transport/CSAEarliestArrival"; -import IJourneyExtractor from "./planner/public-transport/IJourneyExtractor"; -import IPublicTransportPlanner from "./planner/public-transport/IPublicTransportPlanner"; -import JourneyExtractorProfile from "./planner/public-transport/JourneyExtractorProfile"; -import IRoadPlanner from "./planner/road/IRoadPlanner"; -import RoadPlannerPathfinding from "./planner/road/RoadPlannerPathfinding"; -import IReachableStopsFinder from "./planner/stops/IReachableStopsFinder"; -import ReachableStopsFinderDelaunay from "./planner/stops/ReachableStopsFinderDelaunay"; -import ReachableStopsFinderFootpaths from "./planner/stops/ReachableStopsFinderFootpaths"; -import QueryRunnerExponential from "./query-runner/exponential/QueryRunnerExponential"; -import ILocationResolver from "./query-runner/ILocationResolver"; -import IQueryRunner from "./query-runner/IQueryRunner"; -import LocationResolverConvenience from "./query-runner/LocationResolverConvenience"; -import TYPES from "./types"; + +import Catalog from "../Catalog"; +import catalogNmbs from "../catalog.nmbs"; +import Context from "../Context"; +import ReachableStopsSearchPhase from "../enums/ReachableStopsSearchPhase"; +import RoutingPhase from "../enums/RoutingPhase"; +import TravelMode from "../enums/TravelMode"; +import ConnectionsFetcherRaw from "../fetcher/connections/ConnectionsFetcherRaw"; +import ConnectionsProviderDefault from "../fetcher/connections/ConnectionsProviderDefault"; +import IConnectionsFetcher from "../fetcher/connections/IConnectionsFetcher"; +import IConnectionsProvider from "../fetcher/connections/IConnectionsProvider"; +import FootpathsProviderDefault from "../fetcher/footpaths/FootpathsProviderDefault"; +import IFootpathsFetcher from "../fetcher/footpaths/IFootpathsProvider"; +import HydraTemplateFetcherDefault from "../fetcher/hydra/HydraTemplateFetcherDefault"; +import IHydraTemplateFetcher from "../fetcher/hydra/IHydraTemplateFetcher"; +import LDFetch from "../fetcher/LDFetch"; +import IProfileFetcher from "../fetcher/profiles/IProfileFetcher"; +import IProfileProvider from "../fetcher/profiles/IProfileProvider"; +import ProfileFetcherDefault from "../fetcher/profiles/ProfileFetcherDefault"; +import ProfileProviderDefault from "../fetcher/profiles/ProfileProviderDefault"; +import IStopsFetcher from "../fetcher/stops/IStopsFetcher"; +import IStopsProvider from "../fetcher/stops/IStopsProvider"; +import StopsFetcherLDFetch from "../fetcher/stops/ld-fetch/StopsFetcherLDFetch"; +import StopsProviderDefault from "../fetcher/stops/StopsProviderDefault"; +import IRoutableTileFetcher from "../fetcher/tiles/IRoutableTileFetcher"; +import IRoutableTileProvider from "../fetcher/tiles/IRoutableTileProvider"; +import RoutableTileFetcherRaw from "../fetcher/tiles/RoutableTileFetcherRaw"; +import RoutableTileProviderDefault from "../fetcher/tiles/RoutableTileProviderDefault"; +import RoutableTileProviderIntermediate from "../fetcher/tiles/RoutableTileProviderIntermediate"; +import { LDLoader } from "../loader/ldloader"; +import DijkstraTree from "../pathfinding/dijkstra-tree/DijkstraTree"; +import { Dijkstra } from "../pathfinding/dijkstra/Dijkstra"; +import { IShortestPathAlgorithm, IShortestPathTreeAlgorithm } from "../pathfinding/pathfinder"; +import PathfinderProvider from "../pathfinding/PathfinderProvider"; +import CSAEarliestArrival from "../planner/public-transport/CSAEarliestArrival"; +import IJourneyExtractor from "../planner/public-transport/IJourneyExtractor"; +import IPublicTransportPlanner from "../planner/public-transport/IPublicTransportPlanner"; +import JourneyExtractorProfile from "../planner/public-transport/JourneyExtractorProfile"; +import IRoadPlanner from "../planner/road/IRoadPlanner"; +import RoadPlannerPathfinding from "../planner/road/RoadPlannerPathfinding"; +import IReachableStopsFinder from "../planner/stops/IReachableStopsFinder"; +import ReachableStopsFinderDelaunay from "../planner/stops/ReachableStopsFinderDelaunay"; +import ReachableStopsFinderFootpaths from "../planner/stops/ReachableStopsFinderFootpaths"; +import QueryRunnerExponential from "../query-runner/exponential/QueryRunnerExponential"; +import ILocationResolver from "../query-runner/ILocationResolver"; +import IQueryRunner from "../query-runner/IQueryRunner"; +import LocationResolverConvenience from "../query-runner/LocationResolverConvenience"; +import TYPES from "../types"; const container = new Container(); container.bind(TYPES.Context).to(Context).inSingletonScope(); container.bind(TYPES.QueryRunner).to(QueryRunnerExponential); container.bind(TYPES.LocationResolver).to(LocationResolverConvenience); +container.bind(TYPES.HydraTemplateFetcher).to(HydraTemplateFetcherDefault).inSingletonScope(); + container.bind(TYPES.PublicTransportPlanner) .to(CSAEarliestArrival); container.bind>(TYPES.PublicTransportPlannerFactory) @@ -107,12 +104,11 @@ container.bind>(TYPES.StopsFetcherFactory) }, ); -container.bind(TYPES.RoutableTileRegistry).to(RoutableTileRegistry).inSingletonScope(); container.bind(TYPES.RoutableTileFetcher).to(RoutableTileFetcherRaw).inSingletonScope(); container.bind(TYPES.RoutableTileProvider) .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Base); container.bind(TYPES.RoutableTileProvider) - .to(RoutableTileProviderTransit).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Transit); + .to(RoutableTileProviderIntermediate).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Transit); container.bind(TYPES.FootpathsProvider).to(FootpathsProviderDefault).inSingletonScope(); diff --git a/src/configs/dissect.ts b/src/configs/dissect.ts index 16dc8a5f..9e070067 100644 --- a/src/configs/dissect.ts +++ b/src/configs/dissect.ts @@ -9,11 +9,13 @@ import ReachableStopsSearchPhase from "../enums/ReachableStopsSearchPhase"; import RoutingPhase from "../enums/RoutingPhase"; import TravelMode from "../enums/TravelMode"; import ConnectionsFetcherRaw from "../fetcher/connections/ConnectionsFetcherRaw"; -import ConnectionsProviderMerge from "../fetcher/connections/ConnectionsProviderMerge"; +import ConnectionsProviderMerge from "../fetcher/connections/ConnectionsProviderDefault"; import IConnectionsFetcher from "../fetcher/connections/IConnectionsFetcher"; import IConnectionsProvider from "../fetcher/connections/IConnectionsProvider"; import FootpathsProviderRaw from "../fetcher/footpaths/FootpathsProviderRaw"; import IFootpathsFetcher from "../fetcher/footpaths/IFootpathsProvider"; +import HydraTemplateFetcherDefault from "../fetcher/hydra/HydraTemplateFetcherDefault"; +import IHydraTemplateFetcher from "../fetcher/hydra/IHydraTemplateFetcher"; import LDFetch from "../fetcher/LDFetch"; import IProfileFetcher from "../fetcher/profiles/IProfileFetcher"; import IProfileProvider from "../fetcher/profiles/IProfileProvider"; @@ -54,6 +56,8 @@ container.bind(TYPES.Context).to(Context).inSingletonScope(); container.bind(TYPES.QueryRunner).to(QueryRunnerDefault); container.bind(TYPES.LocationResolver).to(LocationResolverConvenience); +container.bind(TYPES.HydraTemplateFetcher).to(HydraTemplateFetcherDefault).inSingletonScope(); + // TODO, make this a fixed property of the planner itself container.bind(TYPES.JourneyExtractor) .to(JourneyExtractorProfile); @@ -107,7 +111,6 @@ container.bind>(TYPES.StopsFetcherFactory) }, ); -container.bind(TYPES.RoutableTileRegistry).to(RoutableTileRegistry).inSingletonScope(); container.bind(TYPES.RoutableTileFetcher).to(RoutableTileFetcherRaw).inSingletonScope(); container.bind(TYPES.RoutableTileProvider) .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Base); diff --git a/src/configs/flexible_transit.ts b/src/configs/flexible_transit.ts new file mode 100644 index 00000000..cfdb1bf0 --- /dev/null +++ b/src/configs/flexible_transit.ts @@ -0,0 +1,118 @@ +import { Container, interfaces } from "inversify"; +import Catalog from "../Catalog"; +import Context from "../Context"; +import ReachableStopsSearchPhase from "../enums/ReachableStopsSearchPhase"; +import RoutingPhase from "../enums/RoutingPhase"; +import TravelMode from "../enums/TravelMode"; +import ConnectionsFetcherRaw from "../fetcher/connections/ConnectionsFetcherRaw"; +import ConnectionsProviderDefault from "../fetcher/connections/ConnectionsProviderDefault"; +import IConnectionsFetcher from "../fetcher/connections/IConnectionsFetcher"; +import IConnectionsProvider from "../fetcher/connections/IConnectionsProvider"; +import HydraTemplateFetcherDefault from "../fetcher/hydra/HydraTemplateFetcherDefault"; +import IHydraTemplateFetcher from "../fetcher/hydra/IHydraTemplateFetcher"; +import LDFetch from "../fetcher/LDFetch"; +import IProfileFetcher from "../fetcher/profiles/IProfileFetcher"; +import IProfileProvider from "../fetcher/profiles/IProfileProvider"; +import ProfileFetcherDefault from "../fetcher/profiles/ProfileFetcherDefault"; +import ProfileProviderDefault from "../fetcher/profiles/ProfileProviderDefault"; +import IStopsFetcher from "../fetcher/stops/IStopsFetcher"; +import IStopsProvider from "../fetcher/stops/IStopsProvider"; +import StopsFetcherLDFetch from "../fetcher/stops/ld-fetch/StopsFetcherLDFetch"; +import StopsProviderDefault from "../fetcher/stops/StopsProviderDefault"; +import IRoutableTileFetcher from "../fetcher/tiles/IRoutableTileFetcher"; +import IRoutableTileProvider from "../fetcher/tiles/IRoutableTileProvider"; +import RoutableTileFetcherRaw from "../fetcher/tiles/RoutableTileFetcherRaw"; +import RoutableTileProviderDefault from "../fetcher/tiles/RoutableTileProviderDefault"; +import { LDLoader } from "../loader/ldloader"; +import { BidirDijkstra } from "../pathfinding/bidirdijkstra/BidirDijkstra"; +import DijkstraTree from "../pathfinding/dijkstra-tree/DijkstraTree"; +import { IShortestPathAlgorithm, IShortestPathTreeAlgorithm } from "../pathfinding/pathfinder"; +import PathfinderProvider from "../pathfinding/PathfinderProvider"; +import CSAProfile from "../planner/public-transport/CSAProfile"; +import IJourneyExtractor from "../planner/public-transport/IJourneyExtractor"; +import IPublicTransportPlanner from "../planner/public-transport/IPublicTransportPlanner"; +import JourneyExtractorProfile from "../planner/public-transport/JourneyExtractorProfile"; +import IRoadPlanner from "../planner/road/IRoadPlanner"; +import RoadPlannerPathfinding from "../planner/road/RoadPlannerPathfinding"; +import IReachableStopsFinder from "../planner/stops/IReachableStopsFinder"; +import ReachableStopsFinderBirdsEyeCached from "../planner/stops/ReachableStopsFinderBirdsEyeCached"; +import ReachableStopsFinderDelaunay from "../planner/stops/ReachableStopsFinderDelaunay"; +import QueryRunnerExponential from "../query-runner/exponential/QueryRunnerExponential"; +import ILocationResolver from "../query-runner/ILocationResolver"; +import IQueryRunner from "../query-runner/IQueryRunner"; +import LocationResolverConvenience from "../query-runner/LocationResolverConvenience"; +import TYPES from "../types"; + +const container = new Container(); +container.bind(TYPES.Context).to(Context).inSingletonScope(); +container.bind(TYPES.QueryRunner).to(QueryRunnerExponential); +container.bind(TYPES.LocationResolver).to(LocationResolverConvenience); + +container.bind(TYPES.HydraTemplateFetcher).to(HydraTemplateFetcherDefault).inSingletonScope(); + +// TODO, make this a fixed property of the planner itself +container.bind(TYPES.JourneyExtractor) + .to(JourneyExtractorProfile); + +container.bind(TYPES.PublicTransportPlanner) + .to(CSAProfile); +container.bind>(TYPES.PublicTransportPlannerFactory) + .toAutoFactory(TYPES.PublicTransportPlanner); + +container.bind(TYPES.RoadPlanner) + .to(RoadPlannerPathfinding); + +container.bind(TYPES.ShortestPathTreeAlgorithm).to(DijkstraTree).inSingletonScope(); +container.bind(TYPES.ShortestPathAlgorithm).to(BidirDijkstra).inSingletonScope(); +container.bind(TYPES.PathfinderProvider).to(PathfinderProvider).inSingletonScope(); +container.bind(TYPES.ProfileFetcher).to(ProfileFetcherDefault).inSingletonScope(); +container.bind(TYPES.ProfileProvider).to(ProfileProviderDefault).inSingletonScope(); + +container.bind(TYPES.ReachableStopsFinder) + .to(ReachableStopsFinderDelaunay).whenTargetTagged("phase", ReachableStopsSearchPhase.Initial); +container.bind(TYPES.ReachableStopsFinder) + .to(ReachableStopsFinderBirdsEyeCached).whenTargetTagged("phase", ReachableStopsSearchPhase.Transfer); +container.bind(TYPES.ReachableStopsFinder) + .to(ReachableStopsFinderDelaunay).whenTargetTagged("phase", ReachableStopsSearchPhase.Final); + +container.bind(TYPES.ConnectionsProvider).to(ConnectionsProviderDefault).inSingletonScope(); +container.bind(TYPES.ConnectionsFetcher).to(ConnectionsFetcherRaw); +container.bind>(TYPES.ConnectionsFetcherFactory) + .toFactory( + (context: interfaces.Context) => + (travelMode: TravelMode) => { + const fetcher = context.container.get(TYPES.ConnectionsFetcher); + + fetcher.setTravelMode(travelMode); + + return fetcher; + }, + ); + +container.bind(TYPES.StopsProvider).to(StopsProviderDefault).inSingletonScope(); +container.bind(TYPES.StopsFetcher).to(StopsFetcherLDFetch); +container.bind>(TYPES.StopsFetcherFactory) + .toFactory( + (context: interfaces.Context) => + (accessUrl: string) => { + const fetcher = context.container.get(TYPES.StopsFetcher); + fetcher.setAccessUrl(accessUrl); + return fetcher; + }, + ); + +container.bind(TYPES.RoutableTileFetcher).to(RoutableTileFetcherRaw).inSingletonScope(); +container.bind(TYPES.RoutableTileProvider) + .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Base); +container.bind(TYPES.RoutableTileProvider) + .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Transit); + +// Bind empty catalog +container.bind(TYPES.Catalog).toConstantValue(new Catalog()); + +// Init LDFetch +container.bind(TYPES.LDFetch).to(LDFetch).inSingletonScope(); + +container.bind(TYPES.LDLoader).to(LDLoader); + +export default container; diff --git a/src/configs/transit_car.ts b/src/configs/transit_car.ts index aba77d5e..8db59907 100644 --- a/src/configs/transit_car.ts +++ b/src/configs/transit_car.ts @@ -2,7 +2,6 @@ import { Container, interfaces } from "inversify"; import Catalog from "../Catalog"; import catalogNmbs from "../catalog.nmbs"; import Context from "../Context"; -import RoutableTileRegistry from "../entities/tiles/registry"; import ReachableStopsSearchPhase from "../enums/ReachableStopsSearchPhase"; import RoutingPhase from "../enums/RoutingPhase"; import TravelMode from "../enums/TravelMode"; @@ -12,6 +11,8 @@ import IConnectionsFetcher from "../fetcher/connections/IConnectionsFetcher"; import IConnectionsProvider from "../fetcher/connections/IConnectionsProvider"; import FootpathsProviderDefault from "../fetcher/footpaths/FootpathsProviderDefault"; import IFootpathsFetcher from "../fetcher/footpaths/IFootpathsProvider"; +import HydraTemplateFetcherDefault from "../fetcher/hydra/HydraTemplateFetcherDefault"; +import IHydraTemplateFetcher from "../fetcher/hydra/IHydraTemplateFetcher"; import LDFetch from "../fetcher/LDFetch"; import IProfileFetcher from "../fetcher/profiles/IProfileFetcher"; import IProfileProvider from "../fetcher/profiles/IProfileProvider"; @@ -52,6 +53,8 @@ container.bind(TYPES.Context).to(Context).inSingletonScope(); container.bind(TYPES.QueryRunner).to(QueryRunnerExponential); container.bind(TYPES.LocationResolver).to(LocationResolverConvenience); +container.bind(TYPES.HydraTemplateFetcher).to(HydraTemplateFetcherDefault).inSingletonScope(); + // TODO, make this a fixed property of the planner itself container.bind(TYPES.JourneyExtractor) .to(JourneyExtractorProfile); @@ -105,7 +108,6 @@ container.bind>(TYPES.StopsFetcherFactory) }, ); -container.bind(TYPES.RoutableTileRegistry).to(RoutableTileRegistry).inSingletonScope(); container.bind(TYPES.RoutableTileFetcher).to(RoutableTileFetcherRaw).inSingletonScope(); container.bind(TYPES.RoutableTileProvider) .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Base); diff --git a/src/configs/triangle_demo.ts b/src/configs/triangle_demo.ts index 4b8a5ee5..35f13206 100644 --- a/src/configs/triangle_demo.ts +++ b/src/configs/triangle_demo.ts @@ -3,16 +3,17 @@ import Catalog from "../Catalog"; import catalogDeLijn from "../catalog.delijn.vlaenderen"; import catalogNmbs from "../catalog.nmbs"; import Context from "../Context"; -import RoutableTileRegistry from "../entities/tiles/registry"; import ReachableStopsSearchPhase from "../enums/ReachableStopsSearchPhase"; import RoutingPhase from "../enums/RoutingPhase"; import TravelMode from "../enums/TravelMode"; import ConnectionsFetcherRaw from "../fetcher/connections/ConnectionsFetcherRaw"; -import ConnectionsProviderMerge from "../fetcher/connections/ConnectionsProviderMerge"; +import ConnectionsProviderMerge from "../fetcher/connections/ConnectionsProviderDefault"; import IConnectionsFetcher from "../fetcher/connections/IConnectionsFetcher"; import IConnectionsProvider from "../fetcher/connections/IConnectionsProvider"; import FootpathsProviderRaw from "../fetcher/footpaths/FootpathsProviderRaw"; import IFootpathsFetcher from "../fetcher/footpaths/IFootpathsProvider"; +import HydraTemplateFetcherDefault from "../fetcher/hydra/HydraTemplateFetcherDefault"; +import IHydraTemplateFetcher from "../fetcher/hydra/IHydraTemplateFetcher"; import LDFetch from "../fetcher/LDFetch"; import IProfileFetcher from "../fetcher/profiles/IProfileFetcher"; import IProfileProvider from "../fetcher/profiles/IProfileProvider"; @@ -53,6 +54,8 @@ container.bind(TYPES.Context).to(Context).inSingletonScope(); container.bind(TYPES.QueryRunner).to(QueryRunnerDefault); container.bind(TYPES.LocationResolver).to(LocationResolverConvenience); +container.bind(TYPES.HydraTemplateFetcher).to(HydraTemplateFetcherDefault).inSingletonScope(); + // TODO, make this a fixed property of the planner itself container.bind(TYPES.JourneyExtractor) .to(JourneyExtractorProfile); @@ -106,7 +109,6 @@ container.bind>(TYPES.StopsFetcherFactory) }, ); -container.bind(TYPES.RoutableTileRegistry).to(RoutableTileRegistry).inSingletonScope(); container.bind(TYPES.RoutableTileFetcher).to(RoutableTileFetcherRaw).inSingletonScope(); container.bind(TYPES.RoutableTileProvider) .to(RoutableTileProviderDefault).inSingletonScope().whenTargetTagged("phase", RoutingPhase.Base); diff --git a/src/demo.ts b/src/demo.ts index 092212f0..0fb07df6 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -1,4 +1,4 @@ -import { BasicTrainPlanner } from "."; +import { FlexibleTransitPlanner } from "."; import EventBus from "./events/EventBus"; import EventType from "./events/EventType"; import IPath from "./interfaces/IPath"; @@ -6,7 +6,9 @@ import Units from "./util/Units"; export default async (logResults) => { - const planner = new BasicTrainPlanner(); + const planner = new FlexibleTransitPlanner(); + planner.addConnectionSource("https://graph.irail.be/sncb/connections"); + planner.addStopSource("https://irail.be/stations/NMBS"); if (logResults) { let scannedPages = 0; @@ -57,52 +59,49 @@ export default async (logResults) => { } return new Promise((resolve, reject) => { - if (logResults) { - console.log(`${new Date()} Start query`); - } + if (logResults) { + console.log(`${new Date()} Start query`); + } - const amount = 1; - let i = 0; + const amount = 1; + let i = 0; - planner - .setProfileID("https://hdelva.be/profile/pedestrian") - .query({ - // roadNetworkOnly: true, - // from: "https://data.delijn.be/stops/201657", - // to: "https://data.delijn.be/stops/205910", - // from: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456 - // to: "https://data.delijn.be/stops/502481", // Tielt Metaalconstructie Goossens - // from: "https://data.delijn.be/stops/509927", // Tield Rameplein perron 1 - // to: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456 - // from: "Ingelmunster", // Ingelmunster - // to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters - from: { latitude: 50.93278, longitude: 5.32665 }, // Pita Aladin, Hasselt - to: { latitude: 50.7980187, longitude: 3.1877779 }, // Burger Pita Pasta, Menen - // from: "Hasselt", - // to: "Kortrijk", - minimumDepartureTime: new Date(), - maximumTransferDuration: Units.fromMinutes(30), - }) - .take(amount) - .on("error", (error) => { - resolve(false); - }) - .on("data", (path: IPath) => { - ++i; + planner + .setProfileID("https://hdelva.be/profile/pedestrian") + .query({ + // roadNetworkOnly: true, + // from: "https://data.delijn.be/stops/201657", + // to: "https://data.delijn.be/stops/205910", + // from: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456 + // to: "https://data.delijn.be/stops/502481", // Tielt Metaalconstructie Goossens + // from: "https://data.delijn.be/stops/509927", // Tield Rameplein perron 1 + // to: "https://data.delijn.be/stops/200455", // Deinze weg op Grammene +456 + // from: "Ingelmunster", // Ingelmunster + // to: "http://irail.be/stations/NMBS/008892007", // Ghent-Sint-Pieters + from: { latitude: 50.93278, longitude: 5.32665 }, // Pita Aladin, Hasselt + to: { latitude: 50.7980187, longitude: 3.1877779 }, // Burger Pita Pasta, Menen + // from: "Hasselt", + // to: "Kortrijk", + minimumDepartureTime: new Date(), + maximumTransferDuration: Units.fromMinutes(30), + }) + .take(amount) + .on("data", (path: IPath) => { + ++i; - if (logResults) { - console.log(new Date()); - console.log(i); - console.log(JSON.stringify(path, null, " ")); - console.log("\n"); - } + if (logResults) { + console.log(new Date()); + console.log(i); + console.log(JSON.stringify(path, null, " ")); + console.log("\n"); + } - if (i === amount) { - resolve(true); - } - }) - .on("end", () => { - resolve(false); - }); - }); + if (i === amount) { + resolve(true); + } + }) + .on("end", () => { + resolve(false); + }); + }); }; diff --git a/src/entities/connections/connections.ts b/src/entities/connections/connections.ts index 4e04a460..de61017b 100644 --- a/src/entities/connections/connections.ts +++ b/src/entities/connections/connections.ts @@ -26,7 +26,6 @@ export default interface IConnection { departureStop: string; departureDelay?: DurationMs; - nextConnection?: string[]; tripId?: string; route?: string; diff --git a/src/entities/hydra/mapping.ts b/src/entities/hydra/mapping.ts index 8f7b7e2b..c5e1d7f6 100644 --- a/src/entities/hydra/mapping.ts +++ b/src/entities/hydra/mapping.ts @@ -4,6 +4,9 @@ export class HydraTemplateMapping { } public id?: string; + public variable: string; + public required: boolean; + public property: string; constructor(id?: string) { this.id = id; diff --git a/src/entities/hydra/search.ts b/src/entities/hydra/search.ts index cbcf0e54..5f93c2a9 100644 --- a/src/entities/hydra/search.ts +++ b/src/entities/hydra/search.ts @@ -1,11 +1,30 @@ +import uriTemplates = require("uri-templates"); + +import { HydraTemplateMapping } from "./mapping"; + export class HydraTemplate { public static create(id?: string) { return new HydraTemplate(id); } public id?: string; + public template: string; + public mappings: HydraTemplateMapping[]; constructor(id?: string) { this.id = id; } + + public fill(values: object): string { + const template = uriTemplates(this.template); + const params = {}; + + for (const mapping of this.mappings) { + if (values[mapping.property]) { + params[mapping.variable] = values[mapping.property]; + } + } + + return template.fill(params); + } } diff --git a/src/entities/tiles/registry.ts b/src/entities/tiles/registry.ts index 66183169..0f4cf9fb 100644 --- a/src/entities/tiles/registry.ts +++ b/src/entities/tiles/registry.ts @@ -4,6 +4,15 @@ import { IRoutableTileWayIndex, RoutableTileWay } from "./way"; @injectable() export default class RoutableTileRegistry { + public static getInstance(): RoutableTileRegistry { + if (!RoutableTileRegistry.instance) { + RoutableTileRegistry.instance = new RoutableTileRegistry(); + } + + return RoutableTileRegistry.instance; + } + + private static instance: RoutableTileRegistry; private nodes: IRoutableTileNodeIndex; private ways: IRoutableTileWayIndex; diff --git a/src/enums/DropOffType.ts b/src/enums/DropOffType.ts index 7187019c..28817e7d 100644 --- a/src/enums/DropOffType.ts +++ b/src/enums/DropOffType.ts @@ -1,8 +1,8 @@ enum DropOffType { - Regular = "gtfs:Regular", - NotAvailable = "gtfs:NotAvailable", - MustPhone = "gtfs:MustPhone", - MustCoordinateWithDriver = "gtfs:MustCoordinateWithDriver", + Regular = "http://vocab.gtfs.org/terms#Regular", + NotAvailable = "http://vocab.gtfs.org/terms#NotAvailable", + MustPhone = "http://vocab.gtfs.org/terms#MustPhone", + MustCoordinateWithDriver = "http://vocab.gtfs.org/terms#MustCoordinateWithDriver", } export default DropOffType; diff --git a/src/enums/PickupType.ts b/src/enums/PickupType.ts index 537a8b15..cabb81f9 100644 --- a/src/enums/PickupType.ts +++ b/src/enums/PickupType.ts @@ -1,8 +1,8 @@ enum PickupType { - Regular = "gtfs:Regular", - NotAvailable = "gtfs:NotAvailable", - MustPhone = "gtfs:MustPhone", - MustCoordinateWithDriver = "gtfs:MustCoordinateWithDriver", + Regular = "http://vocab.gtfs.org/terms#Regular", + NotAvailable = "http://vocab.gtfs.org/terms#NotAvailable", + MustPhone = "http://vocab.gtfs.org/terms#MustPhone", + MustCoordinateWithDriver = "http://vocab.gtfs.org/terms#MustCoordinateWithDriver", } export default PickupType; diff --git a/src/fetcher/connections/ConnectionsFetcherRaw.ts b/src/fetcher/connections/ConnectionsFetcherRaw.ts index b627bb17..bfe3671a 100644 --- a/src/fetcher/connections/ConnectionsFetcherRaw.ts +++ b/src/fetcher/connections/ConnectionsFetcherRaw.ts @@ -2,11 +2,27 @@ import fetch from "cross-fetch"; import { injectable } from "inversify"; import IConnection from "../../entities/connections/connections"; import { LinkedConnectionsPage } from "../../entities/connections/page"; +import DropOffType from "../../enums/DropOffType"; +import PickupType from "../../enums/PickupType"; import TravelMode from "../../enums/TravelMode"; import EventBus from "../../events/EventBus"; import EventType from "../../events/EventType"; +import JSONLDContext from "../../uri/JSONLDContext"; import IConnectionsFetcher from "./IConnectionsFetcher"; +const ARRIVAL_STOP = "http://semweb.mmlab.be/ns/linkedconnections#arrivalStop"; +const ARRIVAL_TIME = "http://semweb.mmlab.be/ns/linkedconnections#arrivalTime"; +const ARRIVAL_DELAY = "http://semweb.mmlab.be/ns/linkedconnections#arrivalDelay"; +const DEPARTURE_STOP = "http://semweb.mmlab.be/ns/linkedconnections#departureStop"; +const DEPARTURE_TIME = "http://semweb.mmlab.be/ns/linkedconnections#departureTime"; +const DEPARTURE_DELAY = "http://semweb.mmlab.be/ns/linkedconnections#departureDelay"; +const NEXT_CONNECTION = "http://semweb.mmlab.be/ns/linkedconnections#nextConnection"; +const DROP_OFF_TYPE = "http://vocab.gtfs.org/terms#dropOffType"; +const HEADSIGN = "http://vocab.gtfs.org/terms#headsign"; +const PICKUP_TYPE = "http://vocab.gtfs.org/terms#pickupType"; +const ROUTE = "http://vocab.gtfs.org/terms#route"; +const TRIP = "http://vocab.gtfs.org/terms#trip"; + @injectable() // tslint:disable: no-string-literal export default class ConnectionsFetcherRaw implements IConnectionsFetcher { @@ -28,25 +44,29 @@ export default class ConnectionsFetcherRaw implements IConnectionsFetcher { const blob = JSON.parse(responseText); const connections: IConnection[] = []; + const context = new JSONLDContext(blob["@context"]); for (const entity of blob["@graph"]) { - const connectionId = entity["@id"]; + for (const [name, data] of Object.entries(entity)) { + entity[context.resolveIdentifier(name)] = data; + } + + const connectionId = context.resolveIdentifier(entity["@id"]); - const arrivalTime = new Date(entity["arrivalTime"]); - const arrivalStop = entity["arrivalStop"]; - const arrivalDelay = entity["arrivalDelay"] ? parseFloat(entity["arrivalDelay"]) : 0; + const arrivalTime = new Date(entity[ARRIVAL_TIME]); + const arrivalStop = context.resolveIdentifier(entity[ARRIVAL_STOP]); + const arrivalDelay = entity[ARRIVAL_DELAY] ? parseFloat(entity[ARRIVAL_DELAY]) : 0; - const departureTime = new Date(entity["departureTime"]); - const departureStop = entity["departureStop"]; - const departureDelay = entity["departureDelay"] ? parseFloat(entity["departureDelay"]) : 0; + const departureTime = new Date(entity[DEPARTURE_TIME]); + const departureStop = context.resolveIdentifier(entity[DEPARTURE_STOP]); + const departureDelay = entity[DEPARTURE_DELAY] ? parseFloat(entity[DEPARTURE_DELAY]) : 0; - const nextConnection = entity["nextConnection"] || []; - const tripId = entity["gtfs:trip"] || null; + const tripId = context.resolveIdentifier(entity[TRIP]) || null; + const route = context.resolveIdentifier(entity[ROUTE]) || null; - const route = entity["gtfs:route"] || null; - const dropOffType = entity["gtfs:dropOffType"]; - const pickupType = entity["gtfs:pickupType"]; - const headsign = entity["direction"] || null; + const dropOffType = context.resolveIdentifier(entity[DROP_OFF_TYPE]) as DropOffType; + const pickupType = context.resolveIdentifier(entity[PICKUP_TYPE]) as PickupType; + const headsign = entity[HEADSIGN] || null; const connection: IConnection = { id: connectionId, travelMode: this.travelMode, @@ -56,7 +76,6 @@ export default class ConnectionsFetcherRaw implements IConnectionsFetcher { departureTime, departureStop, departureDelay, - nextConnection, tripId, route, dropOffType, diff --git a/src/fetcher/connections/ConnectionsProviderDefault.ts b/src/fetcher/connections/ConnectionsProviderDefault.ts index 7a13f027..7b5cf012 100644 --- a/src/fetcher/connections/ConnectionsProviderDefault.ts +++ b/src/fetcher/connections/ConnectionsProviderDefault.ts @@ -1,93 +1,110 @@ import { AsyncIterator } from "asynciterator"; import { inject, injectable } from "inversify"; -import { EventType } from "../.."; -import Catalog from "../../Catalog"; +import Catalog, { IConnectionsSourceConfig } from "../../Catalog"; import IConnection from "../../entities/connections/connections"; -import { ILinkedConnectionsPageIndex, LinkedConnectionsPage } from "../../entities/connections/page"; -import EventBus from "../../events/EventBus"; +import { LinkedConnectionsPage } from "../../entities/connections/page"; import TYPES, { ConnectionsFetcherFactory } from "../../types"; -import BackwardConnectionIterator from "./BackwardConnectionIterator"; -import ForwardConnectionIterator from "./ForwardConnectionIterator"; -import IConnectionsFetcher from "./IConnectionsFetcher"; +import MergeIterator from "../../util/iterators/MergeIterator"; +import IHydraTemplateFetcher from "../hydra/IHydraTemplateFetcher"; +import ConnectionsProviderSingle from "./ConnectionsProviderSingle"; import IConnectionsIteratorOptions from "./IConnectionsIteratorOptions"; import IConnectionsProvider from "./IConnectionsProvider"; @injectable() export default class ConnectionsProviderDefault implements IConnectionsProvider { - protected fetcher: IConnectionsFetcher; - protected pages: ILinkedConnectionsPageIndex = {}; - protected accessUrl: string; - - constructor( - @inject(TYPES.ConnectionsFetcherFactory) connectionsFetcherFactory: ConnectionsFetcherFactory, - @inject(TYPES.Catalog) catalog: Catalog, - ) { - if (catalog.connectionsSourceConfigs.length > 1) { - throw (new Error("Use the ConnectionsProviderMerge if you have multiple connections sources")); - } - - const { accessUrl, travelMode } = catalog.connectionsSourceConfigs[0]; - this.accessUrl = accessUrl; - this.fetcher = connectionsFetcherFactory(travelMode); + private static forwardsConnectionSelector(connections: IConnection[]): number { + if (connections.length === 1) { + return 0; } - public async getByUrl(url: string): Promise { - if (!this.pages[url]) { - this.pages[url] = this.fetcher.get(url); - } + let earliestIndex = 0; + const earliest = connections[earliestIndex]; - return await this.pages[url]; + for (let i = 1; i < connections.length; i++) { + const connection = connections[i]; + + if (connection.departureTime < earliest.departureTime) { + earliestIndex = i; + } } - public async getByTime(date: Date): Promise { - // TODO, look up in the index -- use lower/upper bounds of each page - const url = this.getIdForTime(date); - return this.getByUrl(url); + return earliestIndex; + } + + private static backwardsConnectionsSelector(connections: IConnection[]): number { + if (connections.length === 1) { + return 0; } - public getIdForTime(date: Date): string { - return `${this.accessUrl}?departureTime=${date.toISOString()}`; + let latestIndex = 0; + const latest = connections[latestIndex]; + + for (let i = 1; i < connections.length; i++) { + const connection = connections[i]; + + if (connection.departureTime > latest.departureTime) { + latestIndex = i; + } + } + + return latestIndex; + } + + private singleProviders: ConnectionsProviderSingle[]; + private connectionsFetcherFactory: ConnectionsFetcherFactory; + private templateFetcher: IHydraTemplateFetcher; + + constructor( + @inject(TYPES.ConnectionsFetcherFactory) connectionsFetcherFactory: ConnectionsFetcherFactory, + @inject(TYPES.Catalog) catalog: Catalog, + @inject(TYPES.HydraTemplateFetcher) templateFetcher: IHydraTemplateFetcher, + ) { + this.singleProviders = []; + this.connectionsFetcherFactory = connectionsFetcherFactory; + this.templateFetcher = templateFetcher; + + for (const connectionSource of catalog.connectionsSourceConfigs) { + this.addConnectionSource(connectionSource); } + } - public prefetchConnections(lowerBound: Date, upperBound: Date): void { - const iterator = this.createIterator({ - upperBoundDate: upperBound, - lowerBoundDate: lowerBound, - }); - - iterator.on("readable", () => { - while (iterator.read()) { - // - } - }); + public addConnectionSource(source: IConnectionsSourceConfig) { + this.singleProviders.push( + new ConnectionsProviderSingle(this.connectionsFetcherFactory, source, this.templateFetcher), + ); + } + + public prefetchConnections(lowerBound: Date, upperBound: Date): void { + for (const provider of this.singleProviders) { + provider.prefetchConnections(lowerBound, upperBound); } + } + + public async createIterator(options: IConnectionsIteratorOptions): Promise> { + const iterators = await Promise.all(this.singleProviders + .map((provider) => provider.createIterator(options))); - public createIterator(options: IConnectionsIteratorOptions): AsyncIterator { - EventBus.getInstance().emit( - EventType.ConnectionIteratorView, - options.lowerBoundDate, - options.upperBoundDate, - ); - - let iterator: AsyncIterator; - if (options.backward) { - const beginTime = options.upperBoundDate; - const beginUrl = this.getIdForTime(beginTime); - iterator = new BackwardConnectionIterator(this, options, beginUrl); - } else { - const beginTime = options.lowerBoundDate; - const beginUrl = this.getIdForTime(beginTime); - iterator = new ForwardConnectionIterator(this, options, beginUrl); - } - - return iterator.on("end", () => { - EventBus.getInstance().emit( - EventType.ConnectionIteratorView, - options.lowerBoundDate, - options.upperBoundDate, - true, - ); - }); + const selector = options.backward ? + ConnectionsProviderDefault.backwardsConnectionsSelector + : + ConnectionsProviderDefault.forwardsConnectionSelector; + + if (options.excludedModes) { + return new MergeIterator(iterators, selector, true).filter((item) => { + return !options.excludedModes.has(item.travelMode); + }); + } else { + return new MergeIterator(iterators, selector, true); } + } + + public getByUrl(url: string): Promise { + // TODO, if needed this can delegate the call to one of the sub providers + throw new Error("Not implemented yet"); + } + + public getByTime(date: Date): Promise { + throw new Error("Method not implemented because the semantics would be ambiguous."); + } } diff --git a/src/fetcher/connections/ConnectionsProviderMerge.ts b/src/fetcher/connections/ConnectionsProviderMerge.ts deleted file mode 100644 index 60005b36..00000000 --- a/src/fetcher/connections/ConnectionsProviderMerge.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { AsyncIterator } from "asynciterator"; -import { inject, injectable } from "inversify"; -import Catalog from "../../Catalog"; -import IConnection from "../../entities/connections/connections"; -import { LinkedConnectionsPage } from "../../entities/connections/page"; -import TYPES, { ConnectionsFetcherFactory } from "../../types"; -import MergeIterator from "../../util/iterators/MergeIterator"; -import ConnectionsProviderDefault from "./ConnectionsProviderDefault"; -import IConnectionsIteratorOptions from "./IConnectionsIteratorOptions"; -import IConnectionsProvider from "./IConnectionsProvider"; - -@injectable() -export default class ConnectionsProviderMerge implements IConnectionsProvider { - - private static forwardsConnectionSelector(connections: IConnection[]): number { - if (connections.length === 1) { - return 0; - } - - let earliestIndex = 0; - const earliest = connections[earliestIndex]; - - for (let i = 1; i < connections.length; i++) { - const connection = connections[i]; - - if (connection.departureTime < earliest.departureTime) { - earliestIndex = i; - } - } - - return earliestIndex; - } - - private static backwardsConnectionsSelector(connections: IConnection[]): number { - if (connections.length === 1) { - return 0; - } - - let latestIndex = 0; - const latest = connections[latestIndex]; - - for (let i = 1; i < connections.length; i++) { - const connection = connections[i]; - - if (connection.departureTime > latest.departureTime) { - latestIndex = i; - } - } - - return latestIndex; - } - - private defaultProviders: IConnectionsProvider[]; - - constructor( - @inject(TYPES.ConnectionsFetcherFactory) connectionsFetcherFactory: ConnectionsFetcherFactory, - @inject(TYPES.Catalog) catalog: Catalog, - ) { - this.defaultProviders = []; - - for (const { accessUrl, travelMode } of catalog.connectionsSourceConfigs) { - const subCatalog = new Catalog(); - subCatalog.addConnectionsSource(accessUrl, travelMode); - this.defaultProviders.push(new ConnectionsProviderDefault(connectionsFetcherFactory, subCatalog)); - } - } - - public prefetchConnections(lowerBound: Date, upperBound: Date): void { - for (const provider of this.defaultProviders) { - provider.prefetchConnections(lowerBound, upperBound); - } - } - - public createIterator(options: IConnectionsIteratorOptions): AsyncIterator { - const iterators = this.defaultProviders - .map((provider) => provider.createIterator(options)); - - const selector = options.backward ? - ConnectionsProviderMerge.backwardsConnectionsSelector - : - ConnectionsProviderMerge.forwardsConnectionSelector; - - if (options.excludedModes) { - return new MergeIterator(iterators, selector, true).filter((item) => { - return !options.excludedModes.has(item.travelMode); - }); - } else { - return new MergeIterator(iterators, selector, true); - } - } - - public getByUrl(url: string): Promise { - // TODO, if needed this can delegate the call to one of the sub providers - throw new Error("Not implemented yet"); - } - - public getByTime(date: Date): Promise { - throw new Error("Method not implemented because the semantics would be ambiguous."); - } -} diff --git a/src/fetcher/connections/ConnectionsProviderSingle.ts b/src/fetcher/connections/ConnectionsProviderSingle.ts new file mode 100644 index 00000000..5c0e89f9 --- /dev/null +++ b/src/fetcher/connections/ConnectionsProviderSingle.ts @@ -0,0 +1,107 @@ +import { AsyncIterator } from "asynciterator"; + +import { EventType } from "../.."; +import { IConnectionsSourceConfig } from "../../Catalog"; +import IConnection from "../../entities/connections/connections"; +import { ILinkedConnectionsPageIndex, LinkedConnectionsPage } from "../../entities/connections/page"; +import { HydraTemplate } from "../../entities/hydra/search"; +import EventBus from "../../events/EventBus"; +import { ConnectionsFetcherFactory } from "../../types"; +import IHydraTemplateFetcher from "../hydra/IHydraTemplateFetcher"; +import BackwardConnectionIterator from "./BackwardConnectionIterator"; +import ForwardConnectionIterator from "./ForwardConnectionIterator"; +import IConnectionsFetcher from "./IConnectionsFetcher"; +import IConnectionsIteratorOptions from "./IConnectionsIteratorOptions"; +import IConnectionsProvider from "./IConnectionsProvider"; + +export default class ConnectionsProviderSingle implements IConnectionsProvider { + protected fetcher: IConnectionsFetcher; + protected templateFetcher: IHydraTemplateFetcher; + protected pages: ILinkedConnectionsPageIndex = {}; + protected accessUrl: string; + protected template: Promise; + + constructor( + connectionsFetcherFactory: ConnectionsFetcherFactory, + catalog: IConnectionsSourceConfig, + templateFetcher: IHydraTemplateFetcher, + ) { + const { accessUrl, travelMode } = catalog; + this.accessUrl = accessUrl; + this.fetcher = connectionsFetcherFactory(travelMode); + this.templateFetcher = templateFetcher; + } + + public addConnectionSource(source: IConnectionsSourceConfig) { + throw new Error("Method not implemented."); + } + + public async getByUrl(url: string): Promise { + if (!this.pages[url]) { + this.pages[url] = this.fetcher.get(url); + } + + return await this.pages[url]; + } + + public async getByTime(date: Date): Promise { + // TODO, look up in the index -- use lower/upper bounds of each page + const url = await this.getIdForTime(date); + return this.getByUrl(url); + } + + public async getIdForTime(date: Date): Promise { + const template = await this.getTemplate(); + return template.fill({ + "http://semweb.mmlab.be/ns/linkedconnections#departureTimeQuery": date.toISOString(), + }); + } + + public prefetchConnections(lowerBound: Date, upperBound: Date): void { + this.createIterator({ + upperBoundDate: upperBound, + lowerBoundDate: lowerBound, + }).then((iterator) => { + iterator.on("readable", () => { + while (iterator.read()) { + // + } + }); + }); + } + + public async createIterator(options: IConnectionsIteratorOptions): Promise> { + EventBus.getInstance().emit( + EventType.ConnectionIteratorView, + options.lowerBoundDate, + options.upperBoundDate, + ); + + let iterator: AsyncIterator; + if (options.backward) { + const beginTime = options.upperBoundDate; + const beginUrl = await this.getIdForTime(beginTime); + iterator = new BackwardConnectionIterator(this, options, beginUrl); + } else { + const beginTime = options.lowerBoundDate; + const beginUrl = await this.getIdForTime(beginTime); + iterator = new ForwardConnectionIterator(this, options, beginUrl); + } + + return iterator.on("end", () => { + EventBus.getInstance().emit( + EventType.ConnectionIteratorView, + options.lowerBoundDate, + options.upperBoundDate, + true, + ); + }); + } + + protected async getTemplate(): Promise { + if (!this.template) { + this.template = this.templateFetcher.get(this.accessUrl); + } + return this.template; + } +} diff --git a/src/fetcher/connections/IConnectionsProvider.ts b/src/fetcher/connections/IConnectionsProvider.ts index a889a183..6f1a282e 100644 --- a/src/fetcher/connections/IConnectionsProvider.ts +++ b/src/fetcher/connections/IConnectionsProvider.ts @@ -1,12 +1,14 @@ import { AsyncIterator } from "asynciterator"; +import { IConnectionsSourceConfig } from "../../Catalog"; import IConnection from "../../entities/connections/connections"; import { LinkedConnectionsPage } from "../../entities/connections/page"; -import TravelMode from "../../enums/TravelMode"; import IConnectionsIteratorOptions from "./IConnectionsIteratorOptions"; export default interface IConnectionsProvider { + addConnectionSource(source: IConnectionsSourceConfig); + prefetchConnections(lowerBound: Date, upperBound: Date): void; - createIterator(options: IConnectionsIteratorOptions): AsyncIterator; + createIterator(options: IConnectionsIteratorOptions): Promise>; getByUrl(url: string): Promise; getByTime(date: Date): Promise; diff --git a/src/fetcher/connections/tests/ConnectionsProviderNMBSTest.ts b/src/fetcher/connections/tests/ConnectionsProviderNMBSTest.ts index ac3a756c..75ac9ad6 100644 --- a/src/fetcher/connections/tests/ConnectionsProviderNMBSTest.ts +++ b/src/fetcher/connections/tests/ConnectionsProviderNMBSTest.ts @@ -1,5 +1,6 @@ import { ArrayIterator, AsyncIterator } from "asynciterator"; import { injectable } from "inversify"; +import { IConnectionsSourceConfig } from "../../../Catalog"; import IConnection from "../../../entities/connections/connections"; import { LinkedConnectionsPage } from "../../../entities/connections/page"; import IConnectionsIteratorOptions from "../IConnectionsIteratorOptions"; @@ -14,6 +15,10 @@ export default class ConnectionsProviderNMBSTest implements IConnectionsProvider this.connections = connections; } + public addConnectionSource(source: IConnectionsSourceConfig) { + throw new Error("Method not implemented."); + } + public getByUrl(url: string): Promise { throw new Error("Method not implemented."); } @@ -25,7 +30,7 @@ export default class ConnectionsProviderNMBSTest implements IConnectionsProvider return; } - public createIterator(options: IConnectionsIteratorOptions): AsyncIterator { + public async createIterator(options: IConnectionsIteratorOptions): Promise> { let array = this.connections .map((r) => r.value); diff --git a/src/fetcher/hydra/HydraTemplateFetcherDefault.ts b/src/fetcher/hydra/HydraTemplateFetcherDefault.ts new file mode 100644 index 00000000..d2167e36 --- /dev/null +++ b/src/fetcher/hydra/HydraTemplateFetcherDefault.ts @@ -0,0 +1,61 @@ +import { inject, injectable } from "inversify"; +import LDFetch from "ldfetch"; +import { HydraTemplateMapping } from "../../entities/hydra/mapping"; +import { HydraTemplate } from "../../entities/hydra/search"; +import { LDLoader } from "../../loader/ldloader"; +import { ThingView } from "../../loader/views/single"; +import TYPES from "../../types"; +import { HYDRA } from "../../uri/constants"; +import URI from "../../uri/uri"; +import IHydraTemplateFetcher from "./IHydraTemplateFetcher"; + +@injectable() +export default class HydraTemplateFetcherDefault implements IHydraTemplateFetcher { + + protected ldFetch: LDFetch; + protected ldLoader: LDLoader; + + constructor( + @inject(TYPES.LDFetch) ldFetch: LDFetch, + ) { + this.ldFetch = ldFetch; + this.ldLoader = new LDLoader(); + + // unordered collections + this.ldLoader.defineCollection(URI.inNS(HYDRA, "mapping")); + } + + public async get(url: string): Promise { + const rdfThing = await this.ldFetch.get(url); + const triples = rdfThing.triples; + + const [profile] = this.ldLoader.process(triples, [ + this.getView(), + ]); + + profile.id = url; + + return profile; + } + + protected getView() { + const view = new ThingView(HydraTemplate.create); + view.addFilter((entity) => + entity[URI.inNS(HYDRA, "template")] !== undefined, + ); + view.addMapping(URI.inNS(HYDRA, "template"), "template"); + view.addMapping(URI.inNS(HYDRA, "mapping"), "mappings", this.getMappingView()); + return view; + } + + protected getMappingView() { + const view = new ThingView(HydraTemplateMapping.create); + view.addFilter((entity) => + entity[URI.inNS(HYDRA, "variable")] !== undefined, + ); + view.addMapping(URI.inNS(HYDRA, "variable"), "variable"); + view.addMapping(URI.inNS(HYDRA, "required"), "required"); + view.addMapping(URI.inNS(HYDRA, "property"), "property"); + return view; + } +} diff --git a/src/fetcher/hydra/IHydraTemplateFetcher.ts b/src/fetcher/hydra/IHydraTemplateFetcher.ts new file mode 100644 index 00000000..8de1cee5 --- /dev/null +++ b/src/fetcher/hydra/IHydraTemplateFetcher.ts @@ -0,0 +1,5 @@ +import { HydraTemplate } from "../../entities/hydra/search"; + +export default interface IHydraTemplateFetcher { + get(url: string): Promise; +} diff --git a/src/fetcher/stops/IStopsProvider.ts b/src/fetcher/stops/IStopsProvider.ts index 6f467568..6a0d908b 100644 --- a/src/fetcher/stops/IStopsProvider.ts +++ b/src/fetcher/stops/IStopsProvider.ts @@ -7,7 +7,8 @@ import IStop from "./IStop"; * @method getAllStops Returns concatenated array of [[IStop]]s from all [[IStopsFetcher]]s it mediates */ export default interface IStopsProvider { - prefetchStops: () => void; - getStopById: (stopId: string) => Promise; - getAllStops: () => Promise; + prefetchStops(); + addStopSource(accessUrl: string); + getStopById(stopId: string): Promise; + getAllStops(): Promise; } diff --git a/src/fetcher/stops/StopsFetcherRaw.ts b/src/fetcher/stops/StopsFetcherRaw.ts index 85697359..8822279c 100644 --- a/src/fetcher/stops/StopsFetcherRaw.ts +++ b/src/fetcher/stops/StopsFetcherRaw.ts @@ -30,6 +30,10 @@ export default class StopsFetcherRaw implements IStopsFetcher { this.ldFetch = ldFetch; } + public addStopSource(accessUrl: string) { + throw new Error("Method not implemented."); + } + public setAccessUrl(accessUrl: string) { this.accessUrl = accessUrl; } diff --git a/src/fetcher/stops/StopsProviderDefault.ts b/src/fetcher/stops/StopsProviderDefault.ts index a7fa1cea..0b0e83c8 100644 --- a/src/fetcher/stops/StopsProviderDefault.ts +++ b/src/fetcher/stops/StopsProviderDefault.ts @@ -10,6 +10,8 @@ export default class StopsProviderDefault implements IStopsProvider { private readonly stopsFetchers: IStopsFetcher[]; private cachedStops: IStop[]; + private allStops: Promise; + private stopsFetcherFactory: StopsFetcherFactory; constructor( @inject(TYPES.StopsFetcherFactory) stopsFetcherFactory: StopsFetcherFactory, @@ -17,12 +19,19 @@ export default class StopsProviderDefault implements IStopsProvider { ) { this.stopsFetchers = []; this.cachedStops = []; + this.stopsFetcherFactory = stopsFetcherFactory; for (const { accessUrl } of catalog.stopsSourceConfigs) { - this.stopsFetchers.push(stopsFetcherFactory(accessUrl)); + this.addStopSource(accessUrl); } } + public addStopSource(accessUrl: string) { + this.cachedStops = []; + this.allStops = null; + this.stopsFetchers.push(this.stopsFetcherFactory(accessUrl)); + } + public prefetchStops(): void { for (const stopsFetcher of this.stopsFetchers) { stopsFetcher.prefetchStops(); @@ -36,16 +45,20 @@ export default class StopsProviderDefault implements IStopsProvider { } public async getAllStops(): Promise { - if (this.cachedStops.length > 0) { - return Promise.resolve(this.cachedStops); - } + if (!this.allStops) { + if (this.cachedStops.length > 0) { + return Promise.resolve(this.cachedStops); + } - return Promise.all(this.stopsFetchers - .map((stopsFetcher: IStopsFetcher) => stopsFetcher.getAllStops()), - ).then((results: IStop[][]) => { - this.cachedStops = [].concat(...results); + this.allStops = Promise.all(this.stopsFetchers + .map((stopsFetcher: IStopsFetcher) => stopsFetcher.getAllStops()), + ).then((results: IStop[][]) => { + this.cachedStops = [].concat(...results); + + return this.cachedStops; + }); + } - return this.cachedStops; - }); + return this.allStops; } } diff --git a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts index 6311d3e8..c12d8a51 100644 --- a/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts +++ b/src/fetcher/stops/ld-fetch/StopsFetcherLDFetch.ts @@ -16,7 +16,6 @@ interface IStopMap { @injectable() export default class StopsFetcherLDFetch implements IStopsFetcher { - private accessUrl: string; private ldFetch: LDFetch; @@ -31,6 +30,10 @@ export default class StopsFetcherLDFetch implements IStopsFetcher { this.loadStops(); } + public addStopSource(accessUrl: string) { + throw new Error("Method not implemented."); + } + public setAccessUrl(accessUrl: string) { this.accessUrl = accessUrl; } diff --git a/src/fetcher/tiles/RoutableTileFetcherDefault.test.ts b/src/fetcher/tiles/RoutableTileFetcherDefault.test.ts index 05bed906..b1136c12 100644 --- a/src/fetcher/tiles/RoutableTileFetcherDefault.test.ts +++ b/src/fetcher/tiles/RoutableTileFetcherDefault.test.ts @@ -8,13 +8,13 @@ import ProfileProviderDefault from "../profiles/ProfileProviderDefault"; import RoutableTileFetcherDefault from "./RoutableTileFetcherDefault"; const ldfetch = new LDFetch({ headers: { Accept: "application/ld+json" } }); -const registry = new RoutableTileRegistry(); const profileProvider = new ProfileProviderDefault(new ProfileFetcherDefault(ldfetch)); -const locationResolver = new LocationResolverConvenience(null, registry); +const locationResolver = new LocationResolverConvenience(null); const fetcher = new RoutableTileFetcherDefault( ldfetch, - new PathfinderProvider(undefined, undefined, registry, profileProvider, locationResolver), - registry); + new PathfinderProvider(undefined, undefined, profileProvider, locationResolver)); + +const registry = RoutableTileRegistry.getInstance(); test("[RoutableTileFetcherDefault] data completeness", async () => { jest.setTimeout(15000); diff --git a/src/fetcher/tiles/RoutableTileFetcherDefault.ts b/src/fetcher/tiles/RoutableTileFetcherDefault.ts index 7fc0d8da..603cbd6c 100644 --- a/src/fetcher/tiles/RoutableTileFetcherDefault.ts +++ b/src/fetcher/tiles/RoutableTileFetcherDefault.ts @@ -24,13 +24,12 @@ export default class RoutableTileFetcherDefault implements IRoutableTileFetcher constructor( @inject(TYPES.LDFetch) ldFetch: LDFetch, @inject(TYPES.PathfinderProvider) pathfinderProvider: PathfinderProvider, - @inject(TYPES.RoutableTileRegistry) routableTileRegistry: RoutableTileRegistry, ) { this.ldFetch = ldFetch; this.ldLoader = new LDLoader(); this.ldLoader.defineCollection(URI.inNS(OSM, "hasNodes")); // unordered collection this.pathfinderProvider = pathfinderProvider; - this.routableTileRegistry = routableTileRegistry; + this.routableTileRegistry = RoutableTileRegistry.getInstance(); } public async get(url: string): Promise { diff --git a/src/fetcher/tiles/RoutableTileFetcherExtended.ts b/src/fetcher/tiles/RoutableTileFetcherExtended.ts index 3ee64b20..f88dda90 100644 --- a/src/fetcher/tiles/RoutableTileFetcherExtended.ts +++ b/src/fetcher/tiles/RoutableTileFetcherExtended.ts @@ -1,6 +1,5 @@ import { inject, injectable } from "inversify"; import LDFetch from "ldfetch"; -import RoutableTileRegistry from "../../entities/tiles/registry"; import { RoutableTile } from "../../entities/tiles/tile"; import PathfinderProvider from "../../pathfinding/PathfinderProvider"; import TYPES from "../../types"; @@ -14,9 +13,8 @@ export default class RoutableTileFetcherExtended extends RoutableTileFetcherDefa constructor( @inject(TYPES.LDFetch) ldFetch: LDFetch, @inject(TYPES.PathfinderProvider) pathfinder: PathfinderProvider, - @inject(TYPES.RoutableTileRegistry) routableTileRegistry: RoutableTileRegistry, ) { - super(ldFetch, pathfinder, routableTileRegistry); + super(ldFetch, pathfinder); } public async get(url: string): Promise { diff --git a/src/fetcher/tiles/RoutableTileFetcherRaw.ts b/src/fetcher/tiles/RoutableTileFetcherRaw.ts index 288806ec..b4c9fb01 100644 --- a/src/fetcher/tiles/RoutableTileFetcherRaw.ts +++ b/src/fetcher/tiles/RoutableTileFetcherRaw.ts @@ -21,10 +21,9 @@ export default class RoutableTileFetcherRaw implements IRoutableTileFetcher { constructor( @inject(TYPES.PathfinderProvider) pathfinderProvider: PathfinderProvider, - @inject(TYPES.RoutableTileRegistry) routableTileRegistry: RoutableTileRegistry, ) { this.pathfinderProvider = pathfinderProvider; - this.routableTileRegistry = routableTileRegistry; + this.routableTileRegistry = RoutableTileRegistry.getInstance(); this.mapping = {}; this.mapping["osm:barrier"] = "barrierKind"; diff --git a/src/fetcher/tiles/RoutableTileProviderDefault.ts b/src/fetcher/tiles/RoutableTileProviderDefault.ts index 570290c0..7ce9e644 100644 --- a/src/fetcher/tiles/RoutableTileProviderDefault.ts +++ b/src/fetcher/tiles/RoutableTileProviderDefault.ts @@ -18,10 +18,9 @@ export default class RoutableTileProviderDefault implements IRoutableTileProvide constructor( @inject(TYPES.RoutableTileFetcher) fetcher: IRoutableTileFetcher, - @inject(TYPES.RoutableTileRegistry) registry: RoutableTileRegistry, ) { this.fetcher = fetcher; - this.registry = registry; + this.registry = RoutableTileRegistry.getInstance(); } public async wait() { diff --git a/src/fetcher/tiles/RoutableTileProviderIntermediate.ts b/src/fetcher/tiles/RoutableTileProviderIntermediate.ts new file mode 100644 index 00000000..4d3c3fb4 --- /dev/null +++ b/src/fetcher/tiles/RoutableTileProviderIntermediate.ts @@ -0,0 +1,19 @@ +import { inject, injectable } from "inversify"; +import { RoutableTileCoordinate } from "../../entities/tiles/coordinate"; +import TYPES from "../../types"; +import IRoutableTileFetcher from "./IRoutableTileFetcher"; +import RoutableTileProviderDefault from "./RoutableTileProviderDefault"; + +@injectable() +export default class RoutableTileProviderIntermediate extends RoutableTileProviderDefault { + + constructor( + @inject(TYPES.RoutableTileFetcher) fetcher: IRoutableTileFetcher, + ) { + super(fetcher); + } + + public getIdForTileCoords(coordinate: RoutableTileCoordinate): string { + return `https://hdelva.be/tiles/transit/${coordinate.zoom}/${coordinate.x}/${coordinate.y}`; + } +} diff --git a/src/fetcher/tiles/RoutableTileProviderTransit.ts b/src/fetcher/tiles/RoutableTileProviderTransit.ts index d8a068e7..ac81c43a 100644 --- a/src/fetcher/tiles/RoutableTileProviderTransit.ts +++ b/src/fetcher/tiles/RoutableTileProviderTransit.ts @@ -10,9 +10,8 @@ export default class RoutableTileProviderTransit extends RoutableTileProviderDef constructor( @inject(TYPES.RoutableTileFetcher) fetcher: IRoutableTileFetcher, - @inject(TYPES.RoutableTileRegistry) registry: RoutableTileRegistry, ) { - super(fetcher, registry); + super(fetcher); } public getIdForTileCoords(coordinate: RoutableTileCoordinate): string { diff --git a/src/index.ts b/src/index.ts index 163895c5..ec10d29e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,32 +2,39 @@ import "isomorphic-fetch"; import "reflect-metadata"; import IsochroneGenerator from "./analytics/isochrones/main"; +import TrafficEstimator from "./analytics/traffic/main"; +import RoutableTileRegistry_ from "./entities/tiles/registry"; import TravelMode from "./enums/TravelMode"; import EventBus_ from "./events/EventBus"; import EventType from "./events/EventType"; import BasicTrainPlanner from "./planner/configurations/BasicTrainPlanner"; import DelijnNmbsPlanner from "./planner/configurations/DelijnNmbsPlanner"; import DissectPlanner from "./planner/configurations/DissectPlanner"; +import FlexibleTransitPlanner from "./planner/configurations/FlexibleTransitPlanner"; import TransitCarPlanner from "./planner/configurations/TransitCarPlanner"; import TriangleDemoPlanner from "./planner/configurations/TriangleDemoPlanner"; import Units from "./util/Units"; export { default as EventType } from "./events/EventType"; export { default as IsochroneGenerator } from "./analytics/isochrones/main"; +export { default as TrafficEstimator } from "./analytics/traffic/main"; export { default as Units } from "./util/Units"; export { default as BasicTrainPlanner } from "./planner/configurations/BasicTrainPlanner"; export { default as DelijnNmbsPlanner } from "./planner/configurations/DelijnNmbsPlanner"; export { default as DissectPlanner } from "./planner/configurations/DissectPlanner"; export { default as TransitCarPlanner } from "./planner/configurations/TransitCarPlanner"; +export { default as FlexibleTransitPlanner } from "./planner/configurations/FlexibleTransitPlanner"; export { default as TriangleDemoPlanner } from "./planner/configurations/TriangleDemoPlanner"; export { default as TravelMode } from "./enums/TravelMode"; export const EventBus = EventBus_.getInstance(); +export const RoutableTileRegistry = RoutableTileRegistry_.getInstance(); export default { TravelMode, EventType, IsochroneGenerator, + TrafficEstimator, Units, EventBus, BasicTrainPlanner, @@ -35,4 +42,6 @@ export default { DissectPlanner, TransitCarPlanner, TriangleDemoPlanner, + RoutableTileRegistry, + FlexibleTransitPlanner, }; diff --git a/src/pathfinding/PathfinderProvider.ts b/src/pathfinding/PathfinderProvider.ts index dd083bbd..2b9b68db 100644 --- a/src/pathfinding/PathfinderProvider.ts +++ b/src/pathfinding/PathfinderProvider.ts @@ -47,14 +47,13 @@ export default class PathfinderProvider { constructor( @inject(TYPES.ShortestPathTreeAlgorithm) shortestPathTree: IShortestPathTreeAlgorithm, @inject(TYPES.ShortestPathAlgorithm) pointToPoint: IShortestPathAlgorithm, - @inject(TYPES.RoutableTileRegistry) routableTileRegistry: RoutableTileRegistry, @inject(TYPES.ProfileProvider) profileProvider: ProfileProvider, @inject(TYPES.LocationResolver) locationResolver: ILocationResolver, ) { this.locationResolver = locationResolver; this.shortestPath = pointToPoint; this.shortestPathTree = shortestPathTree; - this.routableTileRegistry = routableTileRegistry; + this.routableTileRegistry = RoutableTileRegistry.getInstance(); this.profileProvider = profileProvider; this.graphs = {}; this.embeddings = []; diff --git a/src/pathfinding/bidirdijkstra/BidirDijkstraInstance.ts b/src/pathfinding/bidirdijkstra/BidirDijkstraInstance.ts index be74926d..ffc3257d 100644 --- a/src/pathfinding/bidirdijkstra/BidirDijkstraInstance.ts +++ b/src/pathfinding/bidirdijkstra/BidirDijkstraInstance.ts @@ -51,6 +51,11 @@ export class BidirDijkstraInstance implements IShortestPathInstance { constructor(graph: PathfindingGraph) { this.useWeightedCost = true; this.graph = graph; + + this.forwardCosts = []; + this.forwardParents = []; + this.backwardCosts = []; + this.backwardParents = []; } public setUseWeightedCost(useWeightedCost: boolean) { @@ -76,10 +81,10 @@ export class BidirDijkstraInstance implements IShortestPathInstance { backwardQueue = new TinyQueue([], (a, b) => a.duration - b.duration); } - this.forwardCosts = [...Array(this.graph.getAdjacencyList().length)].fill(Infinity); - this.forwardParents = [...Array(this.graph.getAdjacencyList().length)].fill(undefined); - this.backwardCosts = [...Array(this.graph.getAdjacencyList().length)].fill(Infinity); - this.backwardParents = [...Array(this.graph.getAdjacencyList().length)].fill(undefined); + this.forwardCosts = this.forwardCosts.fill(Infinity); + this.forwardParents = this.forwardParents.fill(undefined); + this.backwardCosts = this.backwardCosts.fill(Infinity); + this.backwardParents = this.backwardParents.fill(undefined); const fromIndex = this.graph.getNodeIndex(from); const toIndex = this.graph.getNodeIndex(to); diff --git a/src/pathfinding/dijkstra/DijkstraInstance.ts b/src/pathfinding/dijkstra/DijkstraInstance.ts index 7a04d1b8..73ece385 100644 --- a/src/pathfinding/dijkstra/DijkstraInstance.ts +++ b/src/pathfinding/dijkstra/DijkstraInstance.ts @@ -4,23 +4,32 @@ import { DistanceM, DurationMs } from "../../interfaces/units"; import PathfindingGraph from "../graph"; import { IShortestPathInstance } from "../pathfinder"; +const POSITION = 0; +const DISTANCE = 1; +const DURATION = 2; +const COST = 3; + +/* interface IState { position: number; distance: DistanceM; duration: DurationMs; cost: number; -} +}duration +*/ @injectable() export class DijkstraInstance implements IShortestPathInstance { private graph: PathfindingGraph; private useWeightedCost: boolean; - private costs: number[]; - private previousNodes: number[]; + private costs: Float32Array; + private previousNodes: Float32Array; constructor(graph: PathfindingGraph) { this.useWeightedCost = true; this.graph = graph; + this.costs = new Float32Array(); + this.previousNodes = new Float32Array(); } public setUseWeightedCost(useWeightedCost: boolean) { @@ -36,24 +45,29 @@ export class DijkstraInstance implements IShortestPathInstance { } public async queryPath(from: string, to: string, maxDistance = Infinity) { - let queue: TinyQueue; + let queue: TinyQueue; if (this.useWeightedCost) { - queue = new TinyQueue([], (a, b) => a.cost - b.cost); + queue = new TinyQueue([], (a, b) => a[COST] - b[COST]); } else { - queue = new TinyQueue([], (a, b) => a.duration - b.duration); + queue = new TinyQueue([], (a, b) => a[DURATION] - b[DURATION]); } - this.costs = [...Array(this.graph.getAdjacencyList().length)].fill(Infinity); - this.previousNodes = [...Array(this.graph.getAdjacencyList().length)].fill(undefined); + this.costs = this.costs.fill(Infinity); + this.previousNodes = this.previousNodes.fill(undefined); const fromIndex = this.graph.getNodeIndex(from); this.setCost(fromIndex, 0); - queue.push({ distance: 0, duration: 0, cost: 0, position: fromIndex }); + const state = new Float32Array(4); + state[COST] = 0; + state[POSITION] = fromIndex; + state[DISTANCE] = 0; + state[DURATION] = 0; + queue.push(state); const toIndex = this.graph.getNodeIndex(to); while (queue.length) { - const { duration, distance, cost, position } = queue.pop(); + const [position, distance, duration, cost] = queue.pop(); if (distance > maxDistance) { // avoid hopeless searches @@ -75,16 +89,22 @@ export class DijkstraInstance implements IShortestPathInstance { } for (const edge of this.graph.getAdjacencyList()[position]) { - const next = { + const next = new Float32Array(4); + next[COST] = cost + edge.cost; + next[POSITION] = edge.node; + next[DISTANCE] = distance + edge.distance; + next[DURATION] = duration + edge.duration; + + /*const next = { distance: distance + edge.distance, duration: duration + edge.duration, - cost: cost + edge.cost, + cost: Math.fround(cost + edge.cost), position: edge.node, - }; + };*/ - if (next.cost < this.getCost(next.position)) { - this.setCost(next.position, next.cost); - this.previousNodes[next.position] = position; + if (next[COST] < this.getCost(next[POSITION])) { + this.setCost(next[POSITION], next[COST]); + this.previousNodes[next[POSITION]] = position; queue.push(next); } } @@ -94,7 +114,7 @@ export class DijkstraInstance implements IShortestPathInstance { const steps = []; // reconstruct the path - while (this.previousNodes[currentPosition] !== undefined) { + while (!isNaN(this.previousNodes[currentPosition])) { let nextEdge; let nextPositionCost = Infinity; @@ -124,18 +144,30 @@ export class DijkstraInstance implements IShortestPathInstance { private setCost(position: number, cost: number) { if (position >= this.costs.length) { - const missingCosts = this.graph.getAdjacencyList().length - this.costs.length; - this.costs = this.costs.concat([...Array(missingCosts)].fill(Infinity)); - this.previousNodes = this.previousNodes.concat([...Array(missingCosts)].fill(undefined)); + const newCosts = new Float32Array(this.graph.getAdjacencyList().length) + .fill(Infinity, this.costs.length); + newCosts.set(this.costs); + this.costs = newCosts; + + const newPrevious = new Float32Array(this.graph.getAdjacencyList().length) + .fill(undefined, this.previousNodes.length); + newPrevious.set(this.previousNodes); + this.previousNodes = newPrevious; } this.costs[position] = cost; } private getCost(position: number): number { if (position >= this.costs.length) { - const missingCosts = this.graph.getAdjacencyList().length - this.costs.length; - this.costs = this.costs.concat([...Array(missingCosts)].fill(Infinity)); - this.previousNodes = this.previousNodes.concat([...Array(missingCosts)].fill(undefined)); + const newCosts = new Float32Array(this.graph.getAdjacencyList().length) + .fill(Infinity, this.costs.length); + newCosts.set(this.costs); + this.costs = newCosts; + + const newPrevious = new Float32Array(this.graph.getAdjacencyList().length) + .fill(undefined, this.previousNodes.length); + newPrevious.set(this.previousNodes); + this.previousNodes = newPrevious; } return this.costs[position]; } diff --git a/src/planner/configurations/FlexibleTransitPlanner.ts b/src/planner/configurations/FlexibleTransitPlanner.ts new file mode 100644 index 00000000..a550b4a6 --- /dev/null +++ b/src/planner/configurations/FlexibleTransitPlanner.ts @@ -0,0 +1,8 @@ +import flexibleTransit from "../../configs/flexible_transit"; +import Planner from "./Planner"; + +export default class DissectPlanner extends Planner { + constructor() { + super(flexibleTransit); + } +} diff --git a/src/planner/configurations/Planner.ts b/src/planner/configurations/Planner.ts index ce5b0272..425c0218 100644 --- a/src/planner/configurations/Planner.ts +++ b/src/planner/configurations/Planner.ts @@ -1,6 +1,7 @@ import { AsyncIterator } from "asynciterator"; import { PromiseProxyIterator } from "asynciterator-promiseproxy"; import { EventEmitter } from "events"; +import defaultContainer from "../../configs/basic_train"; import Context from "../../Context"; import TravelMode from "../../enums/TravelMode"; import EventBus from "../../events/EventBus"; @@ -11,7 +12,6 @@ import IStop from "../../fetcher/stops/IStop"; import IStopsProvider from "../../fetcher/stops/IStopsProvider"; import IPath from "../../interfaces/IPath"; import IQuery from "../../interfaces/IQuery"; -import defaultContainer from "../../inversify.config"; import IQueryRunner from "../../query-runner/IQueryRunner"; import TYPES from "../../types"; import Iterators from "../../util/Iterators"; @@ -32,6 +32,7 @@ export default abstract class Planner { private profileProvider: ProfileProvider; private roadPlanner: IRoadPlanner; private connectionsProvider: IConnectionsProvider; + private stopsProvider: IStopsProvider; /** * Initializes a new Planner @@ -47,10 +48,19 @@ export default abstract class Planner { this.eventBus = EventBus.getInstance(); this.roadPlanner = container.get(TYPES.RoadPlanner); this.connectionsProvider = container.get(TYPES.ConnectionsProvider); + this.stopsProvider = container.get(TYPES.StopsProvider); this.activeProfileID = "https://hdelva.be/profile/pedestrian"; } + public addConnectionSource(accessUrl: string, travelMode = TravelMode.Train) { + this.connectionsProvider.addConnectionSource({ accessUrl, travelMode }); + } + + public addStopSource(accessUrl: string) { + this.stopsProvider.addStopSource(accessUrl); + } + public async completePath(path: IPath): Promise { const completePath = Path.create(); diff --git a/src/planner/public-transport/CSAEarliestArrival.test.ts b/src/planner/public-transport/CSAEarliestArrival.test.ts index c2a18250..9befecf2 100644 --- a/src/planner/public-transport/CSAEarliestArrival.test.ts +++ b/src/planner/public-transport/CSAEarliestArrival.test.ts @@ -8,6 +8,7 @@ import ConnectionsFetcherRaw from "../../fetcher/connections/ConnectionsFetcherR import ConnectionsProviderDefault from "../../fetcher/connections/ConnectionsProviderDefault"; import ConnectionsProviderNMBSTest from "../../fetcher/connections/tests/ConnectionsProviderNMBSTest"; import connectionsIngelmunsterGhent from "../../fetcher/connections/tests/data/ingelmunster-ghent"; +import HydraTemplateFetcherDefault from "../../fetcher/hydra/HydraTemplateFetcherDefault"; import StopsFetcherLDFetch from "../../fetcher/stops/ld-fetch/StopsFetcherLDFetch"; import ILeg from "../../interfaces/ILeg"; import IPath from "../../interfaces/IPath"; @@ -30,7 +31,7 @@ describe("[PublicTransportPlannerCSAEarliestArrival]", () => { const stopsFetcher = new StopsFetcherLDFetch(ldFetch); stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS"); - const locationResolver = new LocationResolverDefault(stopsFetcher, new RoutableTileRegistry()); + const locationResolver = new LocationResolverDefault(stopsFetcher); const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher); return new CSAEarliestArrival( @@ -180,12 +181,13 @@ describe("[PublicTransportPlannerCSAEarliestArrival]", () => { fetcher.setTravelMode(travelMode); return fetcher; }, catalog, + new HydraTemplateFetcherDefault(ldFetch), ); const stopsFetcher = new StopsFetcherLDFetch(ldFetch); stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS"); - const locationResolver = new LocationResolverDefault(stopsFetcher, new RoutableTileRegistry()); + const locationResolver = new LocationResolverDefault(stopsFetcher); const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher); const CSA = new CSAEarliestArrival( diff --git a/src/planner/public-transport/CSAEarliestArrival.ts b/src/planner/public-transport/CSAEarliestArrival.ts index 1d97e5e6..967431ad 100644 --- a/src/planner/public-transport/CSAEarliestArrival.ts +++ b/src/planner/public-transport/CSAEarliestArrival.ts @@ -102,7 +102,7 @@ export default class CSAEarliestArrival implements IPublicTransportPlanner { } = query; const footpathsQueue = new FootpathQueue(); - const connectionsIterator = this.connectionsProvider.createIterator({ + const connectionsIterator = await this.connectionsProvider.createIterator({ upperBoundDate, lowerBoundDate, excludedModes: query.excludedTravelModes, diff --git a/src/planner/public-transport/CSAProfile.test.ts b/src/planner/public-transport/CSAProfile.test.ts index 72f9c95e..9d728d7e 100644 --- a/src/planner/public-transport/CSAProfile.test.ts +++ b/src/planner/public-transport/CSAProfile.test.ts @@ -10,6 +10,7 @@ import ConnectionsProviderNMBSTest from "../../fetcher/connections/tests/Connect import connectionsIngelmunsterGhent from "../../fetcher/connections/tests/data/ingelmunster-ghent"; import connectionsJoining from "../../fetcher/connections/tests/data/joining"; import connectionsSplitting from "../../fetcher/connections/tests/data/splitting"; +import HydraTemplateFetcherDefault from "../../fetcher/hydra/HydraTemplateFetcherDefault"; import StopsFetcherLDFetch from "../../fetcher/stops/ld-fetch/StopsFetcherLDFetch"; import ILeg from "../../interfaces/ILeg"; import IPath from "../../interfaces/IPath"; @@ -33,7 +34,7 @@ describe("[PublicTransportPlannerCSAProfile]", () => { const stopsFetcher = new StopsFetcherLDFetch(ldFetch); stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS"); - const locationResolver = new LocationResolverDefault(stopsFetcher, new RoutableTileRegistry()); + const locationResolver = new LocationResolverDefault(stopsFetcher); const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher); const journeyExtractor = new JourneyExtractorProfile( locationResolver, @@ -81,96 +82,6 @@ describe("[PublicTransportPlannerCSAProfile]", () => { } }); }); - - describe("splitting", () => { - let result: IPath[]; - - const query: IResolvedQuery = { - from: [{ - id: "http://irail.be/stations/NMBS/008821006", - latitude: 51.2172, - longitude: 4.421101, - }], - to: [{ - id: "http://irail.be/stations/NMBS/008812005", - latitude: 50.859663, - longitude: 4.360846, - }], - profileID: "https://hdelva.be/profile/pedestrian", - minimumDepartureTime: new Date("2017-12-19T15:50:00.000Z"), - maximumArrivalTime: new Date("2017-12-19T16:50:00.000Z"), - maximumTransfers: 1, - minimumWalkingSpeed: Defaults.defaultMinimumWalkingSpeed, - maximumWalkingSpeed: Defaults.defaultMaximumWalkingSpeed, - maximumTransferDuration: Defaults.defaultMaximumTransferDuration, - }; - - beforeAll(async () => { - const CSA = createCSA(connectionsSplitting); - const iterator = await CSA.plan(query); - result = await Iterators.toArray(iterator); - }); - - it("Correct departure and arrival stop", () => { - expect(result).toBeDefined(); - expect(result.length).toBeGreaterThanOrEqual(1); - - for (const path of result) { - expect(path.legs).toBeDefined(); - - expect(path.legs.length).toEqual(1); - expect(path.legs[0]).toBeDefined(); - - expect(query.from.map((from) => from.id)).toContain(path.legs[0].getStartLocation().id); - expect(query.to.map((to) => to.id)).toContain(path.legs[0].getStopLocation().id); - } - }); - }); - - describe("joining", () => { - let result: IPath[]; - - const query: IResolvedQuery = { - from: [{ - id: "http://irail.be/stations/NMBS/008812005", - latitude: 50.859663, - longitude: 4.360846, - }], - to: [{ - id: "http://irail.be/stations/NMBS/008821006", - latitude: 51.2172, - longitude: 4.421101, - }], - profileID: "https://hdelva.be/profile/pedestrian", - minimumDepartureTime: new Date("2017-12-19T16:20:00.000Z"), - maximumArrivalTime: new Date("2017-12-19T16:50:00.000Z"), - maximumTransfers: 1, - minimumWalkingSpeed: Defaults.defaultMinimumWalkingSpeed, - maximumWalkingSpeed: Defaults.defaultMaximumWalkingSpeed, - maximumTransferDuration: Defaults.defaultMaximumTransferDuration, - }; - - beforeAll(async () => { - const CSA = createCSA(connectionsJoining); - const iterator = await CSA.plan(query); - result = await Iterators.toArray(iterator); - }); - - it("Correct departure and arrival stop", () => { - expect(result).toBeDefined(); - expect(result.length).toBeGreaterThanOrEqual(1); - - for (const path of result) { - expect(path.legs).toBeDefined(); - - expect(path.legs.length).toEqual(1); - expect(path.legs[0]).toBeDefined(); - - expect(query.from.map((from) => from.id)).toContain(path.legs[0].getStartLocation().id); - expect(query.to.map((to) => to.id)).toContain(path.legs[0].getStopLocation().id); - } - }); - }); }); describe("real-time data", () => { @@ -185,12 +96,13 @@ describe("[PublicTransportPlannerCSAProfile]", () => { fetcher.setTravelMode(travelMode); return fetcher; }, catalog, + new HydraTemplateFetcherDefault(ldFetch), ); const stopsFetcher = new StopsFetcherLDFetch(ldFetch); stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS"); - const locationResolver = new LocationResolverDefault(stopsFetcher, new RoutableTileRegistry()); + const locationResolver = new LocationResolverDefault(stopsFetcher); const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher); const journeyExtractor = new JourneyExtractorProfile( locationResolver, diff --git a/src/planner/public-transport/CSAProfile.ts b/src/planner/public-transport/CSAProfile.ts index 208482e1..f858808b 100644 --- a/src/planner/public-transport/CSAProfile.ts +++ b/src/planner/public-transport/CSAProfile.ts @@ -120,7 +120,7 @@ export default class CSAProfile implements IPublicTransportPlanner { } = query; const footpathQueue = new FootpathQueue(true); - const connectionsIterator = this.connectionsProvider.createIterator({ + const connectionsIterator = await this.connectionsProvider.createIterator({ backward: true, upperBoundDate, lowerBoundDate, @@ -260,7 +260,10 @@ export default class CSAProfile implements IPublicTransportPlanner { private getTripIdsFromConnection(queryState: IQueryState, connection: IConnection): string[] { const tripIds: string[] = [connection.tripId]; + return tripIds; + /* + // nextConnection isn't useable yet if (!connection.nextConnection) { return tripIds; } @@ -274,6 +277,7 @@ export default class CSAProfile implements IPublicTransportPlanner { } return tripIds; + */ } private calculateEarliestArrivalTime(queryState: IQueryState, connection: IConnection): IArrivalTimeByTransfers { diff --git a/src/planner/road/RoadPlannerBirdsEye.test.ts b/src/planner/road/RoadPlannerBirdsEye.test.ts index 18b329a6..685f3fa0 100644 --- a/src/planner/road/RoadPlannerBirdsEye.test.ts +++ b/src/planner/road/RoadPlannerBirdsEye.test.ts @@ -14,7 +14,7 @@ const planner: IRoadPlanner = new RoadPlannerBirdsEye(); const stopsFetcher = new StopsFetcherLDFetch(ldFetch); stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS"); -const locationResolver = new LocationResolverDefault(stopsFetcher, new RoutableTileRegistry()); +const locationResolver = new LocationResolverDefault(stopsFetcher); test("[RoadPlannerBirdsEye] distance between stops", async () => { diff --git a/src/planner/road/RoadPlannerPathfinding.ts b/src/planner/road/RoadPlannerPathfinding.ts index ec5f8ad2..f57e4f01 100644 --- a/src/planner/road/RoadPlannerPathfinding.ts +++ b/src/planner/road/RoadPlannerPathfinding.ts @@ -42,13 +42,12 @@ export default class RoadPlannerPathfinding implements IRoadPlanner { @inject(TYPES.PathfinderProvider) pathfinderProvider: PathfinderProvider, @inject(TYPES.ProfileProvider) profileProvider: IProfileProvider, @inject(TYPES.LocationResolver) locationResolver: ILocationResolver, - @inject(TYPES.RoutableTileRegistry) registry: RoutableTileRegistry, ) { this.tileProvider = tileProvider; this.pathfinderProvider = pathfinderProvider; this.profileProvider = profileProvider; this.locationResolver = locationResolver; - this.registry = registry; + this.registry = RoutableTileRegistry.getInstance(); this.eventBus = EventBus.getInstance(); this.reachedTiles = new Set(); } diff --git a/src/planner/road/RoadPlannerPathfindingExperimental.ts b/src/planner/road/RoadPlannerPathfindingExperimental.ts index 1b97627c..7146886d 100644 --- a/src/planner/road/RoadPlannerPathfindingExperimental.ts +++ b/src/planner/road/RoadPlannerPathfindingExperimental.ts @@ -51,14 +51,13 @@ export default class RoadPlannerPathfindingExperimental implements IRoadPlanner @inject(TYPES.PathfinderProvider) pathfinderProvider: PathfinderProvider, @inject(TYPES.ProfileProvider) profileProvider: IProfileProvider, @inject(TYPES.LocationResolver) locationResolver: ILocationResolver, - @inject(TYPES.RoutableTileRegistry) registry: RoutableTileRegistry, ) { this.baseTileProvider = baseTileProvider; this.transitTileProvider = transitTileProvider; this.pathfinderProvider = pathfinderProvider; this.profileProvider = profileProvider; this.locationResolver = locationResolver; - this.registry = registry; + this.registry = RoutableTileRegistry.getInstance(); this.eventBus = EventBus.getInstance(); this.reachedTiles = new Set(); } diff --git a/src/query-runner/LocationResolverConvenience.test.ts b/src/query-runner/LocationResolverConvenience.test.ts index 041d9f41..3da63c76 100644 --- a/src/query-runner/LocationResolverConvenience.test.ts +++ b/src/query-runner/LocationResolverConvenience.test.ts @@ -1,16 +1,14 @@ import "jest"; import LDFetch from "ldfetch"; -import RoutableTileRegistry from "../entities/tiles/registry"; import StopsFetcherLDFetch from "../fetcher/stops/ld-fetch/StopsFetcherLDFetch"; import LocationResolverConvenience from "./LocationResolverConvenience"; const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } }); const stopsFetcher = new StopsFetcherLDFetch(ldFetch); -const tileRegistry = new RoutableTileRegistry(); stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS"); -const locationResolver = new LocationResolverConvenience(stopsFetcher, tileRegistry); +const locationResolver = new LocationResolverConvenience(stopsFetcher); describe("[LocationResolverConvenience]", () => { diff --git a/src/query-runner/LocationResolverConvenience.ts b/src/query-runner/LocationResolverConvenience.ts index 556619f4..f737e4b6 100644 --- a/src/query-runner/LocationResolverConvenience.ts +++ b/src/query-runner/LocationResolverConvenience.ts @@ -21,10 +21,9 @@ export default class LocationResolverConvenience implements ILocationResolver { constructor( @inject(TYPES.StopsProvider) stopsProvider: IStopsProvider, - @inject(TYPES.RoutableTileRegistry) tileRegistry: RoutableTileRegistry, ) { this.stopsProvider = stopsProvider; - this.defaultLocationResolver = new LocationResolverDefault(this.stopsProvider, tileRegistry); + this.defaultLocationResolver = new LocationResolverDefault(this.stopsProvider); } public async resolve(input: ILocation | IStop | string): Promise { @@ -42,9 +41,7 @@ export default class LocationResolverConvenience implements ILocationResolver { } } - if (!this.allStops) { - this.allStops = await this.stopsProvider.getAllStops(); - } + this.allStops = await this.stopsProvider.getAllStops(); const matchingStop = this.allStops.find((stop: IStop) => stop.name === input); diff --git a/src/query-runner/LocationResolverDefault.test.ts b/src/query-runner/LocationResolverDefault.test.ts index bf7dd9a6..1ec1a03d 100644 --- a/src/query-runner/LocationResolverDefault.test.ts +++ b/src/query-runner/LocationResolverDefault.test.ts @@ -1,6 +1,5 @@ import "jest"; import LDFetch from "ldfetch"; -import RoutableTileRegistry from "../entities/tiles/registry"; import StopsFetcherLDFetch from "../fetcher/stops/ld-fetch/StopsFetcherLDFetch"; import LocationResolverDefault from "./LocationResolverDefault"; @@ -9,7 +8,7 @@ const ldFetch = new LDFetch({ headers: { Accept: "application/ld+json" } }); const stopsFetcher = new StopsFetcherLDFetch(ldFetch); stopsFetcher.setAccessUrl("https://irail.be/stations/NMBS"); -const locationResolver = new LocationResolverDefault(stopsFetcher, new RoutableTileRegistry()); +const locationResolver = new LocationResolverDefault(stopsFetcher); test("[LocationResolverDefault] Input {id: 'http://...'}", async () => { diff --git a/src/query-runner/LocationResolverDefault.ts b/src/query-runner/LocationResolverDefault.ts index 21db8296..1566b76f 100644 --- a/src/query-runner/LocationResolverDefault.ts +++ b/src/query-runner/LocationResolverDefault.ts @@ -21,10 +21,9 @@ export default class LocationResolverDefault implements ILocationResolver { constructor( @inject(TYPES.StopsProvider) stopsProvider: IStopsProvider, - @inject(TYPES.RoutableTileRegistry) tileRegistry: RoutableTileRegistry, ) { this.stopsProvider = stopsProvider; - this.tileRegistry = tileRegistry; + this.tileRegistry = RoutableTileRegistry.getInstance(); } public async resolve(input: ILocation | IStop | string): Promise { diff --git a/src/query-runner/exponential/QueryRunnerExponential.test.ts b/src/query-runner/exponential/QueryRunnerExponential.test.ts index 64091e11..b3ed2a6b 100644 --- a/src/query-runner/exponential/QueryRunnerExponential.test.ts +++ b/src/query-runner/exponential/QueryRunnerExponential.test.ts @@ -1,10 +1,10 @@ import "jest"; import LDFetch from "ldfetch"; import Catalog from "../../Catalog"; -import RoutableTileRegistry from "../../entities/tiles/registry"; import TravelMode from "../../enums/TravelMode"; import ConnectionsFetcherRaw from "../../fetcher/connections/ConnectionsFetcherRaw"; import ConnectionsProviderDefault from "../../fetcher/connections/ConnectionsProviderDefault"; +import HydraTemplateFetcherDefault from "../../fetcher/hydra/HydraTemplateFetcherDefault"; import StopsFetcherLDFetch from "../../fetcher/stops/ld-fetch/StopsFetcherLDFetch"; import ILeg from "../../interfaces/ILeg"; import IPath from "../../interfaces/IPath"; @@ -40,9 +40,9 @@ describe("[QueryRunnerExponential]", () => { const connectionProvider = new ConnectionsProviderDefault((travelMode: TravelMode) => { connectionsFetcher.setTravelMode(travelMode); return connectionsFetcher; - }, catalog); + }, catalog, new HydraTemplateFetcherDefault(ldFetch)); - const locationResolver = new LocationResolverDefault(stopsFetcher, new RoutableTileRegistry()); + const locationResolver = new LocationResolverDefault(stopsFetcher); const reachableStopsFinder = new ReachableStopsFinderBirdsEyeCached(stopsFetcher); const createJourneyExtractor = () => { diff --git a/src/types.ts b/src/types.ts index 97d8574b..516285b8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,6 +9,8 @@ const TYPES = { QueryRunner: Symbol("QueryRunner"), LocationResolver: Symbol("LocationResolver"), + HydraTemplateFetcher: Symbol("HydraTemplateFetcher"), + ConnectionsProvider: Symbol("ConnectionsProvider"), ConnectionsFetcher: Symbol("ConnectionsFetcher"), ConnectionsFetcherFactory: Symbol("ConnectionsFetcherFactory"), @@ -19,7 +21,6 @@ const TYPES = { RoutableTileProvider: Symbol("TileProvider"), RoutableTileFetcher: Symbol("TileFetcher"), - RoutableTileRegistry: Symbol("RoutableTileRegistry"), FootpathsProvider: Symbol("FootpathsProvider"), diff --git a/src/uri/JSONLDContext.ts b/src/uri/JSONLDContext.ts new file mode 100644 index 00000000..e34a5a6f --- /dev/null +++ b/src/uri/JSONLDContext.ts @@ -0,0 +1,79 @@ +import URI from "./uri"; + +export default class JSONLDContext { + private rawData: object; + private aliases: object; + + public constructor(blob: object) { + this.rawData = {}; + this.aliases = {}; + + if (Array.isArray(blob)) { + this.processArray(blob); + } else { + this.rawData = blob; + this.processObject(blob); + } + } + + public resolveIdentifier(name: string): string { + if (name.indexOf("://") > 0) { + // absolute URI + return name; + } + + if (this.aliases[name]) { + return this.aliases[name]; + } + + if (name.indexOf(":") > 0) { + this.aliases[name] = this.expandURI(name); + return this.aliases[name]; + } + + return name; + } + + private processObject(blob: object) { + for (const [name, data] of Object.entries(blob)) { + if (name.indexOf(":") >= 0) { + this.aliases[name] = this.expandURI(name); + } + + if (typeof (data) === "object") { + if (data["@id"]) { + const rawId = data["@id"]; + this.aliases[name] = this.expandURI(rawId); + } + } else if (typeof (data) === "string") { + this.aliases[name] = this.expandURI(data); + } + } + } + + private expandURI(name: string) { + if (name.indexOf("://") > 0) { + // absolute URI + return name; + } + + if (name.indexOf(":") < 0) { + return name; + } + + const [prefix, rest] = name.split(":"); + const namespace = this.rawData[prefix]; + if (namespace) { + return URI.inNS(namespace, rest); + } else { + return name; + } + } + + private processArray(blobs: object[]) { + for (const blob of blobs) { + this.rawData = Object.assign(this.rawData, blob); + this.processObject(blob); + } + } +}