From: maxious Date: Sun, 28 Nov 2010 14:19:24 +0000 Subject: Added service periods to route names X-Git-Url: https://maxious.lambdacomplex.org/git/?p=bus.git&a=commitdiff&h=a21570efe133af5e9e7f614a199f77c43d6c63b6 --- Added service periods to route names --- --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -maxious-canberra-transit-feed/output/ +origin-src --- /dev/null +++ b/betweenpoint.php @@ -1,1 +1,188 @@ + + + + + + + + + + +
+ + from + to +
+ on routes +Reverse? + + +
+ +
+
+ +
+
+
+ + + --- /dev/null +++ b/betweenpoint.submit.php @@ -1,1 +1,32 @@ +\n"); + } + if ($reverse === "on") { + $ep = explode(";",$points); + $epr = array_reverse($ep); + $p = implode(";",$epr).";"; + $pointsString = substr($p,1); +$sql = "INSERT INTO between_stops ( toLocation, fromLocation, points, routes) VALUES('$from','$to','$pointsString','$routes')"; +$result = pg_query($conn, $sql); + if (!$result) { + echo("Error in SQL query: " . pg_last_error() ."
\n"); + } + } +flush(); + +?> --- /dev/null +++ b/busui/about.php @@ -1,1 +1,9 @@ + +

+Some icons by Joseph Wain / glyphish.com + --- /dev/null +++ b/busui/common.inc.php @@ -1,1 +1,315 @@ - +"; +} +function isFastDevice() { + return true; +} + +function include_header($pageTitle, $opendiv = true, $geolocate = false) { + // if (isDebug()) // set php error level high + echo ' + + + + bus.lambdacomplex.org - '.$pageTitle.' + + + + + + + + '; + if ($geolocate) { +echo " "; + } +echo ' + + '; +if ($opendiv) echo '

+ +
+

'.$pageTitle.'

+
+
'; +} + +function include_footer() +{ + echo '
'; +} + +function service_period() +{ +switch (date('w')){ + +case 0: + return 'sunday'; +case 6: + return 'saturday'; +default: + 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; +} + +function staticmap($mapPoints, $zoom = 0, $markerImage = "iconb") +{ +$width = 300; +$height = 300; +$metersperpixel[9]=305.492*$width; +$metersperpixel[10]=152.746*$width; +$metersperpixel[11]=76.373*$width; +$metersperpixel[12]=38.187*$width; +$metersperpixel[13]=19.093*$width; +$metersperpixel[14]=9.547*$width; +$metersperpixel[15]=4.773*$width; +$metersperpixel[16]=2.387*$width; +// $metersperpixel[17]=1.193*$width; +$center = ""; +$markers = ""; +$minlat = 999; +$minlon = 999; +$maxlat = 0; +$maxlon = 0; + + if (sizeof($mapPoints) < 1) return "map error"; + if (sizeof($mapPoints) === 1) { + if ($zoom == 0) $zoom = 14; + $markers .= "{$mapPoints[0][0]},{$mapPoints[0][1]},$markerimage"; + $center = "{$mapPoints[0][0]},{$mapPoints[0][1]}"; + } else { + foreach ($mapPoints as $index => $mapPoint) { + $markers .= $mapPoint[0].",".$mapPoint[1].",".$markerImage.($index+1); + if ($index+1 != sizeof($mapPoints)) $markers .= "|"; + if ($mapPoint[0] < $minlat) $minlat = $mapPoint[0]; + if ($mapPoint[0] > $maxlat) $maxlat = $mapPoint[0]; + if ($mapPoint[1] < $minlon) $minlon = $mapPoint[1]; + if ($mapPoint[1] > $maxlon) $maxlon = $mapPoint[1]; + $totalLat += $mapPoint[0]; + $totalLon += $mapPoint[1]; + } + if ($zoom == 0) { + $mapwidthinmeters = distance($minlat,$minlon,$minlat,$maxlon); + foreach (array_reverse($metersperpixel,true) as $zoomLevel => $maxdistance) + { + if ($zoom == 0 && $mapwidthinmeters < ($maxdistance + 50)) $zoom = $zoomLevel; + } + } + $center = $totalLat/sizeof($mapPoints).",".$totalLon/sizeof($mapPoints); + } + + return ''; +} + +function distance($lat1, $lng1, $lat2, $lng2) +{ + $pi80 = M_PI / 180; + $lat1 *= $pi80; + $lng1 *= $pi80; + $lat2 *= $pi80; + $lng2 *= $pi80; + + $r = 6372.797; // mean radius of Earth in km + $dlat = $lat2 - $lat1; + $dlng = $lng2 - $lng1; + $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2); + $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); + $km = $r * $c; + + return $km * 1000; +} + +function decodePolylineToArray($encoded) +{ +// source: http://latlongeeks.com/forum/viewtopic.php?f=4&t=5 + $length = strlen($encoded); + $index = 0; + $points = array(); + $lat = 0; + $lng = 0; + + while ($index < $length) + { + // Temporary variable to hold each ASCII byte. + $b = 0; + + // The encoded polyline consists of a latitude value followed by a + // longitude value. They should always come in pairs. Read the + // latitude value first. + $shift = 0; + $result = 0; + do + { + // The `ord(substr($encoded, $index++))` statement returns the ASCII + // code for the character at $index. Subtract 63 to get the original + // value. (63 was added to ensure proper ASCII characters are displayed + // in the encoded polyline string, which is `human` readable) + $b = ord(substr($encoded, $index++)) - 63; + + // AND the bits of the byte with 0x1f to get the original 5-bit `chunk. + // Then left shift the bits by the required amount, which increases + // by 5 bits each time. + // OR the value into $results, which sums up the individual 5-bit chunks + // into the original value. Since the 5-bit chunks were reversed in + // order during encoding, reading them in this way ensures proper + // summation. + $result |= ($b & 0x1f) << $shift; + $shift += 5; + } + // Continue while the read byte is >= 0x20 since the last `chunk` + // was not OR'd with 0x20 during the conversion process. (Signals the end) + while ($b >= 0x20); + + // Check if negative, and convert. (All negative values have the last bit + // set) + $dlat = (($result & 1) ? ~($result >> 1) : ($result >> 1)); + + // Compute actual latitude since value is offset from previous value. + $lat += $dlat; + + // The next values will correspond to the longitude for this point. + $shift = 0; + $result = 0; + do + { + $b = ord(substr($encoded, $index++)) - 63; + $result |= ($b & 0x1f) << $shift; + $shift += 5; + } + while ($b >= 0x20); + + $dlng = (($result & 1) ? ~($result >> 1) : ($result >> 1)); + $lng += $dlng; + + // The actual latitude and longitude values were multiplied by + // 1e5 before encoding so that they could be converted to a 32-bit + // integer representation. (With a decimal accuracy of 5 places) + // Convert back to original values. + $points[] = array($lat * 1e-5, $lng * 1e-5); + } + + return $points; +} + +function object2array($object) { + if (is_object($object)) { + foreach ($object as $key => $value) { + $array[$key] = $value; + } + } + else { + $array = $object; + } + return $array; +} + +function geocode($query, $giveOptions) { + $url = "http://geocoding.cloudmade.com/daa03470bb8740298d4b10e3f03d63e6/geocoding/v2/find.js?query=".$query."&bbox=-35.5,149.00,-35.15,149.1930&return_location=true&bbox_only=true"; + $contents = json_decode(getPage($url)); + if ($giveOptions) return $contents->features; + elseif (isset($contents->features[0]->centroid)) return $contents->features[0]->centroid->coordinates[0].",".$contents->features[0]->centroid->coordinates[1]; + else return ""; +} + +function reverseGeocode($lat,$lng) { + $url = "http://geocoding.cloudmade.com/daa03470bb8740298d4b10e3f03d63e6/geocoding/v2/find.js?around=".$lat.",".$lng."&distance=closest&object_type=road"; + $contents = json_decode(getPage($url)); + return $contents->features[0]->properties->name; +} + +function startsWith($haystack,$needle,$case=true) { + if($case){return (strcmp(substr($haystack, 0, strlen($needle)),$needle)===0);} + return (strcasecmp(substr($haystack, 0, strlen($needle)),$needle)===0); +} + +function endsWith($haystack,$needle,$case=true) { + if($case){return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0);} + return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)),$needle)===0); +} +?> + + --- /dev/null +++ b/busui/index.php @@ -1,1 +1,32 @@ + +
+
+

logo
+ bus.lambdacomplex.org

+
+
+ Launch Trip Planner... + + +
+ Current Location: Search? Update to Here?
+ Time:
+ Service Period:
+
+
+
+ + --- /dev/null +++ b/busui/readme.txt @@ -1,1 +1,30 @@ +# 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 + +static maps +https://code.google.com/apis/maps/documentation/staticmaps/ +http://www.multimap.com/openapidocs/1.2/web_service/staticmaps.htm +http://dev.openstreetmap.de/staticmap/ (os @ http://sourceforge.net/projects/staticmaplite/) +(php and open source @ http://trac.openstreetmap.org/browser/sites/other/StaticMap?rev=16348) +http://pafciu17.dev.openstreetmap.org/ + --- /dev/null +++ b/busui/routeList.php @@ -1,1 +1,58 @@ + + +
+ '; +echo ' \n"; + +include_footer(); +?> + --- /dev/null +++ b/busui/schedule_viewer.py @@ -1,1 +1,560 @@ - +#!/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(): + servicep = None + for t in schedule.GetTripList(): + if t.route_id == r.route_id: + servicep = t.service_period + break + result.append( (r.route_id, r.route_short_name, r.route_long_name, servicep.service_id) ) + 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_routetrips(self, params): + """ Get a trip for a route_id (preferablly the next one) """ + schedule = self.server.schedule + query = params.get('route_id', None).lower() + result = [] + for t in schedule.GetTripList(): + if t.route_id == query: + result.append ( (t.GetStartTime(), t.trip_id) ) + return sorted(result, key=lambda trip: trip[0]) + + 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_timingpoints(self, params): + schedule = self.server.schedule + matches = [] + for s in schedule.GetStopList(): + if s.stop_code.find("Wj") == -1: + matches.append(StopToTuple(s)) + return matches + + 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)) + service_period = params.get('service_period', None) + 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 + if service_period == None or trip.service_id == service_period: + 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() + --- /dev/null +++ b/busui/staticmaplite/.gitignore @@ -1,1 +1,3 @@ +cache/tiles +cache/map --- /dev/null +++ b/busui/staticmaplite/images/markers/GPlotter - Make Google Maps Easily.URL @@ -1,1 +1,3 @@ +[InternetShortcut] +URL=http://gplotter.offwhite.net/ --- /dev/null +++ b/busui/staticmaplite/images/markers/Google Maps Icons, Free!.URL @@ -1,1 +1,3 @@ +[InternetShortcut] +URL=http://brennan.offwhite.net/blog/2005/07/23/new-google-maps-icons-free/ --- /dev/null +++ b/busui/staticmaplite/index.html @@ -1,1 +1,122 @@ + + + + + + staticMapLite + + + +
+ +
+ +
+

+ staticMapLite - simple map for your website +

+

+

+

+ This image was created using the following simple <img> tag: +

<img src="staticmap.php?center=40.714728,-73.998672&zoom=14&size=865x512&maptype=mapnik" />
+

+
+
+
+

+ Place Markers +

+ +

+ +

Add markers by appending them to the image URL: +

markers=40.702147,-74.015794,lightblue1|40.711614,-74.012318,lightblue2|40.718217,-73.998284,lightblue3
+

+
+
+
+

+ Use Different Map Styles (Tile Sources) +

+ +

+

+ +
maptype=mapnik
+
+
+ +
maptype=osmarenderer
+
+
+ +
maptype=cycle
+
+
+

+
+ +
+ +
+ + --- /dev/null +++ b/busui/staticmaplite/selinux-fix.sh @@ -1,1 +1,3 @@ +chcon -R -t httpd_sys_content_rw_t cache + --- /dev/null +++ b/busui/staticmaplite/staticmap.php @@ -1,1 +1,273 @@ - + + * + * USAGE: + * + * staticmap.php?center=40.714728,-73.998672&zoom=14&size=512x512&maptype=mapnik&markers=40.702147,-74.015794,blues|40.711614,-74.012318,greeng|40.718217,-73.998284,redc + * + */ + +error_reporting(0); +ini_set('display_errors','off'); + +Class staticMapLite { + + protected $tileSize = 256; + protected $tileSrcUrl = array( 'mapnik' => 'http://tile.openstreetmap.org/{Z}/{X}/{Y}.png', + 'osmarenderer' => 'http://c.tah.openstreetmap.org/Tiles/tile/{Z}/{X}/{Y}.png', + 'cycle' => 'http://c.andy.sandbox.cloudmade.com/tiles/cycle/{Z}/{X}/{Y}.png' + ); + + protected $tileDefaultSrc = 'mapnik'; + protected $markerBaseDir = 'images/markers'; + protected $osmLogo = 'images/osm_logo.png'; + + protected $useTileCache = true; + protected $tileCacheBaseDir = './cache/tiles'; + + protected $useMapCache = true; + protected $mapCacheBaseDir = './cache/maps'; + protected $mapCacheID = ''; + protected $mapCacheFile = ''; + protected $mapCacheExtension = 'png'; + + protected $zoom, $lat, $lon, $width, $height, $markers, $image, $maptype; + protected $centerX, $centerY, $offsetX, $offsetY; + + public function __construct(){ + $this->zoom = 0; + $this->lat = 0; + $this->lon = 0; + $this->width = 500; + $this->height = 350; + $this->markers = array(); + $this->maptype = $this->tileDefaultSrc; + } + + public function parseParams(){ + global $_GET; + + // get zoom from GET paramter + $this->zoom = $_GET['zoom']?intval($_GET['zoom']):0; + if($this->zoom>18)$this->zoom = 18; + + // get lat and lon from GET paramter + list($this->lat,$this->lon) = split(',',$_GET['center']); + $this->lat = floatval($this->lat); + $this->lon = floatval($this->lon); + + // get zoom from GET paramter + if($_GET['size']){ + list($this->width, $this->height) = split('x',$_GET['size']); + $this->width = intval($this->width); + $this->height = intval($this->height); + } + if($_GET['markers']){ + $markers = split('%7C|\|',$_GET['markers']); + foreach($markers as $marker){ + list($markerLat, $markerLon, $markerImage) = split(',',$marker); + $markerLat = floatval($markerLat); + $markerLon = floatval($markerLon); + $markerImage = basename($markerImage); + $this->markers[] = array('lat'=>$markerLat, 'lon'=>$markerLon, 'image'=>$markerImage); + } + + } + if($_GET['maptype']){ + if(array_key_exists($_GET['maptype'],$this->tileSrcUrl)) $this->maptype = $_GET['maptype']; + } + } + + public function lonToTile($long, $zoom){ + return (($long + 180) / 360) * pow(2, $zoom); + } + + public function latToTile($lat, $zoom){ + return (1 - log(tan($lat * pi()/180) + 1 / cos($lat* pi()/180)) / pi()) /2 * pow(2, $zoom); + } + + public function initCoords(){ + $this->centerX = $this->lonToTile($this->lon, $this->zoom); + $this->centerY = $this->latToTile($this->lat, $this->zoom); + $this->offsetX = floor((floor($this->centerX)-$this->centerX)*$this->tileSize); + $this->offsetY = floor((floor($this->centerY)-$this->centerY)*$this->tileSize); + } + + public function createBaseMap(){ + $this->image = imagecreatetruecolor($this->width, $this->height); + $startX = floor($this->centerX-($this->width/$this->tileSize)/2); + $startY = floor($this->centerY-($this->height/$this->tileSize)/2); + $endX = ceil($this->centerX+($this->width/$this->tileSize)/2); + $endY = ceil($this->centerY+($this->height/$this->tileSize)/2); + $this->offsetX = -floor(($this->centerX-floor($this->centerX))*$this->tileSize); + $this->offsetY = -floor(($this->centerY-floor($this->centerY))*$this->tileSize); + $this->offsetX += floor($this->width/2); + $this->offsetY += floor($this->height/2); + $this->offsetX += floor($startX-floor($this->centerX))*$this->tileSize; + $this->offsetY += floor($startY-floor($this->centerY))*$this->tileSize; + + for($x=$startX; $x<=$endX; $x++){ + for($y=$startY; $y<=$endY; $y++){ + $url = str_replace(array('{Z}','{X}','{Y}'),array($this->zoom, $x, $y), $this->tileSrcUrl[$this->maptype]); + $tileImage = imagecreatefromstring($this->fetchTile($url)); + $destX = ($x-$startX)*$this->tileSize+$this->offsetX; + $destY = ($y-$startY)*$this->tileSize+$this->offsetY; + imagecopy($this->image, $tileImage, $destX, $destY, 0, 0, $this->tileSize, $this->tileSize); + } + } + } + + + public function placeMarkers(){ + foreach($this->markers as $marker){ + $markerLat = $marker['lat']; + $markerLon = $marker['lon']; + $markerImage = $marker['image']; + $markerIndex++; + $markerFilename = $markerImage?(file_exists($this->markerBaseDir.'/'.$markerImage.".png")?$markerImage:'lightblue'.$markerIndex):'lightblue'.$markerIndex; + if(file_exists($this->markerBaseDir.'/'.$markerFilename.".png")){ + $markerImg = imagecreatefrompng($this->markerBaseDir.'/'.$markerFilename.".png"); + } else { + $markerImg = imagecreatefrompng($this->markerBaseDir.'/lightblue1.png'); + } + $destX = floor(($this->width/2)-$this->tileSize*($this->centerX-$this->lonToTile($markerLon, $this->zoom))); + $destY = floor(($this->height/2)-$this->tileSize*($this->centerY-$this->latToTile($markerLat, $this->zoom))); + $destY = $destY - imagesy($markerImg); + + imagecopy($this->image, $markerImg, $destX, $destY, 0, 0, imagesx($markerImg), imagesy($markerImg)); + + }; +} + + + + public function tileUrlToFilename($url){ + return $this->tileCacheBaseDir."/".str_replace(array('http://'),'',$url); + } + + public function checkTileCache($url){ + $filename = $this->tileUrlToFilename($url); + if(file_exists($filename)){ + return file_get_contents($filename); + } + } + + public function checkMapCache(){ + $this->mapCacheID = md5($this->serializeParams()); + $filename = $this->mapCacheIDToFilename(); + if(file_exists($filename)) return true; + } + + public function serializeParams(){ + return join("&",array($this->zoom,$this->lat,$this->lon,$this->width,$this->height, serialize($this->markers),$this->maptype)); + } + + public function mapCacheIDToFilename(){ + if(!$this->mapCacheFile){ + $this->mapCacheFile = $this->mapCacheBaseDir."/".substr($this->mapCacheID,0,2)."/".substr($this->mapCacheID,2,2)."/".substr($this->mapCacheID,4); + } + return $this->mapCacheFile.".".$this->mapCacheExtension; + } + + + + public function mkdir_recursive($pathname, $mode){ + return mkdir($pathname, $mode, true); + } + public function writeTileToCache($url, $data){ + $filename = $this->tileUrlToFilename($url); + $this->mkdir_recursive(dirname($filename),0777); + file_put_contents($filename, $data); + } + + public function fetchTile($url){ + if($this->useTileCache && ($cached = $this->checkTileCache($url))) return $cached; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0"); + curl_setopt($ch, CURLOPT_URL, $url); + $tile = curl_exec($ch); + curl_close($ch); + if($this->useTileCache){ + $this->writeTileToCache($url,$tile); + } + return $tile; + + } + + public function copyrightNotice(){ + $logoImg = imagecreatefrompng($this->osmLogo); + imagecopy($this->image, $logoImg, imagesx($this->image)-imagesx($logoImg), imagesy($this->image)-imagesy($logoImg), 0, 0, imagesx($logoImg), imagesy($logoImg)); + + } + + public function sendHeader(){ + header('Content-Type: image/png'); + $expires = 60*60*24*14; + header("Pragma: public"); + header("Cache-Control: maxage=".$expires); + header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT'); + } + + public function makeMap(){ + $this->initCoords(); + $this->createBaseMap(); + if(count($this->markers))$this->placeMarkers(); + if($this->osmLogo) $this->copyrightNotice(); + } + + public function showMap(){ + $this->parseParams(); + if($this->useMapCache){ + // use map cache, so check cache for map + if(!$this->checkMapCache()){ + // map is not in cache, needs to be build + $this->makeMap(); + $this->mkdir_recursive(dirname($this->mapCacheIDToFilename()),0777); + imagepng($this->image,$this->mapCacheIDToFilename(),9); + $this->sendHeader(); + if(file_exists($this->mapCacheIDToFilename())){ + return file_get_contents($this->mapCacheIDToFilename()); + } else { + return imagepng($this->image); + } + } else { + // map is in cache + $this->sendHeader(); + return file_get_contents($this->mapCacheIDToFilename()); + } + + } else { + // no cache, make map, send headers and deliver png + $this->makeMap(); + $this->sendHeader(); + return imagepng($this->image); + + } + } + +} + +$map = new staticMapLite(); +print $map->showMap(); + +?> + --- /dev/null +++ b/busui/stop.php @@ -1,1 +1,23 @@ +

'.staticmap(Array(0 => Array($stop[2],$stop[3]))).'

'; +echo ' '; +include_footer(); +?> + --- /dev/null +++ b/busui/stopList.php @@ -1,1 +1,36 @@ + + + + '; +echo ' '; + +include_footer(); +?> + + --- /dev/null +++ b/busui/trip.php @@ -1,1 +1,40 @@ + 0)) $tripid = $trips[0][1]; +} +$url = $APIurl."/json/triprows?trip=".$tripid; +$trips = array_flatten(json_decode(getPage($url))); +debug(print_r($trips,true)); +include_header("Stops on ". $trips[1]->route_short_name . ' '. $trips[1]->route_long_name); +echo ' '; +include_footer(); +?> + --- /dev/null +++ b/busui/tripPlanner.php @@ -1,1 +1,125 @@ +$errorMessage"; + echo '
+
+ + + +
+
+ + + +
+
+ + +
+
+ + +
+
'; + echo ""; + } + + function processItinerary($itineraryNumber, $itinerary) + { + echo '
0 ? 'data-collapsed="true"' : "") . '>

Option #' . ($itineraryNumber + 1) . ": " . floor($itinerary->duration / 60000) . " minutes ({$itinerary->startTime} to {$itinerary->endTime})

"; + echo "Walking time: " . floor($itinerary->walkTime / 60000) . " minutes (" . floor($itinerary->walkDistance) . " meters)
\n"; + echo "Transit time: " . floor($itinerary->transitTime / 60000) . " minutes
\n"; + echo "Waiting time: " . floor($itinerary->waitingTime / 60000) . " minutes
\n"; + + + + if (is_array($itinerary->legs->leg)) { + $legMarkers = array(); + foreach ($itinerary->legs->leg as $legNumber => $leg) { + $legMarkers[] = array($leg->from->lat, $leg->from->lon); + } + echo '' . staticmap($legMarkers) . "
\n"; + echo '

"; + } else { + echo '' . staticmap(array(array($itinerary->legs->leg->from->lat, $itinerary->legs->leg->from->lon))) . "
\n"; + processLeg(0, $itinerary-