First prototype
diff --git a/inc/gtfs.php b/inc/gtfs.php
new file mode 100644
index 0000000..b4c49da
--- /dev/null
+++ b/inc/gtfs.php
@@ -0,0 +1,190 @@
+<?php
+class gtfs {
+ //private static $files = ["calendar_dates", "calendar", "routes", "stop_times", "stops", "trips"];
+
+ private $db;
+
+ private static $dow = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
+
+ function __construct($path = null) {
+ global $conf;
+
+ try {
+ $this->db = new PDO('sqlite:'.($conf["databaseFile"]));
+ } catch (PDOException $e) {
+ die("SQLite error: $e\n");
+ }
+ }
+
+ static function timeSinceMidnight() {
+ //return 8*60*60 + 9*60; //TESTING
+ return (time() - mktime(0, 0, 0));
+ }
+
+ static function today() {
+ return date("Ymd");
+ }
+
+ static function time2seconds($time) {
+ $timeSinceMidnight = self::timeSinceMidnight();
+
+ $boom = explode(":", $time);
+ if (count($boom) != 3) return null;
+
+ return ((($boom[0]*60) + $boom[1])*60 + $boom[2]) % (24*60*60);
+ }
+
+ private function fetchAll($sql) {
+ $query = $this->db->query($sql);
+ if (!$query) return false;
+
+ return $query->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ private function fetchAllPrepared($sql, $values) {
+ $query = $this->db->prepare($sql);
+ if (!$query) return false;
+
+ $query->execute($values);
+ return $query->fetchAll(PDO::FETCH_ASSOC);
+ }
+
+ private function fetchTable($table, $filters = [], $orderedField = null) {
+ $order = ($orderedField === null ? "" : " ORDER BY $orderedField");
+
+ if (!count($filters)) {
+ return $this->fetchAll("SELECT * FROM $table".$order);
+ }
+
+ $whereConditions = [];
+ $values = [];
+ foreach ($filters as $filter) {
+ $whereConditions[] = $filter[0]." = ?";
+ $values[] = $filter[1];
+ }
+
+ return $this->fetchAllPrepared("SELECT * FROM $table WHERE ".implode(" AND ", $whereConditions).$order, $values);
+ }
+
+ function getRoutes() {
+ return $this->fetchTable("routes");
+ }
+
+ function getTrips($route) {
+ return $this->fetchTable("trips", [["route_id", $route]]);
+ }
+
+ function getStations($ordered = false) {
+ return $this->fetchTable("stops", [["location_type", Gtfs\Stop\LocationType::STATION]], ($ordered ? "stop_name" : null));
+ }
+
+ function getStop($stop) {
+ $results = $this->fetchTable("stops", [["stop_id", $stop]]);
+
+ if (!count($results)) return null;
+
+ return $results[0];
+ }
+
+ function getPlatforms($stop) {
+ $results = $this->fetchTable("stops", [["parent_station", $stop], ["location_type", Gtfs\Stop\LocationType::STOP]], "stop_id");
+
+ return $results;
+ }
+
+ const TIME_LIMIT = 160;
+ function getStopTimes($stop, $timeLimit = self::TIME_LIMIT) {
+ $stops = $this->getPlatforms($stop);
+
+ $values = [];
+ foreach ($stops as $s) {
+ $values[] = $s["stop_id"]; // Stops
+ }
+
+ $rdow = (int)date("w");
+ $dow = self::$dow[$rdow]; // Today's day of week
+ $dow2 = self::$dow[($rdow + 1) % 7]; // Tomorrow's day of week
+
+ if (!count($stops)) return [];
+
+ $sql = "SELECT st.*, t.*, r.*, c.*, cd.*, strftime('%Y%m%d', 'now', 'localtime') as today, strftime('%Y%m%d', 'now', 'localtime', '1 day') as tomorrow, time('now', 'localtime') as now
+ FROM stop_times st
+ INNER JOIN trips t
+ ON st.trip_id = t.trip_id
+ INNER JOIN routes r
+ ON t.route_id = r.route_id
+ LEFT JOIN calendar c
+ ON t.service_id = c.service_id
+ LEFT JOIN calendar_dates cd
+ ON t.service_id = cd.service_id
+ WHERE
+ st.stop_id IN (".implode(", ", array_fill(0, count($stops), "?")).") AND
+ (
+ cd.service_id IS NULL OR
+ cd.exception_type = ".(int)Gtfs\CalendarDate\ExceptionType::ADDED."
+ ) AND
+ (
+ (
+ (
+ (
+ time(now) < time('00:00:00', '-".(int)$timeLimit." minutes') AND
+ st.departure_time BETWEEN time(now) AND time(now, '".(int)$timeLimit." minutes')
+ ) OR
+ (
+ time(now) >= time('00:00:00', '-".(int)$timeLimit." minutes') AND
+ st.departure_time BETWEEN time(now) AND strftime('24:%M:%S', now, '".(int)$timeLimit." minutes')
+ )
+ ) AND
+ (
+ c.service_id IS NULL OR
+ (
+ c.start_date <= today AND
+ c.end_date >= today AND
+ c.$dow = ".(int)Gtfs\Calendar\CalendarDay::AVAILABLE."
+ )
+ ) AND
+ (
+ cd.service_id IS NULL OR
+ cd.date = today
+ )
+ ) OR
+ (
+ (
+ (
+ time(now) < time('00:00:00', '-".(int)$timeLimit." minutes') AND
+ st.departure_time BETWEEN ((strftime('%H', now) + 24) || strftime(':%M:%S', now)) AND ((strftime('%H', now, '".(int)$timeLimit." minutes') + 24) || strftime(':%M:%S', now, '".(int)$timeLimit." minutes'))
+ ) OR
+ (
+ time(now) >= time('00:00:00', '-".(int)$timeLimit." minutes') AND
+ st.departure_time BETWEEN time(now) AND strftime('24:%M:%S', now, '".(int)$timeLimit." minutes')
+ )
+ ) AND
+ (
+ c.service_id IS NULL OR
+ (
+ c.start_date <= tomorrow AND
+ c.end_date >= tomorrow AND
+ c.$dow2 = ".(int)Gtfs\Calendar\CalendarDay::AVAILABLE."
+ )
+ ) AND
+ (
+ cd.service_id IS NULL OR
+ cd.date = tomorrow
+ )
+ )
+ )
+ ORDER BY departure_time ASC";
+
+ $result = $this->fetchAllPrepared($sql, $values);
+ if ($result === false) {
+ echo implode("\n", $this->db->errorInfo());
+ exit();
+ }
+
+ return $result;
+ }
+
+ function __destruct() {
+ $this->db = null;
+ }
+}