I ended up solving this using Tensorflow. For every app position recording, and every bus I created a timeline of displacement between bus and app position recording.
I got a CSV file looking like this:
displacement-30,displacement-20,displacement-10,displacement0,displacement10,displacement20,displacement30,timeDeviation-30,timeDeviation-20,timeDeviation-10,timeDeviation0,timeDeviation10,timeDeviation20,timeDeviation30,onBus
0.01,0.013,0.023,0.017,0.012,0.006,0.021,0.5,0.5,0.5,0.5,0.5,0.5,0.5,1
0.42,0.42,0.42,0.42,0.425,0.407,0.405,0.47,0.48,0.49,0.5,0.498,0.501,0.503,0
0.42,0.42,0.42,0.425,0.407,0.405,0.406,0.477,0.487,0.497,0.5,0.498,0.5,0.498,0
0.42,0.42,0.42,0.425,0.407,0.405,0.406,0.482,0.492,0.502,0.5,0.503,0.499,0.498,0
0.42,0.42,0.425,0.407,0.405,0.406,0.406,0.489,0.499,0.502,0.5,0.502,0.5,0.505,0
0.42,0.425,0.407,0.405,0.406,0.406,0.405,0.497,0.5,0.498,0.5,0.498,0.503,0.501,0
0.42,0.425,0.407,0.405,0.406,0.405,0.406,0.503,0.501,0.504,0.5,0.499,0.497,0.497,0
0.425,0.407,0.405,0.406,0.406,0.405,0.406,0.502,0.5,0.502,0.5,0.505,0.503,0.503,0
0.425,0.405,0.405,0.406,0.405,0.406,0.406,0.502,0.497,0.501,0.5,0.498,0.498,0.498,0
0.405,0.405,0.406,0.405,0.406,0.406,0.405,0.499,0.503,0.502,0.5,0.5,0.5,0.5,0
0.405,0.406,0.405,0.406,0.406,0.405,0.428,0.503,0.502,0.5,0.5,0.5,0.5,0.5,0
0.406,0.405,0.406,0.406,0.405,0.428,0.424,0.502,0.5,0.5,0.5,0.5,0.5,0.5,0
0.405,0.406,0.406,0.405,0.428,0.424,0.424,0.5,0.5,0.5,0.5,0.5,0.5,0.499,0
Each displacement is normalized as 0-1000 meters. The time deviation is used in case an incomplete timeline needs to be processed.
I will expand the features to include if bus was detected using Bluetooth beacons, the heading between bus and app position.