From: maxious Date: Sat, 30 Oct 2010 02:53:52 +0000 Subject: Add between stops to feed YAML X-Git-Url: http://maxious.lambdacomplex.org/git/?p=bus.git&a=commitdiff&h=cc68a6b77df2a123cb328a842e0bdf5704ff0966 --- Add between stops to feed YAML --- --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -maxious-canberra-transit-feed/output/ +maxious-canberra-transit-feed/source-html +origin-src --- /dev/null +++ b/betweenpoint.load.php @@ -1,1 +1,76 @@ + nodes of the first movie. + * Output the rating scale, too. */ +foreach ($xml->node as $node) { + $geoPo = geopoEncode((float)$node['lat'],(float)$node['lon']); + $node['lat'] = (int) ((float)$node['lat']*10000000); + $node['lon'] = (int) ((float)$node['lon']*10000000); + echo($node['lat'].",".$node['lon']."=$geoPo
"); + $sql = "INSERT INTO stops (geohash, lat, lng) VALUES('$geoPo','{$node['lat']}','{$node['lon']}')"; + $result = pg_query($conn, $sql); + if (!$result) { + echo("Error in SQL query: " . pg_last_error() ."
\n"); + } +flush(); + +} +?> --- a/betweenpoint.php +++ b/betweenpoint.php @@ -7,71 +7,65 @@ function init() { - // create the ol map object - var map = new OpenLayers.Map('map'); + // create the ol map object + var map = new OpenLayers.Map('map'); - var osmtiles = new OpenLayers.Layer.OSM("local", "http://127.0.0.1/Maps/OSM/${z}/${x}/${y}.png") + var osmtiles = new OpenLayers.Layer.OSM("local", "http://127.0.0.1/Maps/OSM/${z}/${x}/${y}.png") // use http://open.atlas.free.fr/GMapsTransparenciesImgOver.php and http://code.google.com/p/googletilecutter/ to make tiles - markers = new OpenLayers.Layer.Markers("Between Stop Markers"); + markers = new OpenLayers.Layer.Markers("Between Stop Markers"); var timeicon = new OpenLayers.Icon("http://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png",new OpenLayers.Size(32,32)); var timepoints = new OpenLayers.Layer.GeoRSS("Timing Points", "displaytimepoints.georss.php", { icon: timeicon }); map.addLayers([osmtiles, markers,timepoints]); map.addControl(new OpenLayers.Control.LayerSwitcher()); - map.zoomToExtent(markers.getDataExtent()); + map.zoomToExtent(markers.getDataExtent()); } + + + + + + +
+ +
+

'.$pageTitle.'

+
+
'; +} + +function include_footer() +{ + echo '
'; +} function service_period() { @@ -13,5 +51,35 @@ return 'weekday'; } } + +function midnight_seconds() +{ +// from http://www.perturb.org/display/Perlfunc__Seconds_Since_Midnight.html + $secs = (date("G") * 3600) + (date("i") * 60) + date("s"); + return $secs; +} + +function midnight_seconds_to_time($seconds) +{ + $midnight = mktime (0, 0, 0, date("n"), date("j"), date("Y")); + return date("h:ia",$midnight+$seconds); +} +function getPage($url) +{ + $ch = curl_init($url); +curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); +curl_setopt( $ch, CURLOPT_HEADER, 0 ); +$page = curl_exec($ch); +curl_close($ch); +return $page; +} +function array_flatten($a,$f=array()){ + if(!$a||!is_array($a))return ''; + foreach($a as $k=>$v){ + if(is_array($v))$f=array_flatten($v,$f); + else $f[$k]=$v; + } + return $f; +} ?> - + --- a/busui/index.php +++ b/busui/index.php @@ -1,32 +1,20 @@ + - jQTouch β - - - - - + bus.lambdacomplex.org + + + + - --> -
-
-

jQTouch

