In Dresden, Germany we should be glad about our public transport system, the DVB. Especially because it got the award as the best public transport system of Germany - multiple times.
And yes, I love it too and use it daily. However, not everything is perfect, and it never can be. And so it happens that the DVB provides an App that is almost perfect - but I wanted a specfic function decoupled from the app. Fun fact: the App is written (or at least was written) as a cross platform App in React using Ionic. I know this because at a programming meetup that I attended in 2017 Kilian reverse engineered the App - and also wrote an article about it.
I just wanted to create a Progressive Web App that reduces the amount of API-Calls to an minimum, while not requiring any user input. This is just so that you can see the departures of close stations immediately. Most people won’t need this, but I just wanted to have it.
The main points are the following:
- 1: The UI has to be as responsive as possible.
- 2: The App should need the least amount of interaction.
Next, I will try to address these in my concept.
I will try to create a DVB departure monitor, as a website, and as fast as possible.
The plan is to reduce the number of interactions down to zero. How? By using the location of a device (this can be done with a web API), the closest station can be determined. Using the DVB-API to find them based on the current location will introduce further delay, that can be eliminated by a local database.
So a user can just open the web page, and immediately see all departures of close stations, without clicking anything.
Getting All Stations
At this point, a local database could significantly speed up the lookup of close stations. However, this database is not available to the public, so I had to create it myself. By using two different APIs (described here) a link could be made between the GPS coordinates and the internal station IDs used to fetch the departures. This is not a new concept, and is used by other public transport Apps like FahrInfo.
Calculating the distance
To get the closest station, we need the distance to all of them. Usually, a server would do that, but in this case it is done locally. This is even better if you look at it from a privacy standpoint as no data is leaving your device.
The easiest, and only feasible way is here to calculate the distance directly (and not as a street route). However, (as I had to find out in a previous project) this can be quite hard. To my knowledge, the DVB uses (or used) a Gauss–Krüger coordinate system internally, with the center not in Dresden. But I might be wrong. Fortunately, there are the GPS Coordinates available.
First, we need the radius of the earth,
rk = 6373km is close enough.
Next, we will need to convert all latitudes and longitudes (which are in degree) to radians, which we will do by multiplying it by π and dividing by 180:
const rad = deg * Math.PI / 180;
Next, we will calculate the difference of these degrees.
We use the haversine equation to calculate distances on a sphere. It uses the central angle θ between two points:
θ = (distance of the two points)/(radius of the sphere)
In this case, we need the difference multiple times in the equation. All mentioned latitudes and longitudes are in radian already.
const dlat = lat2 - lat1; const dlon = lon2 - lon1;
The haversine function is defined by:
hav(θ) = hav (dlat) + cos(lat1)*cos(lat2) hav(dlon) = sin2(θ/2)
So to skip to the interesting part, we can solve for the distance between two points (d) by using archaversine, which is an inverse haversine.
d = r * archav(hav(θ)) = 2*r * arcsin(sqrt(hav(θ)))
const a = Math.pow(Math.sin(dlat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(dlon / 2), 2); const c = Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // great circle distance in rad const dk = 2*rk * c; // great circle distance in km
So there you have it! Local calculation of GPS-Coordinates on a small scale. This only works at a city-level, as the earth is an ellipsoid, and calculating on that is way harder I imagine.
To host all of this, I used Github Pages, as all of this is open source (see Github link) To use it, just open the website in Chrome on Android or Safari on iOS. You can even add it to your home screen and use it as a regular web app: