--- a/origin-src/transitfeed-1.2.6/gtfsscheduleviewer/marey_graph.py +++ b/origin-src/transitfeed-1.2.6/gtfsscheduleviewer/marey_graph.py @@ -1,1 +1,470 @@ - +#!/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. + +"""Output svg/xml data for a marey graph + +Marey graphs are a visualization form typically used for timetables. Time +is on the x-axis and position on the y-axis. This module reads data from a +transitfeed.Schedule and creates a marey graph in svg/xml format. The graph +shows the speed between stops for each trip of a route. + +TODO: This module was taken from an internal Google tool. It works but is not +well intergrated into transitfeed and schedule_viewer. Also, it has lots of +ugly hacks to compensate set canvas size and so on which could be cleaned up. + +For a little more information see (I didn't make this URL ;-) +http://transliteracies.english.ucsb.edu/post/research-project/research-clearinghouse-individual/research-reports/the-indexical-imagination-marey%e2%80%99s-graphic-method-and-the-technological-transformation-of-writing-in-the-nineteenth-century + + MareyGraph: Class, keeps cache of graph data and graph properties + and draws marey graphs in svg/xml format on request. + +""" + +import itertools +import transitfeed + + +class MareyGraph: + """Produces and caches marey graph from transit feed data.""" + + _MAX_ZOOM = 5.0 # change docstring of ChangeScaleFactor if this changes + _DUMMY_SEPARATOR = 10 #pixel + + def __init__(self): + # Timetablerelated state + self._cache = str() + self._stoplist = [] + self._tlist = [] + self._stations = [] + self._decorators = [] + + # TODO: Initialize default values via constructor parameters + # or via a class constants + + # Graph properties + self._tspan = 30 # number of hours to display + self._offset = 0 # starting hour + self._hour_grid = 60 # number of pixels for an hour + self._min_grid = 5 # number of pixels between subhour lines + + # Canvas properties + self._zoomfactor = 0.9 # svg Scaling factor + self._xoffset = 0 # move graph horizontally + self._yoffset = 0 # move graph veritcally + self._bgcolor = "lightgrey" + + # height/width of graph canvas before transform + self._gwidth = self._tspan * self._hour_grid + + def Draw(self, stoplist=None, triplist=None, height=520): + """Main interface for drawing the marey graph. + + If called without arguments, the data generated in the previous call + will be used. New decorators can be added between calls. + + Args: + # Class Stop is defined in transitfeed.py + stoplist: [Stop, Stop, ...] + # Class Trip is defined in transitfeed.py + triplist: [Trip, Trip, ...] + + Returns: + # A string that contain a svg/xml web-page with a marey graph. + " + + + + + + + """ % (self._gwidth + self._xoffset + 20, self._gheight + 15, + self._offset, self._gheight + 10, + self._xoffset, self._yoffset, self._zoomfactor) + + return svg_header + + def _DrawFooter(self): + return "" + + def _DrawDecorators(self): + """Used to draw fancy overlays on trip graphs.""" + return " ".join(self._decorators) + + def _DrawBox(self): + tmpstr = """ + """ % (0, 0, self._gwidth + 20, self._gheight, self._bgcolor) + return tmpstr + + def _BuildStations(self, stoplist): + """Dispatches the best algorithm for calculating station line position. + + Args: + # Class Stop is defined in transitfeed.py + stoplist: [Stop, Stop, ...] + # Class Trip is defined in transitfeed.py + triplist: [Trip, Trip, ...] + + Returns: + # One integer y-coordinate for each station normalized between + # 0 and X, where X is the height of the graph in pixels + [0, 33, 140, ... , X] + """ + stations = [] + dists = self._EuclidianDistances(stoplist) + stations = self._CalculateYLines(dists) + return stations + + def _EuclidianDistances(self,slist): + """Calculate euclidian distances between stops. + + Uses the stoplists long/lats to approximate distances + between stations and build a list with y-coordinates for the + horizontal lines in the graph. + + Args: + # Class Stop is defined in transitfeed.py + stoplist: [Stop, Stop, ...] + + Returns: + # One integer for each pair of stations + # indicating the approximate distance + [0,33,140, ... ,X] + """ + e_dists2 = [transitfeed.ApproximateDistanceBetweenStops(stop, tail) for + (stop,tail) in itertools.izip(slist, slist[1:])] + + return e_dists2 + + def _CalculateYLines(self, dists): + """Builds a list with y-coordinates for the horizontal lines in the graph. + + Args: + # One integer for each pair of stations + # indicating the approximate distance + dists: [0,33,140, ... ,X] + + Returns: + # One integer y-coordinate for each station normalized between + # 0 and X, where X is the height of the graph in pixels + [0, 33, 140, ... , X] + """ + tot_dist = sum(dists) + if tot_dist > 0: + pixel_dist = [float(d * (self._gheight-20))/tot_dist for d in dists] + pixel_grid = [0]+[int(pd + sum(pixel_dist[0:i])) for i,pd in + enumerate(pixel_dist)] + else: + pixel_grid = [] + + return pixel_grid + + def _TravelTimes(self,triplist,index=0): + """ Calculate distances and plot stops. + + Uses a timetable to approximate distances + between stations + + Args: + # Class Trip is defined in transitfeed.py + triplist: [Trip, Trip, ...] + # (Optional) Index of Triplist prefered for timetable Calculation + index: 3 + + Returns: + # One integer for each pair of stations + # indicating the approximate distance + [0,33,140, ... ,X] + """ + + def DistanceInTravelTime(dep_secs, arr_secs): + t_dist = arr_secs-dep_secs + if t_dist<0: + t_dist = self._DUMMY_SEPARATOR # min separation + return t_dist + + if not triplist: + return [] + + if 0 < index < len(triplist): + trip = triplist[index] + else: + trip = triplist[0] + + t_dists2 = [DistanceInTravelTime(stop[3],tail[2]) for (stop,tail) + in itertools.izip(trip.GetTimeStops(),trip.GetTimeStops()[1:])] + return t_dists2 + + def _AddWarning(self, str): + print str + + def _DrawTrips(self,triplist,colpar=""): + """Generates svg polylines for each transit trip. + + Args: + # Class Trip is defined in transitfeed.py + [Trip, Trip, ...] + + Returns: + # A string containing a polyline tag for each trip + ' 0 and not colpar: + color="purple" + scriptcall = 'onmouseover="LineClick(\'%s\',\'Trip %s starting %s\')"' % (t.trip_id, + t.trip_id, transitfeed.FormatSecondsSinceMidnight(t.GetStartTime())) + tmpstrhead = '') + return "".join(tmpstrs) + + def _Uniform(self, triplist): + """Fallback to assuming uniform distance between stations""" + # This should not be neseccary, but we are in fallback mode + longest = max([len(t.GetTimeStops()) for t in triplist]) + return [100] * longest + + def _DrawStations(self, color="#aaa"): + """Generates svg with a horizontal line for each station/stop. + + Args: + # Class Stop is defined in transitfeed.py + stations: [Stop, Stop, ...] + + Returns: + # A string containing a polyline tag for each stop + " ' %(color,20,20+y+.5,self._gwidth+20,20+y+.5)) + return "".join(tmpstrs) + + def _DrawHours(self): + """Generates svg to show a vertical hour and sub-hour grid + + Returns: + # A string containing a polyline tag for each grid line + " ' \ + % (i + .5 + 20, 20, i + .5 + 20, self._gheight)) + tmpstrs.append('%d' + % (i + 20, 20, + (i / self._hour_grid + self._offset) % 24)) + else: + tmpstrs.append('' \ + % (i + .5 + 20, 20, i + .5 + 20, self._gheight)) + return "".join(tmpstrs) + + def AddStationDecoration(self, index, color="#f00"): + """Flushes existing decorations and highlights the given station-line. + + Args: + # Integer, index of stop to be highlighted. + index: 4 + # An optional string with a html color code + color: "#fff" + """ + tmpstr = str() + num_stations = len(self._stations) + ind = int(index) + if self._stations: + if 0 0 and float(newfactor) < self._MAX_ZOOM: + self._zoomfactor = newfactor + + def ScaleLarger(self): + """Increases the zoom of the graph one step (0.1 units).""" + newfactor = self._zoomfactor + 0.1 + if float(newfactor) > 0 and float(newfactor) < self._MAX_ZOOM: + self._zoomfactor = newfactor + + def ScaleSmaller(self): + """Decreases the zoom of the graph one step(0.1 units).""" + newfactor = self._zoomfactor - 0.1 + if float(newfactor) > 0 and float(newfactor) < self._MAX_ZOOM: + self._zoomfactor = newfactor + + def ClearDecorators(self): + """Removes all the current decorators. + """ + self._decorators = [] + + def AddTextStripDecoration(self,txtstr): + tmpstr = '%s' % (0, + 20 + self._gheight, txtstr) + self._decorators.append(tmpstr) + + def SetSpan(self, first_arr, last_arr, mint=5 ,maxt=30): + s_hour = (first_arr / 3600) - 1 + e_hour = (last_arr / 3600) + 1 + self._offset = max(min(s_hour, 23), 0) + self._tspan = max(min(e_hour - s_hour, maxt), mint) + self._gwidth = self._tspan * self._hour_grid +