The ultimate JavaScript framework for journey planning. A level playing field for Mobility as a Service actors.
Everyone should be able to set up their own route planner with their specific needs. Before Planner.js, it used to be expensive to set up your own route planner: you first need to find the right data dumps, need to integrate them manually into your own format, and then you need to connect them to a route planning system. The real-time data then should be handled with a different system if is available at all.
See the Pen Planner.js results NMBS by Julian Rojas (@julianrojas87) on CodePen.
Planner.js makes heavy use of inversify, a dependency injection framework. This allows anyone to make a quick custom build of a route planner, with specific needs. Configuration of what classes should be injected where, happens by inside an inversify Container. The default Container (used by the CDN version) can be found in inversify.config.ts
For a full overview, please consult our code documentation. The interfaces for the Planner are split in:
Providers serve as data interfaces for all fetchers of the same type (registered in the inversify Container). When a class needs some data, it injects a Provider of the right type. The Provider determines the right fetcher and passes any data requests through to that fetcher.
Right now, there are two types of data, so there are two types of providers: connections providers and stops providers
These are ways to fetch data from different sources. Each fetcher corresponds to one source. We will use the Comunica framework to fetch the data as an intelligent agent. For now, we are just using the Linked Data Fetch NPM package and manually implement the routable tiles and Linked Connections specification.
Right now, there are two types of data, so there are two types of fetchers: connections fetchers and stops fetchers
These represent the core algorithms of this library. There are road planners and public-transport planners. Additionally, there are reachable stops finders, which are used in certain steps of public-transport planner algorithms
We still need to properly write this part of the documentation. However, the code may help you out understanding how it currently works. By default, the file inversify.config.ts is going to be used to build the Planner. The Planner instance must be instantiated with the dependencies container. If you bundle a different dependencies container with your Planner, your specific Planner will be able to act differently.
To allow maximum flexibility, some algorithms allow injecting multiple implementations of the same interface, depending on the phase of the algorithm. For example:
container.bind<IJourneyExtractor>(TYPES.JourneyExtractor).to(JourneyExtractorDefault);
container.bind<IRoadPlanner>(TYPES.RoadPlanner).to(RoadPlannerBirdsEye).whenTargetTagged("phase", JourneyExtractionPhase.Initial);
container.bind<IRoadPlanner>(TYPES.RoadPlanner).to(RoadPlannerBirdsEye).whenTargetTagged("phase", JourneyExtractionPhase.Transfer);
container.bind<IRoadPlanner>(TYPES.RoadPlanner).to(RoadPlannerBirdsEye).whenTargetTagged("phase", JourneyExtractionPhase.Final);
This example is pointless right now, because only one road planner is implemented
Each container should have a Catalog which holds the access URLs (and other meta data) of all data sources. For example, a planner that should only plan NMBS routes could have this catalog:
const catalog = new Catalog();
catalog.addStopsFetcher("http://irail.be/stations/NMBS/", "https://irail.be/stations/NMBS");
catalog.addConnectionsFetcher("https://graph.irail.be/sncb/connections", TravelMode.Train);
container.bind<Catalog>(TYPES.Catalog).toConstantValue(catalog);
Providers create all the necessary fetchers based on the data sources configured in the catalog. Factories form the glue between all these parts: they create a fetcher based on a catalog entry on behalf of a provider. Warning: subject to change
For example:
container.bind<IConnectionsProvider>(TYPES.ConnectionsProvider).to(ConnectionsProviderPassthrough).inSingletonScope();
container.bind<IConnectionsFetcher>(TYPES.ConnectionsFetcher).to(ConnectionsFetcherLazy);
container.bind<interfaces.Factory<IConnectionsFetcher>>(TYPES.ConnectionsFetcherFactory)
.toFactory<IConnectionsFetcher>(
(context: interfaces.Context) =>
(travelMode: TravelMode) => {
const fetcher = context.container.get<ConnectionsFetcherLazy>(TYPES.ConnectionsFetcher);
fetcher.setTravelMode(travelMode);
return fetcher;
},
);
One could make a dependency container specifically for shared bikes and public transport.
Another example would be to create a dependency container for public transport systems only. In this case, we would change the RoadPlanner to just using RoadPlannerBirdsEye, in order to understand where we can transfer.
This project is licensed under the MIT License - see the LICENSE.md file for details