- About -
-
    -
  • User Interface 4
  • -
  • All stops List
  • -
  • Nearby List
  • -
  • Favourites List
  • +
    +
    +

    bus.lambdacomplex.org

    +
    +
    + +
    -

    Add this page to your home screen to view the custom icon, startup screen, and full screen mode.

    +

    + Time:
    + Service Period:
    - +
    + --- a/busui/readme.txt +++ b/busui/readme.txt @@ -1,2 +1,23 @@ +# input location (via GPS or favourites or search) and destination (via searchable list, optional) +# http://10.0.1.153:8765/json/boundboxstops?n=-35.27568499917103&e=149.1346514225006&s=-35.279495003493516 +&w=149.12622928619385&limit=50 +# http://10.0.1.153:8765/json/stoptrips?stop=43&time=64440 # recursively call to show all services nearby, sort by distance, need to filter by service period +# Hey, can pick destination again from a list filtered to places these stops go if you're curious! +# http://10.0.1.153:8765/json/tripstoptimes?trip=2139 # Can recursively call and parse based on intended destination to show ETA +# http://10.0.1.153:8765/json/triprows?trip=2139 # For pretty maps + +have to do +/usr/sbin/setsebool -P httpd_can_network_connect=1 +on fedora + +might need http://forum.jquery.com/topic/google-maps-inside-jquery-mobile + +some extras +/json/routes = all routes +/json/neareststops?lat/lng/number TODO +Destinations +Favourites +OOP stops/routes +Stop sorting/search-filter --- a/busui/routeList.php +++ b/busui/routeList.php @@ -1,1 +1,34 @@ +'; +echo "";*/ +echo '
      '; +$url = $APIurl."/json/routes"; +$contents = json_decode(getPage($url)); +foreach ($contents as $key => $row) { + $routeDestinations[$row[2]][] = $row; +} +foreach ($routeDestinations as $destination => $routes) +{ + echo '
    • '.$destination."...
        \n"; + foreach($routes as $row) { + echo '
      • '.$row[1].' '.$row[2]."
      • \n"; + } + echo "
    • \n"; +} +echo "
    \n"; +/* +
    +
    + +
    +*/ +include_footer(); +?> + --- /dev/null +++ b/busui/schedule_viewer.py @@ -1,1 +1,536 @@ - +#!/usr/bin/python2.5 + +# Copyright (C) 2007 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +An example application that uses the transitfeed module. + +You must provide a Google Maps API key. +""" + + +import BaseHTTPServer, sys, urlparse +import bisect +from gtfsscheduleviewer.marey_graph import MareyGraph +import gtfsscheduleviewer +import mimetypes +import os.path +import re +import signal +import simplejson +import socket +import time +import transitfeed +from transitfeed import util +import urllib + + +# By default Windows kills Python with Ctrl+Break. Instead make Ctrl+Break +# raise a KeyboardInterrupt. +if hasattr(signal, 'SIGBREAK'): + signal.signal(signal.SIGBREAK, signal.default_int_handler) + + +mimetypes.add_type('text/plain', '.vbs') + + +class ResultEncoder(simplejson.JSONEncoder): + def default(self, obj): + try: + iterable = iter(obj) + except TypeError: + pass + else: + return list(iterable) + return simplejson.JSONEncoder.default(self, obj) + +# Code taken from +# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/425210/index_txt +# An alternate approach is shown at +# http://mail.python.org/pipermail/python-list/2003-July/212751.html +# but it requires multiple threads. A sqlite object can only be used from one +# thread. +class StoppableHTTPServer(BaseHTTPServer.HTTPServer): + def server_bind(self): + BaseHTTPServer.HTTPServer.server_bind(self) + self.socket.settimeout(1) + self._run = True + + def get_request(self): + while self._run: + try: + sock, addr = self.socket.accept() + sock.settimeout(None) + return (sock, addr) + except socket.timeout: + pass + + def stop(self): + self._run = False + + def serve(self): + while self._run: + self.handle_request() + + +def StopToTuple(stop): + """Return tuple as expected by javascript function addStopMarkerFromList""" + return (stop.stop_id, stop.stop_name, float(stop.stop_lat), + float(stop.stop_lon), stop.location_type) + + +class ScheduleRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(self): + scheme, host, path, x, params, fragment = urlparse.urlparse(self.path) + parsed_params = {} + for k in params.split('&'): + k = urllib.unquote(k) + if '=' in k: + k, v = k.split('=', 1) + parsed_params[k] = unicode(v, 'utf8') + else: + parsed_params[k] = '' + + if path == '/': + return self.handle_GET_home() + + m = re.match(r'/json/([a-z]{1,64})', path) + if m: + handler_name = 'handle_json_GET_%s' % m.group(1) + handler = getattr(self, handler_name, None) + if callable(handler): + return self.handle_json_wrapper_GET(handler, parsed_params) + + # Restrict allowable file names to prevent relative path attacks etc + m = re.match(r'/file/([a-z0-9_-]{1,64}\.?[a-z0-9_-]{1,64})$', path) + if m and m.group(1): + try: + f, mime_type = self.OpenFile(m.group(1)) + return self.handle_static_file_GET(f, mime_type) + except IOError, e: + print "Error: unable to open %s" % m.group(1) + # Ignore and treat as 404 + + m = re.match(r'/([a-z]{1,64})', path) + if m: + handler_name = 'handle_GET_%s' % m.group(1) + handler = getattr(self, handler_name, None) + if callable(handler): + return handler(parsed_params) + + return self.handle_GET_default(parsed_params, path) + + def OpenFile(self, filename): + """Try to open filename in the static files directory of this server. + Return a tuple (file object, string mime_type) or raise an exception.""" + (mime_type, encoding) = mimetypes.guess_type(filename) + assert mime_type + # A crude guess of when we should use binary mode. Without it non-unix + # platforms may corrupt binary files. + if mime_type.startswith('text/'): + mode = 'r' + else: + mode = 'rb' + return open(os.path.join(self.server.file_dir, filename), mode), mime_type + + def handle_GET_default(self, parsed_params, path): + self.send_error(404) + + def handle_static_file_GET(self, fh, mime_type): + content = fh.read() + self.send_response(200) + self.send_header('Content-Type', mime_type) + self.send_header('Content-Length', str(len(content))) + self.end_headers() + self.wfile.write(content) + + def AllowEditMode(self): + return False + + def handle_GET_home(self): + schedule = self.server.schedule + (min_lat, min_lon, max_lat, max_lon) = schedule.GetStopBoundingBox() + forbid_editing = ('true', 'false')[self.AllowEditMode()] + + agency = ', '.join(a.agency_name for a in schedule.GetAgencyList()).encode('utf-8') + + key = self.server.key + host = self.server.host + + # A very simple template system. For a fixed set of values replace [xxx] + # with the value of local variable xxx + f, _ = self.OpenFile('index.html') + content = f.read() + for v in ('agency', 'min_lat', 'min_lon', 'max_lat', 'max_lon', 'key', + 'host', 'forbid_editing'): + content = content.replace('[%s]' % v, str(locals()[v])) + + self.send_response(200) + self.send_header('Content-Type', 'text/html') + self.send_header('Content-Length', str(len(content))) + self.end_headers() + self.wfile.write(content) + + def handle_json_GET_routepatterns(self, params): + """Given a route_id generate a list of patterns of the route. For each + pattern include some basic information and a few sample trips.""" + schedule = self.server.schedule + route = schedule.GetRoute(params.get('route', None)) + if not route: + self.send_error(404) + return + time = int(params.get('time', 0)) + sample_size = 10 # For each pattern return the start time for this many trips + + pattern_id_trip_dict = route.GetPatternIdTripDict() + patterns = [] + + for pattern_id, trips in pattern_id_trip_dict.items(): + time_stops = trips[0].GetTimeStops() + if not time_stops: + continue + has_non_zero_trip_type = False; + for trip in trips: + if trip['trip_type'] and trip['trip_type'] != '0': + has_non_zero_trip_type = True + name = u'%s to %s, %d stops' % (time_stops[0][2].stop_name, time_stops[-1][2].stop_name, len(time_stops)) + transitfeed.SortListOfTripByTime(trips) + + num_trips = len(trips) + if num_trips <= sample_size: + start_sample_index = 0 + num_after_sample = 0 + else: + # Will return sample_size trips that start after the 'time' param. + + # Linear search because I couldn't find a built-in way to do a binary + # search with a custom key. + start_sample_index = len(trips) + for i, trip in enumerate(trips): + if trip.GetStartTime() >= time: + start_sample_index = i + break + + num_after_sample = num_trips - (start_sample_index + sample_size) + if num_after_sample < 0: + # Less than sample_size trips start after 'time' so return all the + # last sample_size trips. + num_after_sample = 0 + start_sample_index = num_trips - sample_size + + sample = [] + for t in trips[start_sample_index:start_sample_index + sample_size]: + sample.append( (t.GetStartTime(), t.trip_id) ) + + patterns.append((name, pattern_id, start_sample_index, sample, + num_after_sample, (0,1)[has_non_zero_trip_type])) + + patterns.sort() + return patterns + + def handle_json_wrapper_GET(self, handler, parsed_params): + """Call handler and output the return value in JSON.""" + schedule = self.server.schedule + result = handler(parsed_params) + content = ResultEncoder().encode(result) + self.send_response(200) + self.send_header('Content-Type', 'text/plain') + self.send_header('Content-Length', str(len(content))) + self.end_headers() + self.wfile.write(content) + + def handle_json_GET_routes(self, params): + """Return a list of all routes.""" + schedule = self.server.schedule + result = [] + for r in schedule.GetRouteList(): + result.append( (r.route_id, r.route_short_name, r.route_long_name) ) + result.sort(key = lambda x: x[1:3]) + return result + + def handle_json_GET_routerow(self, params): + schedule = self.server.schedule + route = schedule.GetRoute(params.get('route', None)) + return [transitfeed.Route._FIELD_NAMES, route.GetFieldValuesTuple()] + + def handle_json_GET_triprows(self, params): + """Return a list of rows from the feed file that are related to this + trip.""" + schedule = self.server.schedule + try: + trip = schedule.GetTrip(params.get('trip', None)) + except KeyError: + # if a non-existent trip is searched for, the return nothing + return + route = schedule.GetRoute(trip.route_id) + trip_row = dict(trip.iteritems()) + route_row = dict(route.iteritems()) + return [['trips.txt', trip_row], ['routes.txt', route_row]] + + def handle_json_GET_tripstoptimes(self, params): + schedule = self.server.schedule + try: + trip = schedule.GetTrip(params.get('trip')) + except KeyError: + # if a non-existent trip is searched for, the return nothing + return + time_stops = trip.GetTimeStops() + stops = [] + times = [] + for arr,dep,stop in time_stops: + stops.append(StopToTuple(stop)) + times.append(arr) + return [stops, times] + + def handle_json_GET_tripshape(self, params): + schedule = self.server.schedule + try: + trip = schedule.GetTrip(params.get('trip')) + except KeyError: + # if a non-existent trip is searched for, the return nothing + return + points = [] + if trip.shape_id: + shape = schedule.GetShape(trip.shape_id) + for (lat, lon, dist) in shape.points: + points.append((lat, lon)) + else: + time_stops = trip.GetTimeStops() + for arr,dep,stop in time_stops: + points.append((stop.stop_lat, stop.stop_lon)) + return points + + def handle_json_GET_neareststops(self, params): + """Return a list of the nearest 'limit' stops to 'lat', 'lon'""" + schedule = self.server.schedule + lat = float(params.get('lat')) + lon = float(params.get('lon')) + limit = int(params.get('limit')) + stops = schedule.GetNearestStops(lat=lat, lon=lon, n=limit) + return [StopToTuple(s) for s in stops] + + def handle_json_GET_boundboxstops(self, params): + """Return a list of up to 'limit' stops within bounding box with 'n','e' + and 's','w' in the NE and SW corners. Does not handle boxes crossing + longitude line 180.""" + schedule = self.server.schedule + n = float(params.get('n')) + e = float(params.get('e')) + s = float(params.get('s')) + w = float(params.get('w')) + limit = int(params.get('limit')) + stops = schedule.GetStopsInBoundingBox(north=n, east=e, south=s, west=w, n=limit) + return [StopToTuple(s) for s in stops] + + def handle_json_GET_stops(self, params): + schedule = self.server.schedule + return [StopToTuple(s) for s in schedule.GetStopList()] + + def handle_json_GET_stopsearch(self, params): + schedule = self.server.schedule + query = params.get('q', None).lower() + matches = [] + for s in schedule.GetStopList(): + if s.stop_id.lower().find(query) != -1 or s.stop_name.lower().find(query) != -1: + matches.append(StopToTuple(s)) + return matches + + def handle_json_GET_stop(self, params): + schedule = self.server.schedule + query = params.get('stop_id', None).lower() + for s in schedule.GetStopList(): + if s.stop_id.lower() == query: + return StopToTuple(s) + return [] + + def handle_json_GET_stoptrips(self, params): + """Given a stop_id and time in seconds since midnight return the next + trips to visit the stop.""" + schedule = self.server.schedule + stop = schedule.GetStop(params.get('stop', None)) + time = int(params.get('time', 0)) + time_trips = stop.GetStopTimeTrips(schedule) + time_trips.sort() # OPT: use bisect.insort to make this O(N*ln(N)) -> O(N) + # Keep the first 15 after param 'time'. + # Need make a tuple to find correct bisect point + time_trips = time_trips[bisect.bisect_left(time_trips, (time, 0)):] + time_trips = time_trips[:15] + # TODO: combine times for a route to show next 2 departure times + result = [] + for time, (trip, index), tp in time_trips: + headsign = None + # Find the most recent headsign from the StopTime objects + for stoptime in trip.GetStopTimes()[index::-1]: + if stoptime.stop_headsign: + headsign = stoptime.stop_headsign + break + # If stop_headsign isn't found, look for a trip_headsign + if not headsign: + headsign = trip.trip_headsign + route = schedule.GetRoute(trip.route_id) + trip_name = '' + if route.route_short_name: + trip_name += route.route_short_name + if route.route_long_name: + if len(trip_name): + trip_name += " - " + trip_name += route.route_long_name + if headsign: + trip_name += " (Direction: %s)" % headsign + + result.append((time, (trip.trip_id, trip_name, trip.service_id), tp)) + return result + + def handle_GET_ttablegraph(self,params): + """Draw a Marey graph in SVG for a pattern (collection of trips in a route + that visit the same sequence of stops).""" + schedule = self.server.schedule + marey = MareyGraph() + trip = schedule.GetTrip(params.get('trip', None)) + route = schedule.GetRoute(trip.route_id) + height = int(params.get('height', 300)) + + if not route: + print 'no such route' + self.send_error(404) + return + + pattern_id_trip_dict = route.GetPatternIdTripDict() + pattern_id = trip.pattern_id + if pattern_id not in pattern_id_trip_dict: + print 'no pattern %s found in %s' % (pattern_id, pattern_id_trip_dict.keys()) + self.send_error(404) + return + triplist = pattern_id_trip_dict[pattern_id] + + pattern_start_time = min((t.GetStartTime() for t in triplist)) + pattern_end_time = max((t.GetEndTime() for t in triplist)) + + marey.SetSpan(pattern_start_time,pattern_end_time) + marey.Draw(triplist[0].GetPattern(), triplist, height) + + content = marey.Draw() + + self.send_response(200) + self.send_header('Content-Type', 'image/svg+xml') + self.send_header('Content-Length', str(len(content))) + self.end_headers() + self.wfile.write(content) + + +def FindPy2ExeBase(): + """If this is running in py2exe return the install directory else return + None""" + # py2exe puts gtfsscheduleviewer in library.zip. For py2exe setup.py is + # configured to put the data next to library.zip. + windows_ending = gtfsscheduleviewer.__file__.find('\\library.zip\\') + if windows_ending != -1: + return transitfeed.__file__[:windows_ending] + else: + return None + + +def FindDefaultFileDir(): + """Return the path of the directory containing the static files. By default + the directory is called 'files'. The location depends on where setup.py put + it.""" + base = FindPy2ExeBase() + if base: + return os.path.join(base, 'schedule_viewer_files') + else: + # For all other distributions 'files' is in the gtfsscheduleviewer + # directory. + base = os.path.dirname(gtfsscheduleviewer.__file__) # Strip __init__.py + return os.path.join(base, 'files') + + +def GetDefaultKeyFilePath(): + """In py2exe return absolute path of file in the base directory and in all + other distributions return relative path 'key.txt'""" + windows_base = FindPy2ExeBase() + if windows_base: + return os.path.join(windows_base, 'key.txt') + else: + return 'key.txt' + + +def main(RequestHandlerClass = ScheduleRequestHandler): + usage = \ +'''%prog [options] [] + +Runs a webserver that lets you explore a in your browser. + +If is omited the filename is read from the console. Dragging +a file into the console may enter the filename. +''' + parser = util.OptionParserLongError( + usage=usage, version='%prog '+transitfeed.__version__) + parser.add_option('--feed_filename', '--feed', dest='feed_filename', + help='file name of feed to load') + parser.add_option('--key', dest='key', + help='Google Maps API key or the name ' + 'of a text file that contains an API key') + parser.add_option('--host', dest='host', help='Host name of Google Maps') + parser.add_option('--port', dest='port', type='int', + help='port on which to listen') + parser.add_option('--file_dir', dest='file_dir', + help='directory containing static files') + parser.add_option('-n', '--noprompt', action='store_false', + dest='manual_entry', + help='disable interactive prompts') + parser.set_defaults(port=8765, + host='maps.google.com', + file_dir=FindDefaultFileDir(), + manual_entry=True) + (options, args) = parser.parse_args() + + if not os.path.isfile(os.path.join(options.file_dir, 'index.html')): + print "Can't find index.html with --file_dir=%s" % options.file_dir + exit(1) + + if not options.feed_filename and len(args) == 1: + options.feed_filename = args[0] + + if not options.feed_filename and options.manual_entry: + options.feed_filename = raw_input('Enter Feed Location: ').strip('"') + + default_key_file = GetDefaultKeyFilePath() + if not options.key and os.path.isfile(default_key_file): + options.key = open(default_key_file).read().strip() + + if options.key and os.path.isfile(options.key): + options.key = open(options.key).read().strip() + + schedule = transitfeed.Schedule(problem_reporter=transitfeed.ProblemReporter()) + print 'Loading data from feed "%s"...' % options.feed_filename + print '(this may take a few minutes for larger cities)' + schedule.Load(options.feed_filename) + + server = StoppableHTTPServer(server_address=('', options.port), + RequestHandlerClass=RequestHandlerClass) + server.key = options.key + server.schedule = schedule + server.file_dir = options.file_dir + server.host = options.host + server.feed_path = options.feed_filename + + print ("To view, point your browser at http://localhost:%d/" % + (server.server_port)) + server.serve_forever() + + +if __name__ == '__main__': + main() + --- a/busui/stop.php +++ b/busui/stop.php @@ -1,1 +1,17 @@ +'; +$url = $APIurl."/json/stoptrips?stop=".$_REQUEST['stopid']."&time=".midnight_seconds(); +$page = getPage($url); +foreach (json_decode($page) as $row) +{ +if ($row[1][2] === service_period()) echo '
  • '.midnight_seconds_to_time($row[0]).' '.$row[1][1].'
  • '; +} +echo '
'; +include_footer(); +?> + --- a/busui/stopList.php +++ b/busui/stopList.php @@ -1,23 +1,35 @@ -
-
-

GET Example

- AJAX -
-
- This page was loaded via AJAX. -
- -
-
-
-

Events test

- AJAX - Home -
-
- This is a test of live events. -
-
+'; +$url = $APIurl."/json/stops"; +if ($_REQUEST['lat'] && $_REQUEST['lon']) $url = $APIurl."/json/neareststops?lat={$_REQUEST['lat']}&lon={$_REQUEST['lon']}&limit=15"; +$contents = json_decode(getPage($url)); +foreach ($contents as $key => $row) { + $stopName[$key] = $row[1]; +} +// Sort the data with volume descending, edition ascending +// Add $data as the last parameter, to sort by the common key +array_multisort($stopName, SORT_ASC, $contents); + +foreach ($contents as $row) +{ + + echo '
  • '.$row[1].'
  • '; + } +echo ''; +/* +
    +
    + +
    +*/ +include_footer(); +?> + + --- /dev/null +++ b/busui/trip.php @@ -1,1 +1,25 @@ +route_short_name . ' '. $trips[1]->route_long_name); +echo '
      '; + + + + +$url = $APIurl."/json/tripstoptimes?trip=".$_REQUEST['tripid']; + +$json = json_decode(getPage($url)); +$stops = $json[0]; +$times = $json[1]; +foreach ($stops as $key => $row) +{ +echo '
    • '.midnight_seconds_to_time($times[$key]).' '.$row[1].'
    • '; +} +echo '
    '; +include_footer(); +?> + --- /dev/null +++ b/busui/view.sh @@ -1,1 +1,8 @@ +# input location (via GPS or favourites or search) and destination (via searchable list, optional) +# http://10.0.1.153:8765/json/boundboxstops?n=-35.27568499917103&e=149.1346514225006&s=-35.279495003493516&w=149.12622928619385&limit=50 +# http://10.0.1.153:8765/json/stoptrips?stop=43&time=64440 # recursively call to show all services nearby, sort by distance, need to filter by service period +# Hey, can pick destination again from a list filtered to places these stops go if you're curious! +# http://10.0.1.153:8765/json/tripstoptimes?trip=2139 # Can recursively call and parse based on intended destination to show ETA +# http://10.0.1.153:8765/json/triprows?trip=2139 # For pretty maps +python schedule_viewer.py --feed=../maxious-canberra-transit-feed/cbrfeed.zip --key=ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q --- a/maxious-canberra-transit-feed/02-tidytimepoints.rb +++ b/maxious-canberra-transit-feed/02-tidytimepoints.rb @@ -38,7 +38,7 @@ "Lathlain St Station - Platform 5" => "Lathlain St Bus Station - Platform 5", "Lathlain St Station - Platform 6" => "Lathlain St Bus Station - Platform 6", "Flemington Rd, Sandford St" => "Flemington Rd/Sandford St", - "Erindale Centre / - Sternberg Crescent" => "Erindale Drive/Sternberg", + "Erindale Centre /Sternberg Crescent" => "Erindale Drive/Sternberg", "Canberra Hospita" => "Canberra Hospital", "Cohen Str Station - Platform 1" => "Cohen St Bus Station - Platform 1", "Cohen Street Station" => "Cohen St Bus Station", --- a/maxious-canberra-transit-feed/04-generateymlinclude.rb +++ b/maxious-canberra-transit-feed/04-generateymlinclude.rb @@ -1,10 +1,10 @@ #!/usr/bin/ruby -require 'postgres' require 'highline.rb' include HighLine require 'rubygems' +require 'postgres' require 'json' require 'yaml' require 'pp' @@ -19,7 +19,7 @@ f2.puts header f2.puts "stops:\n"; begin - time_points = connbus.exec("SELECT * from timing_point") + time_points = connbus.exec("SELECT * from timing_point ORDER BY name") rescue PGError => e puts "Error reading from DB #{e}" #conn.close() if conn @@ -30,7 +30,22 @@ # 1 = lat*100000 # 2 = lng*100000 + #pp time_point[0] f2.puts " - { name: #{time_point[0]},stop_code: #{time_point[0]}, lat: #{Float(time_point[1])/10000000}, lng: #{Float(time_point[2])/10000000}}" + end + begin + stops = connbus.exec("SELECT * from stops") + rescue PGError => e + puts "Error reading from DB #{e}" + #conn.close() if conn + end + stops.each do |stop| + #pp stop + # 0 = geoPo + # 1 = lat*100000 + # 2 = lng*100000 + #pp time_point[0] + f2.puts " - { name: #{stop[0]},stop_code: #{stop[0]}, lat: #{Float(stop[1])/10000000}, lng: #{Float(stop[2])/10000000}}" end f2.puts "routes:\n"; end --- /dev/null +++ b/maxious-canberra-transit-feed/05-addbetweenpointstotimetables.rb @@ -1,1 +1,50 @@ +require 'rubygems' +require 'postgres' +require 'pp' +require 'yaml' +class String + def escape_single_quotes + self.gsub(/'/, "''") + end +end +class Array + def to_yaml_style + :inline + end +end +Dir.chdir("output") +connbus = PGconn.connect("localhost", 5432, '', '', "bus", "postgres", "snmc") + +Dir.glob("*.yml") { |file| + timetable = YAML::load_file(file) + if timetable + route_name = timetable["short_name"] + timetable["between_stops"] = {} + for i in 0..timetable["time_points"].length-2 + begin + searchFrom = timetable["time_points"][i].escape_single_quotes.split("-")[0].strip + searchTo = timetable["time_points"][i+1].escape_single_quotes.split("-")[0].strip + + between_points = connbus.exec("SELECT * from between_stops + WHERE fromlocation = '#{searchFrom}' + AND tolocation = '#{searchTo}' AND routes LIKE '%#{route_name};%'") + rescue PGError => e + puts "Error selecting matching between points from DB #{e}" + #conn.close() if conn + end + between_points.each do |between_point_row| + timetable["between_stops"][timetable["time_points"][i] + '-' +timetable["time_points"][i+1]] \ + = between_point_row['points'].split(";"); + end + end + #pp timetable["between_stops"] + File.open(file, "w") do |f| + f.write timetable.to_yaml + end + + else + print "error, #{file} empty\n" + end +} + --- a/maxious-canberra-transit-feed/05-generateymlinclude.rb +++ /dev/null @@ -1,53 +1,1 @@ -#!/usr/bin/ruby -require 'postgres' -require 'highline.rb' -include HighLine - -require 'rubygems' -require 'json' -require 'yaml' -require 'pp' -# make - { name: Civic Interchange Platform 1,stop_code: civic_platform_1, lat: -35.2794347, lng: 149.130588} -connbus = PGconn.connect("localhost", 5432, '', '', "bus", "postgres", "snmc") - -f = File.open('cbrtable.yml.in.in') -header = f.readlines -f.close - -File.open('cbrtable.yml.in', 'w') do |f2| - f2.puts header - f2.puts "stops:\n"; - begin - time_points = connbus.exec("SELECT * from timing_point ORDER BY name") - rescue PGError => e - puts "Error reading from DB #{e}" - #conn.close() if conn - end - time_points.each do |time_point| - #pp time_point - # 0 = name - - # 1 = lat*100000 - # 2 = lng*100000 - #pp time_point[0] - f2.puts " - { name: #{time_point[0]},stop_code: #{time_point[0]}, lat: #{Float(time_point[1])/10000000}, lng: #{Float(time_point[2])/10000000}}" - end - begin - stops = connbus.exec("SELECT * from stops") - rescue PGError => e - puts "Error reading from DB #{e}" - #conn.close() if conn - end - stops.each do |stop| - #pp stop - # 0 = geoPo - # 1 = lat*100000 - # 2 = lng*100000 - #pp time_point[0] - f2.puts " - { name: #{stop[0]},stop_code: #{stop[0]}, lat: #{Float(stop[1])/10000000}, lng: #{Float(stop[2])/10000000}}" - end - f2.puts "routes:\n"; -end - - --- a/maxious-canberra-transit-feed/900-intertown.yml +++ b/maxious-canberra-transit-feed/900-intertown.yml @@ -1,7 +1,9 @@ short_name: 900 long_name: Intertown time_points: [ civic_platform_6, 3042, 4531, 4929, civic_platform_1, civic_platform_5 ] -between_stops: [ ] +between_stops: [ +civic_platform_6-3042: [ADFA] +] stop_times: [ [ 632a, 642a, 657a, 708a, 715a, 727a], [ 702a, 712a, 727a, 738a, 745a, 757a], --- a/maxious-canberra-transit-feed/Makefile +++ b/maxious-canberra-transit-feed/Makefile @@ -1,19 +1,21 @@ default: cbrfeed.zip cbrfeed.zip: cbrtable.yml createfeed.py + sh 06-cleanupyaml.sh ./createfeed.py --input=cbrtable.yml --output=cbrfeed.zip cbrtable.yml: cbrtable.yml.in indent-route.pl cp cbrtable.yml.in cbrtable.yml @$(foreach ROUTE_FILE, $(wildcard output/*), \ echo "Parsing $(ROUTE_FILE)"; \ - echo "TODO: replace friendly timing spot names with OSM node IDs or geohash in $(ROUTE_FILE)"; \ - echo "TODO: add inbetween stops in $(ROUTE_FILE)"; \ - ./indent-route.pl < $(ROUTE_FILE) >> cbrtable.yml;) + ./indent-route.pl < "$(ROUTE_FILE)" >> cbrtable.yml;) cbrtable.yml.in: cbrtable.yml.in.in - ruby 04-generateymlinclude.rb + ruby 01-extracttimes.rb + ruby 02-tidytimepoints.rb + ruby 04-generateymlinclude.rb + ruby 05-addbetweenpointstotimetables.rb clean: - rm -f cbrtable.yml cbrtable.yml.in cbrfeed.zip *~ + rm -f cbrtable.yml cbrtable.yml.in cbrfeed.zip *~ output/* --- a/maxious-canberra-transit-feed/README +++ b/maxious-canberra-transit-feed/README @@ -1,10 +1,9 @@ === Introduction === This distribution contains everything required to build a basic google transit -feed for Halifax Metro Transit, Nova Scotia, Canada. Note that it is woefully -incomplete at the moment. +feed for Canberra, Australia. Note that it is woefully incomplete at the moment. -Requirements: GNU Make, Perl, Python 2.5. +Requirements: GNU Make, Perl, Python 2.5, PHP, Ruby, libpq (postgres-devel package on Fedora), postgres,json gem for Ruby, libyaml and pyyaml for Python === Usage === @@ -21,7 +20,7 @@ Then just type "make" to build the feed. The output at the end is "feed.zip". For fun, you can view this feed using the snazzy transit feed view application: -$HOME/src/transitfeed-1.1.7/python/schedule_viewer.py --feed=hfxfeed.zip +$HOME/src/transitfeed-1.1.7/python/schedule_viewer.py --feed=cbrfeed.zip === Copyright === @@ -29,7 +28,7 @@ License, please consider all software tools in distribution to be in the public domain. Use them for what you will. -I believe the Metro Transit route data is considered factual information -which can not be copyrighted. Note, however, that Metro Transit and/or -the city of Halifax may have claim over its own name and other trademarks. +I believe the ACTION busses route data is considered factual information +which can not be copyrighted. Note, however, that ACTION busses and/or +the ACT Government may have claim over its own name and other trademarks. --- a/maxious-canberra-transit-feed/add-between-times.pl +++ /dev/null @@ -1,104 +1,1 @@ -#!/usr/bin/perl -use strict; - -sub parse_time { - my ($time) = @_; - - my ($hour, $minute); - - if ($time =~ /a\Z/) { - $time =~ m/([0-9]+)([0-9][0-9])a/; - ($hour, $minute) = ($1, $2); - } elsif ($time =~ /p\Z/) { - $time =~ m/([0-9]+)([0-9][0-9])p/; - ($hour, $minute) = ($1, $2); - if ($hour < 12) { - $hour += 12; - } - } elsif ($time =~ /x\Z/) { - $time =~ m/([0-9]+)([0-9][0-9])x/; - ($hour, $minute) = ($1, $2); - if ($hour == 12) { - $hour += 12; - } else { - $hour += 24; - } - } elsif ($time =~ /^\ *-\Z/) { - ($hour, $minute) = (0, 0); - # no stop at this time - } else { - print "Should not happen! Time ('$time') misformed.\n"; - exit; - } - - return ($hour, $minute); -} - -my $num_intervals = $ARGV[0] or die "No num intervals given!"; -my $interval = $ARGV[1] or die "No interval given!"; - -my @times; - -$_ = ; -print $_; - -if ($_ !~ /^\#/) { - my @timestrs; - if ($_ =~ m/\[(.*)\]/) { - my $inner = $1; - @timestrs = split (/\,/, $inner); - - } else { - @timestrs = split /\ /; - } - - foreach (@timestrs) { - my ($hour, $minute) = parse_time($_); - push @times, [ $hour, $minute ]; - } -} - -for (my $i=1; $i<($num_intervals+1); $i++) { - my $first = 1; - foreach (@times) { - my $mytime = $_; - my ($hour, $minute) = (@$mytime[0], @$mytime[1]); - if ($hour > 0 || $minute > 0) { - $minute += $interval * $i; - if ($minute > 59) { - $hour += int($minute / 60); - $minute = $minute % 60; - if ($minute < 10) { - $minute = "0" . $minute; - } - } - } - - sub print_time { - my ($hour, $minute) = @_; - if ($hour == 0 && $minute == 0) { - print "-"; - } else { - if ($hour < 12) { - print "$hour$minute" . "a"; - } else { - if ($hour > 12) { - $hour -= 12; - } - print "$hour$minute" . "p"; - } - } - } - - if (!$first) { - print " "; - print_time($hour, $minute); - } else { - $first = 0; - print_time($hour, $minute); - } - } -print "\n"; -} - --- a/maxious-canberra-transit-feed/cbrtable.yml +++ b/maxious-canberra-transit-feed/cbrtable.yml @@ -238,20777 +238,3401 @@ - { name: Woden Interchange - Platform 9,stop_code: Woden Interchange - Platform 9, lat: -35.3442083, lng: 149.0877771} - { name: Woodcock/Clare Dennis,stop_code: Woodcock/Clare Dennis, lat: -35.4422566, lng: 149.0854375} - { name: Yarralumla Shops,stop_code: Yarralumla Shops, lat: -35.30725, lng: 149.0972} + - { name: Wjz2mTK,stop_code: Wjz2mTK, lat: -35.3815863, lng: 149.0936139} + - { name: Wjz6kCT,stop_code: Wjz6kCT, lat: -35.217402, lng: 149.0910262} + - { name: Wjz6keB,stop_code: Wjz6keB, lat: -35.2175697, lng: 149.0866478} + - { name: Wjr-TRM,stop_code: Wjr-TRM, lat: -35.2021703, lng: 149.0498418} + - { name: Wjr_MMi,stop_code: Wjr_MMi, lat: -35.200018, lng: 149.0491234} + - { name: Wjr_Mxy,stop_code: Wjr_Mxy, lat: -35.1992913, lng: 149.0468658} + - { name: Wjr-IeY,stop_code: Wjr-IeY, lat: -35.2176259, lng: 149.032238} + - { name: Wjr-J8t,stop_code: Wjr-J8t, lat: -35.2161747, lng: 149.0315719} + - { name: Wjr-InZ,stop_code: Wjr-InZ, lat: -35.2169003, lng: 149.0335258} + - { name: Wjr-Jm9,stop_code: Wjr-Jm9, lat: -35.2124379, lng: 149.0325045} + - { name: Wjr-J44,stop_code: Wjr-J44, lat: -35.2135626, lng: 149.0296181} + - { name: Wjr-BB3,stop_code: Wjr-BB3, lat: -35.2129096, lng: 149.0241561} + - { name: Wjr-Alc,stop_code: Wjr-Alc, lat: -35.2183514, lng: 149.021625} + - { name: Wjr-AY4,stop_code: Wjr-AY4, lat: -35.2190044, lng: 149.0282415} + - { name: Wjr-I4P,stop_code: Wjr-I4P, lat: -35.2191133, lng: 149.0306838} + - { name: Wjr-IcO,stop_code: Wjr-IcO, lat: -35.2191858, lng: 149.0319716} + - { name: Wjr-IqS,stop_code: Wjr-IqS, lat: -35.2202741, lng: 149.034858} + - { name: Wjr-IGJ,stop_code: Wjr-IGJ, lat: -35.2203467, lng: 149.0373003} + - { name: Wjr-IMR,stop_code: Wjr-IMR, lat: -35.2216889, lng: 149.0389433} + - { name: Wjr-H-a,stop_code: Wjr-H-a, lat: -35.2232851, lng: 149.039343} + - { name: Wjr-Q4G,stop_code: Wjr-Q4G, lat: -35.2192221, lng: 149.0415189} + - { name: Wjz6zth,stop_code: Wjz6zth, lat: -35.2241129, lng: 149.1109391} + - { name: Wjz6zon,stop_code: Wjz6zon, lat: -35.2269858, lng: 149.1109391} + - { name: Wjz6ytu,stop_code: Wjz6ytu, lat: -35.2291622, lng: 149.1110812} + - { name: Wjz6yir,stop_code: Wjz6yir, lat: -35.2314837, lng: 149.1098378} + - { name: Wjz5mpm,stop_code: Wjz5mpm, lat: -35.2538531, lng: 149.0889493} + - { name: Wjz5mxf,stop_code: Wjz5mxf, lat: -35.2538241, lng: 149.0902637} + - { name: Wjzd0oD,stop_code: Wjzd0oD, lat: -35.2874406, lng: 149.1552177} + - { name: Wjr-MNh,stop_code: Wjr-MNh, lat: -35.2433401, lng: 149.0492618} + - { name: Wjr-Mqd,stop_code: Wjr-Mqd, lat: -35.2422956, lng: 149.0448568} + - { name: Wjr-N9a,stop_code: Wjr-N9a, lat: -35.2377693, lng: 149.0421213} + - { name: Wjr-EYe,stop_code: Wjr-EYe, lat: -35.2408449, lng: 149.0394925} + - { name: Wjr-EA_,stop_code: Wjr-EA_, lat: -35.2407288, lng: 149.0362953} + - { name: Wjr-EuB,stop_code: Wjr-EuB, lat: -35.2395683, lng: 149.034448} + - { name: Wjr-Fw4,stop_code: Wjr-Fw4, lat: -35.2382916, lng: 149.035194} + - { name: Wjr-VdI,stop_code: Wjr-VdI, lat: -35.2348097, lng: 149.0539156} + - { name: Wjr-Vnf,stop_code: Wjr-Vnf, lat: -35.2331848, lng: 149.054555} + - { name: Wjr-Ws2,stop_code: Wjr-Ws2, lat: -35.230167, lng: 149.0557628} + - { name: Wjr-PWf,stop_code: Wjr-PWf, lat: -35.225611, lng: 149.0504341} + - { name: Wjr-PyX,stop_code: Wjr-PyX, lat: -35.2259882, lng: 149.0472724} + - { name: WjrZKZn,stop_code: WjrZKZn, lat: -35.2510294, lng: 149.0396391} + - { name: WjrZKnY,stop_code: WjrZKnY, lat: -35.2498968, lng: 149.0336595} + - { name: Wjz4S1U,stop_code: Wjz4S1U, lat: -35.2983385, lng: 149.1296979} + - { name: Wjz4RbQ,stop_code: Wjz4RbQ, lat: -35.3021238, lng: 149.1308574} + - { name: Wjz4IrL,stop_code: Wjz4IrL, lat: -35.307326, lng: 149.1225503} + - { name: Wjz4INj,stop_code: Wjz4INj, lat: -35.3091118, lng: 149.1261312} + - { name: Wjz3fO2,stop_code: Wjz3fO2, lat: -35.3359729, lng: 149.0817737} + - { name: Wjz3fCx,stop_code: Wjz3fCx, lat: -35.333256, lng: 149.0798309} + - { name: Wjz4peM,stop_code: Wjz4peM, lat: -35.322342, lng: 149.0979263} + - { name: Wjz4qgy,stop_code: Wjz4qgy, lat: -35.3208475, lng: 149.098981} + - { name: Wjz4qTw,stop_code: Wjz4qTw, lat: -35.3162151, lng: 149.1045086} + - { name: Wjz4qtY,stop_code: Wjz4qtY, lat: -35.3172423, lng: 149.100878} + - { name: Wjz4qs0,stop_code: Wjz4qs0, lat: -35.3182278, lng: 149.09964} + - { name: Wjz4y7z,stop_code: Wjz4y7z, lat: -35.3159129, lng: 149.1072689} + - { name: Wjz4yng,stop_code: Wjz4yng, lat: -35.316172, lng: 149.1095953} + - { name: Wjz4yzk,stop_code: Wjz4yzk, lat: -35.3186155, lng: 149.1123352} + - { name: Wjz4yGG,stop_code: Wjz4yGG, lat: -35.3194308, lng: 149.1142224} + - { name: Wjz4yQ-,stop_code: Wjz4yQ-, lat: -35.3177825, lng: 149.1159796} + - { name: Wjz4H0P,stop_code: Wjz4H0P, lat: -35.3152936, lng: 149.1185178} + - { name: Wjz4Hbx,stop_code: Wjz4Hbx, lat: -35.3133913, lng: 149.1195724} + - { name: Wjz4A7o,stop_code: Wjz4A7o, lat: -35.3052441, lng: 149.107042} + - { name: Wjz4z67,stop_code: Wjz4z67, lat: -35.3107704, lng: 149.1065979} + - { name: Wjz4A2c,stop_code: Wjz4A2c, lat: -35.3082791, lng: 149.1066534} + - { name: Wjz4tUp,stop_code: Wjz4tUp, lat: -35.3044055, lng: 149.1056974} + - { name: Wjz4tpE,stop_code: Wjz4tpE, lat: -35.3038329, lng: 149.1005569} + - { name: Wjz1HTi,stop_code: Wjz1HTi, lat: -35.4423392, lng: 149.1260397} + - { name: Wjz1HOf,stop_code: Wjz1HOf, lat: -35.4453654, lng: 149.1258946} + - { name: Wjz1HEb,stop_code: Wjz1HEb, lat: -35.4471149, lng: 149.1245306} + - { name: Wjz1GsO,stop_co