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 could be perfect - but has tons of little problems that hold it back. For example, it is 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. This might not seem problematic, but because of the specific implementation it is just extremely slow and unresponsive, even on the shiniest and newest Smartphones. The main reason for this is the decoupling of a native UI and the program logic. React Native is a mighty tool, but you have to use it carefully, otherwise no benefit over a website is given.
An unresponsive App should not be a deal breaker, and it is not. However, another issue amplifies it: Too many clicks. As React Native uses hooks to call functions. If the App is slow, every time the user interacts with a UI element, it adds to the waiting time. The DVB-APP takes this to an extreme. If you just want to know what the next departures are at the station closest to you, you have to open the App -> click on departures -> click location. If you are in a hurry, this can be stressful, as it fetches APIs multiple times during this. Worse, if you want to search for a specific station, the App makes an API call every time you type in a letter. If this API is overloaded by too many people using it, then you cannot search for departures, even if you typed in the station name correctly. You HAVE to select a station from the search results. This is bad design and bad load balancing.
The last - and maybe most frustrating - problem with the DVB App is the inconsistent UI. At my university, one of my professors told us the basics of good UI. The first and major thing was this: “Never change the meaning of a button”. This stuck with me, as it is extremely important. If you use the DVB-App (and you can do that in your Browser too, just visit m.dvb.de) the meaning of all buttons in the lower icon bar change all the time. The back-button sometimes vanishes or changes to your notepad. The search-icon sometimes changes to ‘tickets’. And there is a dedicated button to tell you that you can sync your preferences in their cloud. That is cool but why are all these buttons always available if their meaning change? This breaks my heart, as the DVB-App has the potential to be absolutely amazing. So next I will tell you what I learned from this.
The main points are the following:
- 1: The UI has to be as responsive as possible.
- 2: The UI should need the least amount of interaction.
- 3: Buttons should always have the same meaning.
- 4: The UI should be as clean as possible.
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. Who knows why. 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: