/hafas-monitor-trips

Using a HAFAS endpoint, watch all trips in a bounding box.

Primary LanguageJavaScriptISC LicenseISC

hafas-monitor-trips

Using a HAFAS endpoint, watch all movements/vehicles in a bounding box, and optionally their trips.

npm version build status ISC-licensed support me via GitHub Sponsors chat with me on Twitter

hafas-monitor-trips will periodically fetch all movements/vehicles in the whole bounding box. If configured, it will then fetch each vehicle's whole trip to get up-to-date data about its stopovers.

Installation

Note: hafas-monitor-trips needs access to Redis. Set the REDIS_URL environment variable to configure access to it.

npm install hafas-monitor-trips

Usage

In the following example, we'll keep things simple:

  • We use vbb-hafas, a HAFAS client querying endpoint of the Berlin & Brandenburg public transport service (VBB).
  • We monitor a small bounding box in the center of Berlin.
  • We console.log all positionss (of all movements/vehicles being monitored).

Note: hafas-monitor-trips only works with hafas-client@6-compatible API clients.

import {createVbbHafas as createHafas} from 'vbb-hafas'
import {createMonitor} from 'hafas-monitor-trips'

const bbox = { // Potsdamer Platz in Berlin
	north: 52.52,
	west: 13.36,
	south: 52.5,
	east: 13.39,
}

const hafas = createHafas('hafas-monitor-trips example')
const monitor = createMonitor(hafas, bbox)

monitor.on('error', err => console.error(err))
monitor.on('hafas-error', err => console.error(err))
monitor.on('hafas-client-error', err => console.error(err))
monitor.on('position', (loc, movement) => console.log(loc, movement))

You can listen for these events:

  • error – An error occured, e.g. a network error.
  • hafas-error – The HAFAS endpoint rejected a request with an error.
  • hafas-client-errorhafas-client failed to process the HAFAS response.
  • position – The current (estimated) position and full details of a movement/vehicle.

If you listen to position events, you'll receive all movements (a movement one of >0 results of a radar() call) which are within the bounding box right now. Each movement will not have all stopovers of its trip though, just the next few; If you want to access all stopovers, you need to fetch the respective trip (see below).

fetching trips

You can configure hafas-monitor-trips to also fetch a movements trip, including all its stopovers. You can configure

  • which movements/vehicles to fetch the trip for (e.g. all, or just buses starting with "1"), as well as
  • when to fetch the trip (by default, right after the movement/vehicle has been fetched, see below for more details), the trip fetching strategy.

These additional events will be emitted:

  • trip – Every trip that has been fetched.
  • stopover – Each stopover of every trip that has been fetched.

fetching trips right away

For example, we can fetch all buses' trips, right away:

import {
	addTripsFetchingToMonitor as fetchTrips,
} from 'hafas-monitor-trips/fetch-trips/index.js'
import {
	createRightAwayTripFetchingStrategy as rightAwayStrategy,
} from 'hafas-monitor-trips/fetch-trips/right-away.js'

const shouldFetchTrip = movement => movement.line.product === 'bus'
fetchTrips(monitor, rightAwayStrategy(shouldFetchTrip))

monitor.on('trip', trip => console.log(trip.stopovers))

fetching important trips first

You can control the order in which the trips get fetched. For example, let's fetch the "A" line's trips first, then all other buses' trips:

import {
	addTripsFetchingToMonitor as fetchTrips,
} from 'hafas-monitor-trips/fetch-trips/index.js'
import {
	createPriorityBasedTripFetchingStrategy as priorityBasedStrategy,
} from 'hafas-monitor-trips/fetch-trips/priority-based.js'

const shouldFetchTrip = (movement) => {
	if (movement.line.product === 'bus') {
		return movement.line.name === 'A' ? 1 : 0
	}
	return null // don't fetch other products
}
fetchTrips(monitor, priorityBasedStrategy(shouldFetchTrip))

fetching trips with a delay

You can fetch the trips after a certain delay. For example, let's fetch trips after 10s, but only for movements with a delayed next stopover:

import {
	addTripsFetchingToMonitor as fetchTrips,
} from 'hafas-monitor-trips/fetch-trips/index.js'
import {
	createTimeBasedTripFetchingStrategy as timeBasedStrategy,
} from 'hafas-monitor-trips/fetch-trips/time-based.js'

const shouldFetchTrip = (movement) => {
	const nextStopover = movement.nextStopovers[0]
	if (nextStopover && nextStopover.arrivalDelay > 0) {
		return 10 * 1000
	}
	return null // don't fetch other movements' trips
}
fetchTrips(monitor, timeBasedStrategy(shouldFetchTrip))

preventing excessive requests

If you fetch all movements' trips, with a bounding larger than a few km², there will be so many HAFAS calls made that you will likely get rate-limited by the HAFAS endpoint; The amount depends on the specific endpoint. This is how you can reduce the request rate:

  • Use the shouldFetchTrip(movement) function to restrict the number of movements/vehicles that you fetch trips of, e.g. to only the line you're interested in, or only buses.
  • Instead of passing a hafas-client instance directly into hafas-monitor-trips, use hafas-client/throttle to prevent bursts of requests. You will have to experiment with the rate until you get a balance, between not sending too many requests, and being able to monitor all relevant trips.
  • Use e.g. hafas-client-rpc to run the requests from a pool of worker machines.

Contributing

If you have a question or need support using hafas-monitor-trips, please double-check your code and setup first. If you think you have found a bug or want to propose a feature, refer to the issues page.