Thursday, August 2, 2018

Old Phone/Tablet as an Info Board Part 5: PHP capture-and-process

Finally, I describe one more method of putting information on the info board, suitable when there is a very limited amount of information you need from the target page, and when your output will have very little, if anything, to do with the target page layout. It is also the only possible method when the server outputs data in any format other than HTML.

The example is our local bus company, where I would like to get the bus departure info for a certain stop. In the browser, the result looks like this:



Now the biggest difficulty about using the previously described methods is the lack of a clearly defined URL associated with the results page - the results are dynamically generated by the parent page's javascript. After some sniffing around, it was possible to extract a working request link in the format
http://www4.mississauga.ca/PlanATrip/NextPassingTimes/RequestNextPassingTimes ?suggestionInputIdentifier=(stop_number) &suggestionInputType=Stop &stopInputIdentifier= &mustBeAccessible=false

which brings up the result in this JSON-like format:



Well, because the departure information I am after is somewhere in this JSON, I decided to parse it entirely in PHP using preg_match_all, extracting route number and arrival time for the next upcoming bus, as well as three more after it, like so:

<?php
date_default_timezone_set('EST');
$opts = array('http'=>array('header' => "User-Agent:MyAgent/1.0\r\n"));
$context = stream_context_create($opts);
$url = "http://www4.mississauga.ca/PlanATrip/NextPassingTimes/RequestNextPassingTimes?suggestionInputIdentifier=1127&suggestionInputType=Stop&stopInputIdentifier=&mustBeAccessible=false";
$timestamp = date("H:i");
$code = file_get_contents($url,false,$context); 
 
$code = strstr($code, 'NextPassingTimesList',false);
$code = strstr($code, 'NextPassingTimesLegend',true);
// now code contains only trip data
$rid = preg_match_all("/data-route-key=([^=]+)data-trip-key/",$code,$matches1);
$tid = preg_match_all("/NextPassingTimesTime ([^N]+)u003c\/div/",$code,$matches2);
for($i=0;$i<$rid;$i++)
{
$route = str_replace("\\","",$matches1[1][$i]); $route = str_replace("\"","",$route); $route = str_replace("r","",$route);$route = str_replace("n","",$route);
$routes[$i] = str_replace("~~East"," E",$route);
 
$time = $matches2[1][$i];
$id1=stripos($time,"u003e"); 
$time = substr($time,$id1+5,strlen($time)-($id1+5)-1);
$sid = stripos($time,"u003c");
if ($sid!==false) {$time = substr($time, 0, $sid-1);}
$times[$i] = $time;
}
$color = (strpos($times[0],"min")===false)?"rgb(255,128,128)":"red";
 
echo '<html><head></head><body>';
echo '<div style="color:rgb(255,128,128); font-family: Arial, Helvetica, sans-serif; font-size: 20px; position: relative; top: -5px;">';
echo '<span style="color:gray;">', $timestamp, '&nbsp;</span>';
echo '<span id="bustoken" style="background: ',$color,'; color:white;">', " ".$routes[0], '</span>',  '<span style="font-weight: bold; color:',$color,'">', "&nbsp;", $times[0], '</span>';
if ($rid > 1 && $tid > 1)
{
    echo "<br/>";
    echo '<span style="color:rgb(255,235,235);">', $timestamp, '&nbsp;</span>';
    for ($j=1; $j<(($rid>4)?4:$rid); $j++)
    {
        echo '<span style="background: rgb(255,128,128); color:white; font-size: 14px;">', " ".$routes[$j], '</span>',  '<span style="font-weight: bold; color: rgb(255,128,128);font-size: 14px;">', "&nbsp;", $times[$j], '</span> &nbsp;';
 
    };
}
echo '</div></body></html>';
?> 


Putting it into our standard PHP iframe like this:
<div id="bus" class="basic info" style="position: absolute; left: 150px; bottom: 270px; width: 415px; height: 50px; background:rgb(255,235,235);">
<iframe id="miway" scrolling="no" src="bus.php" style="height: 100px; width: 415px; border: none;"> </iframe>
</div>


The result looks like:


Note the following tricks:
  • Whenever the real-time information is available ("the bus is leaving in XX minutes"), I am accenting the color to highlight it.
  • I am also outputting the time of the query, so that the "XX minutes" format remains meaningful without having to query every single minute.
  • The line with date_default_timezone_set serves to ensure correct DST for $timestamp.
  • As a future development, I plan to supplement the "XX minutes" format with actual projected time, replacing it with "HH:MM (XX min)" and retaining accentuation.
  • As another exercise, I plan to change color accentuation depending on the remaining time - highlighting buses that are still reachable given the walking/running distance to the stop, and dimming the buses that aren't.

I admit that this method, as implemented, is quick-and-dirty and extremely brute-force, and is rather vulnerable to the underlying data format changes. A much more robust way would be to have the PHP parse the JSON "the proper way" and either process it using dedicated JSON functions, or simply echo the HTML result in its entirety, using jQuery to interact with it. However, my approach has worked surprisingly well for over 6 months already - and "if it ain't broken, won't fix it".

No comments:

Post a Comment