signature:
def plot_all_stops ( g , ax = None , scalebar = False ) :
    """Parameters
 g : A gtfspy . gtfs . GTFS object
 ax : matplotlib . Axes object , optional
 If None , a new figure and an axis is created , otherwise results are plotted on the axis .
 scalebar : bool , optional
 Whether to include a scalebar to the plot .
 Returns
 ax : matplotlib . Axes"""

implementation:
assert ( isinstance ( g , GTFS ) )
lon_min , lon_max , lat_min , lat_max = get_spatial_bounds ( g )
smopy_map = get_smopy_map ( lon_min , lon_max , lat_min , lat_max )
if ax is None :
    fig = plt . figure ( )
    ax = fig . add_subplot ( 111 )
ax = smopy_map . show_mpl ( figsize = None , ax = ax , alpha = 0.8 )
stops = g . stops ( )
lats = numpy . array ( stops [ 'lat' ] )
lons = numpy . array ( stops [ 'lon' ] )
xs , ys = smopy_map . to_pixels ( lats , lons )
ax . scatter ( xs , ys , color = "red" , s = 10 )
ax . set_xlim ( min ( xs ) , max ( xs ) )
ax . set_ylim ( max ( ys ) , min ( ys ) )
return ax


signature:
def set_process_timezone ( TZ ) :
    """Parameters
 TZ : string"""

implementation:
try :
    prev_timezone = os . environ [ 'TZ' ]
except KeyError :
    prev_timezone = None
os . environ [ 'TZ' ] = TZ
time . tzset ( )
# Cause C - library functions to notice the update .
return prev_timezone


signature:
def wgs84_distance ( lat1 , lon1 , lat2 , lon2 ) :
    """Distance ( in meters ) between two points in WGS84 coord system ."""

implementation:
dLat = math . radians ( lat2 - lat1 )
dLon = math . radians ( lon2 - lon1 )
a = ( math . sin ( dLat / 2 ) * math . sin ( dLat / 2 ) + math . cos ( math . radians ( lat1 ) ) * math . cos ( math . radians ( lat2 ) ) * math . sin ( dLon / 2 ) * math . sin ( dLon / 2 ) )
c = 2 * math . atan2 ( math . sqrt ( a ) , math . sqrt ( 1 - a ) )
d = EARTH_RADIUS * c
return d


signature:
def create_file ( fname = None , fname_tmp = None , tmpdir = None , save_tmpfile = False , keepext = False ) :
    """Context manager for making files with possibility of failure .
 If you are creating a file , it is possible that the code will fail
 and leave a corrupt intermediate file . This is especially damaging
 if this is used as automatic input to another process . This context
 manager helps by creating a temporary filename , your code runs and
 creates that temporary file , and then if no exceptions are raised ,
 the context manager will move the temporary file to the original
 filename you intended to open .
 Parameters
 fname : str
 Target filename , this file will be created if all goes well
 fname _ tmp : str
 If given , this is used as the temporary filename .
 tmpdir : str or bool
 If given , put temporary files in this directory . If ` True ` ,
 then find a good tmpdir that is not on local filesystem .
 save _ tmpfile : bool
 If true , the temporary file is not deleteted if an exception
 is raised .
 keepext : bool , default False
 If true , have tmpfile have same extension as final file .
 Returns ( as context manager value )
 fname _ tmp : str
 Temporary filename to be used . Same as ` fname _ tmp `
 if given as an argument .
 Raises
 Re - raises any except occuring during the context block ."""

implementation:
# Do nothing if requesting sqlite memory DB .
if fname == ':memory:' :
    yield fname
    return
if fname_tmp is None : # no tmpfile name given - compute some basic info
    basename = os . path . basename ( fname )
    root , ext = os . path . splitext ( basename )
    dir_ = this_dir = os . path . dirname ( fname )
    # Remove filename extension , in case this matters for
    # automatic things itself .
    if not keepext :
        root = root + ext
        ext = ''
    if tmpdir : # we should use a different temporary directory
        if tmpdir is True : # Find a directory ourself , searching some common
        # places .
            for dir__ in possible_tmpdirs :
                if os . access ( dir__ , os . F_OK ) :
                    dir_ = dir__
                    break
    # Make the actual tmpfile , with our chosen tmpdir , directory ,
    # extension . Set it to not delete automatically , since on
    # success we will move it to elsewhere .
    tmpfile = tempfile . NamedTemporaryFile ( prefix = 'tmp-' + root + '-' , suffix = ext , dir = dir_ , delete = False )
    fname_tmp = tmpfile . name
try :
    yield fname_tmp
except Exception as e :
    if save_tmpfile :
        print ( "Temporary file is '%s'" % fname_tmp )
    else :
        os . unlink ( fname_tmp )
    raise
# Move the file back to the original location .
try :
    os . rename ( fname_tmp , fname )
    # We have to manually set permissions . tempfile does not use
    # umask , for obvious reasons .
    os . chmod ( fname , 0o777 & ~ current_umask )
# ' Invalid cross - device link ' - you can ' t rename files across
# filesystems . So , we have to fallback to moving it . But , we
# want to move it using tmpfiles also , so that the final file
# appearing is atomic . We use . . . tmpfiles .
except OSError as e : # New temporary file in same directory
    tmpfile2 = tempfile . NamedTemporaryFile ( prefix = 'tmp-' + root + '-' , suffix = ext , dir = this_dir , delete = False )
    # Copy contents over
    shutil . copy ( fname_tmp , tmpfile2 . name )
    # Rename new tmpfile , unlink old one on other filesystem .
    os . rename ( tmpfile2 . name , fname )
    os . chmod ( fname , 0o666 & ~ current_umask )
    os . unlink ( fname_tmp )


signature:
def execute ( cur , * args ) :
    """Utility function to print sqlite queries before executing .
 Use instead of cur . execute ( ) . First argument is cursor .
 cur . execute ( stmt )
 becomes
 util . execute ( cur , stmt )"""

implementation:
stmt = args [ 0 ]
if len ( args ) > 1 :
    stmt = stmt . replace ( '%' , '%%' ) . replace ( '?' , '%r' )
    print ( stmt % ( args [ 1 ] ) )
return cur . execute ( * args )


signature:
def str_time_to_day_seconds ( time ) :
    """Converts time strings to integer seconds
 : param time : % H : % M : % S string
 : return : integer seconds"""

implementation:
t = str ( time ) . split ( ':' )
seconds = int ( t [ 0 ] ) * 3600 + int ( t [ 1 ] ) * 60 + int ( t [ 2 ] )
return seconds


signature:
def makedirs ( path ) :
    """Create directories if they do not exist , otherwise do nothing .
 Return path for convenience"""

implementation:
if not os . path . isdir ( path ) :
    os . makedirs ( path )
return path


signature:
def source_csv_to_pandas ( path , table , read_csv_args = None ) :
    """Parameters
 path : str
 path to directory or zipfile
 table : str
 name of table
 read _ csv _ args :
 string arguments passed to the read _ csv function
 Returns
 df : pandas : DataFrame"""

implementation:
if '.txt' not in table :
    table += '.txt'
if isinstance ( path , dict ) :
    data_obj = path [ table ]
    f = data_obj . split ( "\n" )
else :
    if os . path . isdir ( path ) :
        f = open ( os . path . join ( path , table ) )
    else :
        z = zipfile . ZipFile ( path )
        for path in z . namelist ( ) :
            if table in path :
                table = path
                break
        try :
            f = zip_open ( z , table )
        except KeyError as e :
            return pd . DataFrame ( )
if read_csv_args :
    df = pd . read_csv ( ** read_csv_args )
else :
    df = pd . read_csv ( f )
return df


signature:
def write_shapefile ( data , shapefile_path ) :
    from numpy import int64
    """: param data : list of dicts where dictionary contains the keys lons and lats
 : param shapefile _ path : path where shapefile is saved
 : return :"""

implementation:
w = shp . Writer ( shp . POLYLINE )
# shapeType = 3)
fields = [ ]
encode_strings = [ ]
# This makes sure every geom has all the attributes
w . autoBalance = 1
# Create all attribute fields except for lats and lons . In addition the field names are saved for the
# datastoring phase . Encode _ strings stores . encode methods as strings for all fields that are strings
if not fields :
    for key , value in data [ 0 ] . items ( ) :
        if key != u'lats' and key != u'lons' :
            fields . append ( key )
            if type ( value ) == float :
                w . field ( key . encode ( 'ascii' ) , fieldType = 'N' , size = 11 , decimal = 3 )
                print ( "float" , type ( value ) )
            elif type ( value ) == int or type ( value ) == int64 :
                print ( "int" , type ( value ) )
                # encode _ strings . append ( " . encode ( ' ascii ' ) " )
                w . field ( key . encode ( 'ascii' ) , fieldType = 'N' , size = 6 , decimal = 0 )
            else :
                print ( "other type" , type ( value ) )
                w . field ( key . encode ( 'ascii' ) )
for dict_item in data :
    line = [ ]
    lineparts = [ ]
    records = [ ]
    records_string = ''
    for lat , lon in zip ( dict_item [ u'lats' ] , dict_item [ u'lons' ] ) :
        line . append ( [ float ( lon ) , float ( lat ) ] )
    lineparts . append ( line )
    w . line ( parts = lineparts )
    # The shapefile records command is built up as strings to allow a differing number of columns
    for field in fields :
        if records_string :
            records_string += ", dict_item['" + field + "']"
        else :
            records_string += "dict_item['" + field + "']"
    method_string = "w.record(" + records_string + ")"
    # w . record ( dict _ item [ ' name ' ] , dict _ item [ ' agency ' ] , dict _ item [ ' agency _ name ' ] , dict _ item [ ' type ' ] , dict _ item [ ' lons ' ] )
    print ( method_string )
    eval ( method_string )
w . save ( shapefile_path )


signature:
def draw_net_using_node_coords ( net ) :
    """Plot a networkx . Graph by using the lat and lon attributes of nodes .
 Parameters
 net : networkx . Graph
 Returns
 fig : matplotlib . figure
 the figure object where the network is plotted"""

implementation:
import matplotlib . pyplot as plt
fig = plt . figure ( )
node_coords = { }
for node , data in net . nodes ( data = True ) :
    node_coords [ node ] = ( data [ 'lon' ] , data [ 'lat' ] )
ax = fig . add_subplot ( 111 )
networkx . draw ( net , pos = node_coords , ax = ax , node_size = 50 )
return fig


signature:
def difference_of_pandas_dfs ( df_self , df_other , col_names = None ) :
    """Returns a dataframe with all of df _ other that are not in df _ self , when considering the columns specified in col _ names
 : param df _ self : pandas Dataframe
 : param df _ other : pandas Dataframe
 : param col _ names : list of column names
 : return :"""

implementation:
df = pd . concat ( [ df_self , df_other ] )
df = df . reset_index ( drop = True )
df_gpby = df . groupby ( col_names )
idx = [ x [ 0 ] for x in list ( df_gpby . groups . values ( ) ) if len ( x ) == 1 ]
df_sym_diff = df . reindex ( idx )
df_diff = pd . concat ( [ df_other , df_sym_diff ] )
df_diff = df_diff . reset_index ( drop = True )
df_gpby = df_diff . groupby ( col_names )
idx = [ x [ 0 ] for x in list ( df_gpby . groups . values ( ) ) if len ( x ) == 2 ]
df_diff = df_diff . reindex ( idx )
return df_diff


signature:
def _finalize_profiles ( self ) :
    """Deal with the first walks by joining profiles to other stops within walking distance ."""

implementation:
for stop , stop_profile in self . _stop_profiles . items ( ) :
    assert ( isinstance ( stop_profile , NodeProfileMultiObjective ) )
    neighbor_label_bags = [ ]
    walk_durations_to_neighbors = [ ]
    departure_arrival_stop_pairs = [ ]
    if stop_profile . get_walk_to_target_duration ( ) != 0 and stop in self . _walk_network . node :
        neighbors = networkx . all_neighbors ( self . _walk_network , stop )
        for neighbor in neighbors :
            neighbor_profile = self . _stop_profiles [ neighbor ]
            assert ( isinstance ( neighbor_profile , NodeProfileMultiObjective ) )
            neighbor_real_connection_labels = neighbor_profile . get_labels_for_real_connections ( )
            neighbor_label_bags . append ( neighbor_real_connection_labels )
            walk_durations_to_neighbors . append ( int ( self . _walk_network . get_edge_data ( stop , neighbor ) [ "d_walk" ] / self . _walk_speed ) )
            departure_arrival_stop_pairs . append ( ( stop , neighbor ) )
    stop_profile . finalize ( neighbor_label_bags , walk_durations_to_neighbors , departure_arrival_stop_pairs )


signature:
def import_gtfs ( gtfs_sources , output , preserve_connection = False , print_progress = True , location_name = None , ** kwargs ) :
    """Import a GTFS database
 gtfs _ sources : str , dict , list
 Paths to the gtfs zip file or to the directory containing the GTFS data .
 Alternatively , a dict can be provide that maps gtfs filenames
 ( like ' stops . txt ' and ' agencies . txt ' ) to their string presentations .
 output : str or sqlite3 . Connection
 path to the new database to be created , or an existing
 sqlite3 connection
 preserve _ connection : bool , optional
 Whether to close the connection in the end , or not .
 print _ progress : bool , optional
 Whether to print progress output
 location _ name : str , optional
 set the location of this database"""

implementation:
if isinstance ( output , sqlite3 . Connection ) :
    conn = output
else : # if os . path . isfile ( output ) :
# raise RuntimeError ( ' File already exists ' )
    conn = sqlite3 . connect ( output )
if not isinstance ( gtfs_sources , list ) :
    gtfs_sources = [ gtfs_sources ]
cur = conn . cursor ( )
time_import_start = time . time ( )
# These are a bit unsafe , but make importing much faster ,
# especially on scratch .
cur . execute ( 'PRAGMA page_size = 4096;' )
cur . execute ( 'PRAGMA mmap_size = 1073741824;' )
cur . execute ( 'PRAGMA cache_size = -2000000;' )
cur . execute ( 'PRAGMA temp_store=2;' )
# Changes of isolation level are python3.6 workarounds -
# eventually will probably be fixed and this can be removed .
conn . isolation_level = None
# change to autocommit mode ( former default )
cur . execute ( 'PRAGMA journal_mode = OFF;' )
# cur . execute ( ' PRAGMA journal _ mode = WAL ; ' )
cur . execute ( 'PRAGMA synchronous = OFF;' )
conn . isolation_level = ''
# change back to python default .
# end python3.6 workaround
# Do the actual importing .
loaders = [ L ( gtfssource = gtfs_sources , print_progress = print_progress , ** kwargs ) for L in Loaders ]
for loader in loaders :
    loader . assert_exists_if_required ( )
# Do initial import . This consists of making tables , raw insert
# of the CSVs , and then indexing .
for loader in loaders :
    loader . import_ ( conn )
# Do any operations that require all tables present .
for Loader in loaders :
    Loader . post_import_round2 ( conn )
# Make any views
for Loader in loaders :
    Loader . make_views ( conn )
# Make any views
for F in postprocessors :
    F ( conn )
# Set up same basic metadata .
from gtfspy import gtfs as mod_gtfs
G = mod_gtfs . GTFS ( output )
G . meta [ 'gen_time_ut' ] = time . time ( )
G . meta [ 'gen_time' ] = time . ctime ( )
G . meta [ 'import_seconds' ] = time . time ( ) - time_import_start
G . meta [ 'download_date' ] = ''
G . meta [ 'location_name' ] = ''
G . meta [ 'n_gtfs_sources' ] = len ( gtfs_sources )
# Extract things from GTFS
download_date_strs = [ ]
for i , source in enumerate ( gtfs_sources ) :
    if len ( gtfs_sources ) == 1 :
        prefix = ""
    else :
        prefix = "feed_" + str ( i ) + "_"
    if isinstance ( source , string_types ) :
        G . meta [ prefix + 'original_gtfs' ] = decode_six ( source ) if source else None
        # Extract GTFS date . Last date pattern in filename .
        filename_date_list = re . findall ( r'\d{4}-\d{2}-\d{2}' , source )
        if filename_date_list :
            date_str = filename_date_list [ - 1 ]
            G . meta [ prefix + 'download_date' ] = date_str
            download_date_strs . append ( date_str )
        if location_name :
            G . meta [ 'location_name' ] = location_name
        else :
            location_name_list = re . findall ( r'/([^/]+)/\d{4}-\d{2}-\d{2}' , source )
            if location_name_list :
                G . meta [ prefix + 'location_name' ] = location_name_list [ - 1 ]
            else :
                try :
                    G . meta [ prefix + 'location_name' ] = source . split ( "/" ) [ - 4 ]
                except :
                    G . meta [ prefix + 'location_name' ] = source
if G . meta [ 'download_date' ] == "" :
    unique_download_dates = list ( set ( download_date_strs ) )
    if len ( unique_download_dates ) == 1 :
        G . meta [ 'download_date' ] = unique_download_dates [ 0 ]
G . meta [ 'timezone' ] = cur . execute ( 'SELECT timezone FROM agencies LIMIT 1' ) . fetchone ( ) [ 0 ]
stats . update_stats ( G )
del G
if print_progress :
    print ( "Vacuuming..." )
# Next 3 lines are python 3.6 work - arounds again .
conn . isolation_level = None
# former default of autocommit mode
cur . execute ( 'VACUUM;' )
conn . isolation_level = ''
# back to python default
# end python3.6 workaround
if print_progress :
    print ( "Analyzing..." )
cur . execute ( 'ANALYZE' )
if not ( preserve_connection is True ) :
    conn . close ( )


signature:
def validate_day_start_ut ( conn ) :
    """This validates the day _ start _ ut of the days table ."""

implementation:
G = GTFS ( conn )
cur = conn . execute ( 'SELECT date, day_start_ut FROM days' )
for date , day_start_ut in cur : # print date , day _ start _ ut
    assert day_start_ut == G . get_day_start_ut ( date )


signature:
def main_make_views ( gtfs_fname ) :
    """Re - create all views ."""

implementation:
print ( "creating views" )
conn = GTFS ( fname_or_conn = gtfs_fname ) . conn
for L in Loaders :
    L ( None ) . make_views ( conn )
conn . commit ( )


signature:
def _validate_table_row_counts ( self ) :
    """Imports source . txt files , checks row counts and then compares the rowcounts with the gtfsobject
 : return :"""

implementation:
for db_table_name in DB_TABLE_NAME_TO_SOURCE_FILE . keys ( ) :
    table_name_source_file = DB_TABLE_NAME_TO_SOURCE_FILE [ db_table_name ]
    row_warning_str = DB_TABLE_NAME_TO_ROWS_MISSING_WARNING [ db_table_name ]
    # Row count in GTFS object :
    database_row_count = self . gtfs . get_row_count ( db_table_name )
    # Row counts in source files :
    source_row_count = 0
    for gtfs_source in self . gtfs_sources :
        frequencies_in_source = source_csv_to_pandas ( gtfs_source , 'frequencies.txt' )
        try :
            if table_name_source_file == 'trips' and not frequencies_in_source . empty :
                source_row_count += self . _frequency_generated_trips_rows ( gtfs_source )
            elif table_name_source_file == 'stop_times' and not frequencies_in_source . empty :
                source_row_count += self . _compute_number_of_frequency_generated_stop_times ( gtfs_source )
            else :
                df = source_csv_to_pandas ( gtfs_source , table_name_source_file )
                source_row_count += len ( df . index )
        except IOError as e :
            if hasattr ( e , "filename" ) and db_table_name in e . filename :
                pass
            else :
                raise e
    if source_row_count == database_row_count and self . verbose :
        print ( "Row counts match for " + table_name_source_file + " between the source and database (" + str ( database_row_count ) + ")" )
    else :
        difference = database_row_count - source_row_count
        ( 'Row counts do not match for ' + str ( table_name_source_file ) + ': (source=' + str ( source_row_count ) + ', database=' + str ( database_row_count ) + ")" )
        if table_name_source_file == "calendar" and difference > 0 :
            query = "SELECT count(*) FROM (SELECT * FROM calendar ORDER BY service_I DESC LIMIT " + str ( int ( difference ) ) + ") WHERE start_date=end_date AND m=0 AND t=0 AND w=0 AND th=0 AND f=0 AND s=0 AND su=0"
            number_of_entries_added_by_calendar_dates_loader = self . gtfs . execute_custom_query ( query ) . fetchone ( ) [ 0 ]
            if number_of_entries_added_by_calendar_dates_loader == difference and self . verbose :
                print ( "    But don't worry, the extra entries seem to just dummy entries due to calendar_dates" )
            else :
                if self . verbose :
                    print ( "    Reason for this is unknown." )
                self . warnings_container . add_warning ( row_warning_str , self . location , difference )
        else :
            self . warnings_container . add_warning ( row_warning_str , self . location , difference )


signature:
def _validate_no_null_values ( self ) :
    """Loads the tables from the gtfs object and counts the number of rows that have null values in
 fields that should not be null . Stores the number of null rows in warnings _ container"""

implementation:
for table in DB_TABLE_NAMES :
    null_not_ok_warning = "Null values in must-have columns in table {table}" . format ( table = table )
    null_warn_warning = "Null values in good-to-have columns in table {table}" . format ( table = table )
    null_not_ok_fields = DB_TABLE_NAME_TO_FIELDS_WHERE_NULL_NOT_OK [ table ]
    null_warn_fields = DB_TABLE_NAME_TO_FIELDS_WHERE_NULL_OK_BUT_WARN [ table ]
    # CW , TODO : make this validation source by source
    df = self . gtfs . get_table ( table )
    for warning , fields in zip ( [ null_not_ok_warning , null_warn_warning ] , [ null_not_ok_fields , null_warn_fields ] ) :
        null_unwanted_df = df [ fields ]
        rows_having_null = null_unwanted_df . isnull ( ) . any ( 1 )
        if sum ( rows_having_null ) > 0 :
            rows_having_unwanted_null = df [ rows_having_null . values ]
            self . warnings_container . add_warning ( warning , rows_having_unwanted_null , len ( rows_having_unwanted_null ) )


signature:
def _validate_danglers ( self ) :
    """Checks for rows that are not referenced in the the tables that should be linked
 stops < > stop _ times using stop _ I
 stop _ times < > trips < > days , using trip _ I
 trips < > routes , using route _ I
 : return :"""

implementation:
for query , warning in zip ( DANGLER_QUERIES , DANGLER_WARNINGS ) :
    dangler_count = self . gtfs . execute_custom_query ( query ) . fetchone ( ) [ 0 ]
    if dangler_count > 0 :
        if self . verbose :
            print ( str ( dangler_count ) + " " + warning )
        self . warnings_container . add_warning ( warning , self . location , count = dangler_count )


signature:
def _frequency_generated_trips_rows ( self , gtfs_soure_path , return_df_freq = False ) :
    """This function calculates the equivalent rowcounts for trips when
 taking into account the generated rows in the gtfs object
 Parameters
 gtfs _ soure _ path : path to the source file
 param txt : txt file in question
 : return : sum of all trips"""

implementation:
df_freq = source_csv_to_pandas ( gtfs_soure_path , 'frequencies' )
df_trips = source_csv_to_pandas ( gtfs_soure_path , "trips" )
df_freq [ 'n_trips' ] = df_freq . apply ( lambda row : len ( range ( str_time_to_day_seconds ( row [ 'start_time' ] ) , str_time_to_day_seconds ( row [ 'end_time' ] ) , row [ 'headway_secs' ] ) ) , axis = 1 )
df_trips_freq = pd . merge ( df_freq , df_trips , how = 'outer' , on = 'trip_id' )
n_freq_generated_trips = int ( df_trips_freq [ 'n_trips' ] . fillna ( 1 ) . sum ( axis = 0 ) )
if return_df_freq :
    return df_trips_freq
else :
    return n_freq_generated_trips


signature:
def _compute_number_of_frequency_generated_stop_times ( self , gtfs_source_path ) :
    """Parameters
 Same as for " _ frequency _ generated _ trips _ rows " but for stop times table
 gtfs _ source _ path :
 table _ name :
 Return"""

implementation:
df_freq = self . _frequency_generated_trips_rows ( gtfs_source_path , return_df_freq = True )
df_stop_times = source_csv_to_pandas ( gtfs_source_path , "stop_times" )
df_stop_freq = pd . merge ( df_freq , df_stop_times , how = 'outer' , on = 'trip_id' )
return int ( df_stop_freq [ 'n_trips' ] . fillna ( 1 ) . sum ( axis = 0 ) )


signature:
def update_pareto_optimal_tuples ( self , new_label ) :
    """Parameters
 new _ label : LabelTime
 Returns
 updated : bool"""

implementation:
assert ( isinstance ( new_label , LabelTime ) )
if self . _labels :
    assert ( new_label . departure_time <= self . _labels [ - 1 ] . departure_time )
    best_later_departing_arrival_time = self . _labels [ - 1 ] . arrival_time_target
else :
    best_later_departing_arrival_time = float ( 'inf' )
walk_to_target_arrival_time = new_label . departure_time + self . _walk_to_target_duration
best_arrival_time = min ( walk_to_target_arrival_time , best_later_departing_arrival_time , new_label . arrival_time_target )
# this should be changed to get constant time insertions / additions
# ( with time - indexing )
if ( new_label . arrival_time_target < walk_to_target_arrival_time and new_label . arrival_time_target < best_later_departing_arrival_time ) :
    self . _labels . append ( LabelTime ( new_label . departure_time , best_arrival_time ) )
    return True
else :
    return False


signature:
def print_coords ( rows , prefix = '' ) :
    """Print coordinates within a sequence .
 This is only used for debugging . Printed in a form that can be
 pasted into Python for visualization ."""

implementation:
lat = [ row [ 'lat' ] for row in rows ]
lon = [ row [ 'lon' ] for row in rows ]
print ( 'COORDS' + '-' * 5 )
print ( "%slat, %slon = %r, %r" % ( prefix , prefix , lat , lon ) )
print ( '-' * 5 )


signature:
def find_segments ( stops , shape ) :
    """Find corresponding shape points for a list of stops and create shape break points .
 Parameters
 stops : stop - sequence ( list )
 List of stop points
 shape : list of shape points
 shape - sequence of shape points
 Returns
 break _ points : list [ int ]
 stops [ i ] corresponds to shape [ break _ points [ i ] ] . This list can
 be used to partition the shape points into segments between
 one stop and the next .
 badness : float
 Lower indicates better fit to the shape . This is the sum of
 distances ( in meters ) between every each stop and its closest
 shape point . This is not needed in normal use , but in the
 cases where you must determine the best - fitting shape for a
 stop - sequence , use this ."""

implementation:
if not shape :
    return [ ] , 0
break_points = [ ]
last_i = 0
cumul_d = 0
badness = 0
d_last_stop = float ( 'inf' )
lstlat , lstlon = None , None
break_shape_points = [ ]
for stop in stops :
    stlat , stlon = stop [ 'lat' ] , stop [ 'lon' ]
    best_d = float ( 'inf' )
    # print stop
    if badness > 500 and badness > 30 * len ( break_points ) :
        return [ ] , badness
    for i in range ( last_i , len ( shape ) ) :
        d = wgs84_distance ( stlat , stlon , shape [ i ] [ 'lat' ] , shape [ i ] [ 'lon' ] )
        if lstlat :
            d_last_stop = wgs84_distance ( lstlat , lstlon , shape [ i ] [ 'lat' ] , shape [ i ] [ 'lon' ] )
        # If we are getting closer to next stop , record this as
        # the best stop so far . continue
        if d < best_d :
            best_d = d
            best_i = i
            # print best _ d , i , last _ i , len ( shape )
            cumul_d += d
        # We have to be very careful about our stop condition .
        # This is trial and error , basically .
        if ( d_last_stop < d ) or ( d > 500 ) or ( i < best_i + 100 ) :
            continue
        # We have decided our best stop , stop looking and continue
        # the outer loop .
        else :
            badness += best_d
            break_points . append ( best_i )
            last_i = best_i
            lstlat , lstlon = stlat , stlon
            break_shape_points . append ( shape [ best_i ] )
            break
    else : # Executed if we did * not * break the inner loop
        badness += best_d
        break_points . append ( best_i )
        last_i = best_i
        lstlat , lstlon = stlat , stlon
        break_shape_points . append ( shape [ best_i ] )
        pass
# print " Badness : " , badness
# print _ coords ( stops , ' stop ' )
# print _ coords ( shape , ' shape ' )
# print _ coords ( break _ shape _ points , ' break ' )
return break_points , badness


signature:
def find_best_segments ( cur , stops , shape_ids , route_id = None , breakpoints_cache = None ) :
    """Finds the best shape _ id for a stop - sequence .
 This is used in cases like when you have GPS data with a route
 name , but you don ' t know the route direction . It tries shapes
 going both directions and returns the shape that best matches .
 Could be used in other cases as well .
 Parameters
 cur : sqlite3 . Cursor
 database cursor
 stops : list
 shape _ ids : list of shape _ id : s
 route _ id : route _ id to search for stops
 breakpoints _ cache : dict
 If given , use this to cache results from this function ."""

implementation:
cache_key = None
if breakpoints_cache is not None : # Calculate a cache key for this sequence . If shape _ id and
# all stop _ Is are the same , then we assume that it is the same
# route and re - use existing breakpoints .
    cache_key = ( route_id , tuple ( x [ 'stop_I' ] for x in stops ) )
    if cache_key in breakpoints_cache :
        print ( 'found in cache' )
        return breakpoints_cache [ cache_key ]
if route_id is not None :
    cur . execute ( '''SELECT DISTINCT shape_id
                        FROM routes
                        LEFT JOIN trips
                        USING (route_I)
                        WHERE route_id=?''' , ( route_id , ) )
    data = cur . fetchall ( )
    # If not data , then route _ id didn ' t match anything , or there
    # were no shapes defined . We have to exit in this case .
    if not data :
        print ( "No data for route_id=%s" % route_id )
        return [ ] , None , None , None
    shape_ids = zip ( * data ) [ 0 ]
# print ' find _ best _ segments : ' , shape _ ids
results = [ ]
for shape_id in shape_ids :
    shape = get_shape_points ( cur , shape_id )
    breakpoints , badness = find_segments ( stops , shape )
    results . append ( [ badness , breakpoints , shape , shape_id ] )
    if len ( stops ) > 5 and badness < 5 * ( len ( stops ) ) :
        break
best = np . argmin ( zip ( * results ) [ 0 ] )
# print ' best ' , best
badness = results [ best ] [ 0 ]
breakpoints = results [ best ] [ 1 ]
shape = results [ best ] [ 2 ]
shape_id = results [ best ] [ 3 ]
if breakpoints_cache is not None :
    print ( "storing in cache" , cache_key [ 0 ] , hash ( cache_key [ 1 : ] ) )
    breakpoints_cache [ cache_key ] = breakpoints , badness , shape , shape_id
return breakpoints , badness , shape , shape_id


signature:
def return_segments ( shape , break_points ) :
    """Break a shape into segments between stops using break _ points .
 This function can use the ` break _ points ` outputs from
 ` find _ segments ` , and cuts the shape - sequence into pieces
 corresponding to each stop ."""

implementation:
# print ' xxx '
# print stops
# print shape
# print break _ points
# assert len ( stops ) = = len ( break _ points )
segs = [ ]
bp = 0
# not used
bp2 = 0
for i in range ( len ( break_points ) - 1 ) :
    bp = break_points [ i ] if break_points [ i ] is not None else bp2
    bp2 = break_points [ i + 1 ] if break_points [ i + 1 ] is not None else bp
    segs . append ( shape [ bp : bp2 + 1 ] )
segs . append ( [ ] )
return segs


signature:
def gen_cumulative_distances ( stops ) :
    """Add a ' d ' key for distances to a stop / shape - sequence .
 This takes a shape - sequence or stop - sequence , and adds an extra
 ' d ' key that is cumulative , geographic distances between each
 point . This uses ` wgs84 _ distance ` from the util module . The
 distances are in meters . Distances are rounded to the nearest
 integer , because otherwise JSON size increases greatly .
 Parameters
 stops : list
 elements are dicts with ' lat ' and ' lon ' keys
 and the function adds the ' d ' key ( ' d ' stands for distance )
 to the dictionaries"""

implementation:
stops [ 0 ] [ 'd' ] = 0.0
for i in range ( 1 , len ( stops ) ) :
    stops [ i ] [ 'd' ] = stops [ i - 1 ] [ 'd' ] + wgs84_distance ( stops [ i - 1 ] [ 'lat' ] , stops [ i - 1 ] [ 'lon' ] , stops [ i ] [ 'lat' ] , stops [ i ] [ 'lon' ] , )
for stop in stops :
    stop [ 'd' ] = int ( stop [ 'd' ] )


signature:
def get_shape_points ( cur , shape_id ) :
    """Given a shape _ id , return its shape - sequence .
 Parameters
 cur : sqlite3 . Cursor
 cursor to a GTFS database
 shape _ id : str
 id of the route
 Returns
 shape _ points : list
 elements are dictionaries containing the ' seq ' , ' lat ' , and ' lon ' of the shape"""

implementation:
cur . execute ( '''SELECT seq, lat, lon, d FROM shapes where shape_id=?
                    ORDER BY seq''' , ( shape_id , ) )
shape_points = [ dict ( seq = row [ 0 ] , lat = row [ 1 ] , lon = row [ 2 ] , d = row [ 3 ] ) for row in cur ]
return shape_points


signature:
def get_shape_points2 ( cur , shape_id ) :
    """Given a shape _ id , return its shape - sequence ( as a dict of lists ) .
 get _ shape _ points function returns them as a list of dicts
 Parameters
 cur : sqlite3 . Cursor
 cursor to a GTFS database
 shape _ id : str
 id of the route
 Returns
 shape _ points : dict of lists
 dict contains keys ' seq ' , ' lat ' , ' lon ' , and ' d ' ( istance ) of the shape"""

implementation:
cur . execute ( '''SELECT seq, lat, lon, d FROM shapes where shape_id=?
                    ORDER BY seq''' , ( shape_id , ) )
shape_points = { 'seqs' : [ ] , 'lats' : [ ] , 'lons' : [ ] , 'd' : [ ] }
for row in cur :
    shape_points [ 'seqs' ] . append ( row [ 0 ] )
    shape_points [ 'lats' ] . append ( row [ 1 ] )
    shape_points [ 'lons' ] . append ( row [ 2 ] )
    shape_points [ 'd' ] . append ( row [ 3 ] )
return shape_points


signature:
def get_route_shape_segments ( cur , route_id ) :
    """Given a route _ id , return its stop - sequence .
 Parameters
 cur : sqlite3 . Cursor
 cursor to a GTFS database
 route _ id : str
 id of the route
 Returns
 shape _ points : list
 elements are dictionaries containing the ' seq ' , ' lat ' , and ' lon ' of the shape"""

implementation:
cur . execute ( '''SELECT seq, lat, lon
                    FROM (
                        SELECT shape_id
                        FROM route
                        LEFT JOIN trips
                        USING (route_I)
                        WHERE route_id=? limit 1
                        )
                    JOIN shapes
                    USING (shape_id)
                    ORDER BY seq''' , ( route_id , ) )
shape_points = [ dict ( seq = row [ 0 ] , lat = row [ 1 ] , lon = row [ 2 ] ) for row in cur ]
return shape_points


signature:
def get_shape_between_stops ( cur , trip_I , seq_stop1 = None , seq_stop2 = None , shape_breaks = None ) :
    """Given a trip _ I ( shortened id ) , return shape points between two stops
 ( seq _ stop1 and seq _ stop2 ) .
 Trip _ I is used for matching obtaining the full shape of one trip ( route ) .
 From the resulting shape we then obtain only shape points between
 stop _ seq1 and stop _ seq2
 trip _ I - - - ( trips ) - - - > shape _ id
 trip _ I , seq _ stop1 - - - - ( stop _ times ) - - - > shape _ break1
 trip _ I , seq _ stop2 - - - - ( stop _ times ) - - - > shape _ break2
 shapes _ id + shape _ break1 + shape _ break2 - - ( shapes ) - - > result
 Parameters
 cur : sqlite3 . Cursor
 cursor to sqlite3 DB containing GTFS
 trip _ I : int
 transformed trip _ id ( i . e . a new column that is created when
 GTFS is imported to a DB )
 seq _ stop1 : int
 a positive inger describing the index of the point of the shape that
 corresponds to the first stop
 seq _ stop2 : int
 a positive inger describing the index of the point of the shape that
 corresponds to the second stop
 shape _ breaks : ? ?
 Returns
 shapedict : dict
 Dictionary containing the latitudes and longitudes :
 lats = shapedict [ ' lat ' ]
 lons = shapedict [ ' lon ' ]"""

implementation:
assert ( seq_stop1 and seq_stop2 ) or shape_breaks
if not shape_breaks :
    shape_breaks = [ ]
    for seq_stop in [ seq_stop1 , seq_stop2 ] :
        query = """SELECT shape_break FROM stop_times
                        WHERE trip_I=%d AND seq=%d
                    """ % ( trip_I , seq_stop )
        for row in cur . execute ( query ) :
            shape_breaks . append ( row [ 0 ] )
assert len ( shape_breaks ) == 2
query = """SELECT seq, lat, lon
                FROM (SELECT shape_id FROM trips WHERE trip_I=%d)
                JOIN shapes USING (shape_id)
                WHERE seq>=%d AND seq <= %d;
            """ % ( trip_I , shape_breaks [ 0 ] , shape_breaks [ 1 ] )
shapedict = { 'lat' : [ ] , 'lon' : [ ] , 'seq' : [ ] }
for row in cur . execute ( query ) :
    shapedict [ 'seq' ] . append ( row [ 0 ] )
    shapedict [ 'lat' ] . append ( row [ 1 ] )
    shapedict [ 'lon' ] . append ( row [ 2 ] )
return shapedict


signature:
def get_trip_points ( cur , route_id , offset = 0 , tripid_glob = '' ) :
    """Get all scheduled stops on a particular route _ id .
 Given a route _ id , return the trip - stop - list with
 latitude / longitudes . This is a bit more tricky than it seems ,
 because we have to go from table route - > trips - > stop _ times . This
 functions finds an arbitrary trip ( in trip table ) with this route ID
 and , and then returns all stop points for that trip .
 Parameters
 cur : sqlite3 . Cursor
 cursor to sqlite3 DB containing GTFS
 route _ id : string or any
 route _ id to get stop points of
 offset : int
 LIMIT offset if you don ' t want the first trip returned .
 tripid _ glob : string
 If given , allows you to limit tripids which can be selected .
 Mainly useful in debugging .
 Returns
 stop - list
 List of stops in stop - seq format ."""

implementation:
extra_where = ''
if tripid_glob :
    extra_where = "AND trip_id GLOB '%s'" % tripid_glob
cur . execute ( 'SELECT seq, lat, lon ' 'FROM (select trip_I from route ' '      LEFT JOIN trips USING (route_I) ' '      WHERE route_id=? %s limit 1 offset ? ) ' 'JOIN stop_times USING (trip_I) ' 'LEFT JOIN stop USING (stop_id) ' 'ORDER BY seq' % extra_where , ( route_id , offset ) )
stop_points = [ dict ( seq = row [ 0 ] , lat = row [ 1 ] , lon = row [ 2 ] ) for row in cur ]
return stop_points


signature:
def interpolate_shape_times ( shape_distances , shape_breaks , stop_times ) :
    """Interpolate passage times for shape points .
 Parameters
 shape _ distances : list
 list of cumulative distances along the shape
 shape _ breaks : list
 list of shape _ breaks
 stop _ times : list
 list of stop _ times
 Returns
 shape _ times : list of ints ( seconds ) / numpy array
 interpolated shape passage times
 The values of stop times before the first shape - break are given the first
 stopping time , and the any shape points after the last break point are
 given the value of the last shape point ."""

implementation:
shape_times = np . zeros ( len ( shape_distances ) )
shape_times [ : shape_breaks [ 0 ] ] = stop_times [ 0 ]
for i in range ( len ( shape_breaks ) - 1 ) :
    cur_break = shape_breaks [ i ]
    cur_time = stop_times [ i ]
    next_break = shape_breaks [ i + 1 ]
    next_time = stop_times [ i + 1 ]
    if cur_break == next_break :
        shape_times [ cur_break ] = stop_times [ i ]
    else :
        cur_distances = shape_distances [ cur_break : next_break + 1 ]
        norm_distances = ( ( np . array ( cur_distances ) - float ( cur_distances [ 0 ] ) ) / float ( cur_distances [ - 1 ] - cur_distances [ 0 ] ) )
        times = ( 1. - norm_distances ) * cur_time + norm_distances * next_time
        shape_times [ cur_break : next_break ] = times [ : - 1 ]
# deal final ones separately :
shape_times [ shape_breaks [ - 1 ] : ] = stop_times [ - 1 ]
return list ( shape_times )


signature:
def update_pareto_optimal_tuples ( self , new_pareto_tuple ) :
    """# this function should be optimized
 Parameters
 new _ pareto _ tuple : LabelTimeSimple
 Returns
 added : bool
 whether new _ pareto _ tuple was added to the set of pareto - optimal tuples"""

implementation:
if new_pareto_tuple . duration ( ) > self . _walk_to_target_duration :
    direct_walk_label = self . _label_class . direct_walk_label ( new_pareto_tuple . departure_time , self . _walk_to_target_duration )
    if not direct_walk_label . dominates ( new_pareto_tuple ) :
        raise
direct_walk_label = self . _label_class . direct_walk_label ( new_pareto_tuple . departure_time , self . _walk_to_target_duration )
if direct_walk_label . dominates ( new_pareto_tuple ) :
    return False
if self . _new_paretotuple_is_dominated_by_old_tuples ( new_pareto_tuple ) :
    return False
else :
    self . _remove_old_tuples_dominated_by_new_and_insert_new_paretotuple ( new_pareto_tuple )
    return True


signature:
def evaluate_earliest_arrival_time_at_target ( self , dep_time , transfer_margin ) :
    """Get the earliest arrival time at the target , given a departure time .
 Parameters
 dep _ time : float , int
 time in unix seconds
 transfer _ margin : float , int
 transfer margin in seconds
 Returns
 arrival _ time : float
 Arrival time in the given time unit ( seconds after unix epoch ) ."""

implementation:
minimum = dep_time + self . _walk_to_target_duration
dep_time_plus_transfer_margin = dep_time + transfer_margin
for label in self . _labels :
    if label . departure_time >= dep_time_plus_transfer_margin and label . arrival_time_target < minimum :
        minimum = label . arrival_time_target
return float ( minimum )


signature:
def _run ( self ) :
    """Run the actual simulation ."""

implementation:
if self . _has_run :
    raise RuntimeError ( "This spreader instance has already been run: " "create a new Spreader object for a new run." )
i = 1
while self . event_heap . size ( ) > 0 and len ( self . _uninfected_stops ) > 0 :
    event = self . event_heap . pop_next_event ( )
    this_stop = self . _stop_I_to_spreading_stop [ event . from_stop_I ]
    if event . arr_time_ut > self . start_time_ut + self . max_duration_ut :
        break
    if this_stop . can_infect ( event ) :
        target_stop = self . _stop_I_to_spreading_stop [ event . to_stop_I ]
        already_visited = target_stop . has_been_visited ( )
        target_stop . visit ( event )
        if not already_visited :
            self . _uninfected_stops . remove ( event . to_stop_I )
            print ( i , self . event_heap . size ( ) )
            transfer_distances = self . gtfs . get_straight_line_transfer_distances ( event . to_stop_I )
            self . event_heap . add_walk_events_to_heap ( transfer_distances , event , self . start_time_ut , self . walk_speed , self . _uninfected_stops , self . max_duration_ut )
            i += 1
self . _has_run = True


signature:
def add_walk_distances_to_db_python ( gtfs , osm_path , cutoff_distance_m = 1000 ) :
    """Computes the walk paths between stops , and updates these to the gtfs database .
 Parameters
 gtfs : gtfspy . GTFS or str
 A GTFS object or a string representation .
 osm _ path : str
 path to the OpenStreetMap file
 cutoff _ distance _ m : number
 maximum allowed distance in meters
 Returns
 None
 See Also
 gtfspy . calc _ transfers
 compute _ walk _ paths _ java"""

implementation:
if isinstance ( gtfs , str ) :
    gtfs = GTFS ( gtfs )
assert ( isinstance ( gtfs , GTFS ) )
print ( "Reading in walk network" )
walk_network = create_walk_network_from_osm ( osm_path )
print ( "Matching stops to the OSM network" )
stop_I_to_nearest_osm_node , stop_I_to_nearest_osm_node_distance = match_stops_to_nodes ( gtfs , walk_network )
transfers = gtfs . get_straight_line_transfer_distances ( )
from_I_to_to_stop_Is = { stop_I : set ( ) for stop_I in stop_I_to_nearest_osm_node }
for transfer_tuple in transfers . itertuples ( ) :
    from_I = transfer_tuple . from_stop_I
    to_I = transfer_tuple . to_stop_I
    from_I_to_to_stop_Is [ from_I ] . add ( to_I )
print ( "Computing walking distances" )
for from_I , to_stop_Is in from_I_to_to_stop_Is . items ( ) :
    from_node = stop_I_to_nearest_osm_node [ from_I ]
    from_dist = stop_I_to_nearest_osm_node_distance [ from_I ]
    shortest_paths = networkx . single_source_dijkstra_path_length ( walk_network , from_node , cutoff = cutoff_distance_m - from_dist , weight = "distance" )
    for to_I in to_stop_Is :
        to_distance = stop_I_to_nearest_osm_node_distance [ to_I ]
        to_node = stop_I_to_nearest_osm_node [ to_I ]
        osm_distance = shortest_paths . get ( to_node , float ( 'inf' ) )
        total_distance = from_dist + osm_distance + to_distance
        from_stop_I_transfers = transfers [ transfers [ 'from_stop_I' ] == from_I ]
        straigth_distance = from_stop_I_transfers [ from_stop_I_transfers [ "to_stop_I" ] == to_I ] [ "d" ] . values [ 0 ]
        assert ( straigth_distance < total_distance + 2 )
        # allow for a maximum of 2 meters in calculations
        if total_distance <= cutoff_distance_m :
            gtfs . conn . execute ( "UPDATE stop_distances " "SET d_walk = " + str ( int ( total_distance ) ) + " WHERE from_stop_I=" + str ( from_I ) + " AND to_stop_I=" + str ( to_I ) )
gtfs . conn . commit ( )


signature:
def match_stops_to_nodes ( gtfs , walk_network ) :
    """Parameters
 gtfs : a GTFS object
 walk _ network : networkx . Graph
 Returns
 stop _ I _ to _ node : dict
 maps stop _ I to closest walk _ network node
 stop _ I _ to _ dist : dict
 maps stop _ I to the distance to the closest walk _ network node"""

implementation:
network_nodes = walk_network . nodes ( data = "true" )
stop_Is = set ( gtfs . get_straight_line_transfer_distances ( ) [ 'from_stop_I' ] )
stops_df = gtfs . stops ( )
geo_index = GeoGridIndex ( precision = 6 )
for net_node , data in network_nodes :
    geo_index . add_point ( GeoPoint ( data [ 'lat' ] , data [ 'lon' ] , ref = net_node ) )
stop_I_to_node = { }
stop_I_to_dist = { }
for stop_I in stop_Is :
    stop_lat = float ( stops_df [ stops_df . stop_I == stop_I ] . lat )
    stop_lon = float ( stops_df [ stops_df . stop_I == stop_I ] . lon )
    geo_point = GeoPoint ( stop_lat , stop_lon )
    min_dist = float ( 'inf' )
    min_dist_node = None
    search_distances_m = [ 0.100 , 0.500 ]
    for search_distance_m in search_distances_m :
        for point , distance in geo_index . get_nearest_points ( geo_point , search_distance_m , "km" ) :
            if distance < min_dist :
                min_dist = distance * 1000
                min_dist_node = point . ref
        if min_dist_node is not None :
            break
    if min_dist_node is None :
        warn ( "No OSM node found for stop: " + str ( stops_df [ stops_df . stop_I == stop_I ] ) )
    stop_I_to_node [ stop_I ] = min_dist_node
    stop_I_to_dist [ stop_I ] = min_dist
return stop_I_to_node , stop_I_to_dist


signature:
def walk_transfer_stop_to_stop_network ( gtfs , max_link_distance = None ) :
    """Construct the walk network .
 If OpenStreetMap - based walking distances have been computed , then those are used as the distance .
 Otherwise , the great circle distances ( " d " ) is used .
 Parameters
 gtfs : gtfspy . GTFS
 max _ link _ distance : int , optional
 If given , all walking transfers with great circle distance longer
 than this limit ( expressed in meters ) will be omitted .
 Returns
 net : networkx . DiGraph
 edges have attributes
 straight - line distance between stops
 d _ walk :
 distance along the road / tracks / . ."""

implementation:
if max_link_distance is None :
    max_link_distance = 1000
net = networkx . Graph ( )
_add_stops_to_net ( net , gtfs . get_table ( "stops" ) )
stop_distances = gtfs . get_table ( "stop_distances" )
if stop_distances [ "d_walk" ] [ 0 ] is None :
    osm_distances_available = False
    warn ( "Warning: OpenStreetMap-based walking distances have not been computed, using euclidean distances instead." "Ignore this warning if running unit tests." )
else :
    osm_distances_available = True
for stop_distance_tuple in stop_distances . itertuples ( ) :
    from_node = stop_distance_tuple . from_stop_I
    to_node = stop_distance_tuple . to_stop_I
    if osm_distances_available :
        if stop_distance_tuple . d_walk > max_link_distance or isnan ( stop_distance_tuple . d_walk ) :
            continue
        data = { 'd' : stop_distance_tuple . d , 'd_walk' : stop_distance_tuple . d_walk }
    else :
        if stop_distance_tuple . d > max_link_distance :
            continue
        data = { 'd' : stop_distance_tuple . d }
    net . add_edge ( from_node , to_node , data )
return net


signature:
def stop_to_stop_network_for_route_type ( gtfs , route_type , link_attributes = None , start_time_ut = None , end_time_ut = None ) :
    """Get a stop - to - stop network describing a single mode of travel .
 Parameters
 gtfs : gtfspy . GTFS
 route _ type : int
 See gtfspy . route _ types . TRANSIT _ ROUTE _ TYPES for the list of possible types .
 link _ attributes : list [ str ] , optional
 defaulting to use the following link attributes :
 " n _ vehicles " : Number of vehicles passed
 " duration _ min " : minimum travel time between stops
 " duration _ max " : maximum travel time between stops
 " duration _ median " : median travel time between stops
 " duration _ avg " : average travel time between stops
 " d " : distance along straight line ( wgs84 _ distance )
 " distance _ shape " : minimum distance along shape
 " capacity _ estimate " : approximate capacity passed through the stop
 " route _ I _ counts " : dict from route _ I to counts
 start _ time _ ut : int
 start time of the time span ( in unix time )
 end _ time _ ut : int
 end time of the time span ( in unix time )
 Returns
 net : networkx . DiGraph
 A directed graph Directed graph"""

implementation:
if link_attributes is None :
    link_attributes = DEFAULT_STOP_TO_STOP_LINK_ATTRIBUTES
assert ( route_type in route_types . TRANSIT_ROUTE_TYPES )
stops_dataframe = gtfs . get_stops_for_route_type ( route_type )
net = networkx . DiGraph ( )
_add_stops_to_net ( net , stops_dataframe )
events_df = gtfs . get_transit_events ( start_time_ut = start_time_ut , end_time_ut = end_time_ut , route_type = route_type )
if len ( net . nodes ( ) ) < 2 :
    assert events_df . shape [ 0 ] == 0
# group events by links , and loop over them ( i . e . each link ) :
link_event_groups = events_df . groupby ( [ 'from_stop_I' , 'to_stop_I' ] , sort = False )
for key , link_events in link_event_groups :
    from_stop_I , to_stop_I = key
    assert isinstance ( link_events , pd . DataFrame )
    # ' dep _ time _ ut ' ' arr _ time _ ut ' ' shape _ id ' ' route _ type ' ' trip _ I ' ' duration ' ' from _ seq ' ' to _ seq '
    if link_attributes is None :
        net . add_edge ( from_stop_I , to_stop_I )
    else :
        link_data = { }
        if "duration_min" in link_attributes :
            link_data [ 'duration_min' ] = float ( link_events [ 'duration' ] . min ( ) )
        if "duration_max" in link_attributes :
            link_data [ 'duration_max' ] = float ( link_events [ 'duration' ] . max ( ) )
        if "duration_median" in link_attributes :
            link_data [ 'duration_median' ] = float ( link_events [ 'duration' ] . median ( ) )
        if "duration_avg" in link_attributes :
            link_data [ 'duration_avg' ] = float ( link_events [ 'duration' ] . mean ( ) )
        # statistics on numbers of vehicles :
        if "n_vehicles" in link_attributes :
            link_data [ 'n_vehicles' ] = int ( link_events . shape [ 0 ] )
        if "capacity_estimate" in link_attributes :
            link_data [ 'capacity_estimate' ] = route_types . ROUTE_TYPE_TO_APPROXIMATE_CAPACITY [ route_type ] * int ( link_events . shape [ 0 ] )
        if "d" in link_attributes :
            from_lat = net . node [ from_stop_I ] [ 'lat' ]
            from_lon = net . node [ from_stop_I ] [ 'lon' ]
            to_lat = net . node [ to_stop_I ] [ 'lat' ]
            to_lon = net . node [ to_stop_I ] [ 'lon' ]
            distance = wgs84_distance ( from_lat , from_lon , to_lat , to_lon )
            link_data [ 'd' ] = int ( distance )
        if "distance_shape" in link_attributes :
            assert "shape_id" in link_events . columns . values
            found = None
            for i , shape_id in enumerate ( link_events [ "shape_id" ] . values ) :
                if shape_id is not None :
                    found = i
                    break
            if found is None :
                link_data [ "distance_shape" ] = None
            else :
                link_event = link_events . iloc [ found ]
                distance = gtfs . get_shape_distance_between_stops ( link_event [ "trip_I" ] , int ( link_event [ "from_seq" ] ) , int ( link_event [ "to_seq" ] ) )
                link_data [ 'distance_shape' ] = distance
        if "route_I_counts" in link_attributes :
            link_data [ "route_I_counts" ] = link_events . groupby ( "route_I" ) . size ( ) . to_dict ( )
        net . add_edge ( from_stop_I , to_stop_I , attr_dict = link_data )
return net


signature:
def stop_to_stop_networks_by_type ( gtfs ) :
    """Compute stop - to - stop networks for all travel modes ( route _ types ) .
 Parameters
 gtfs : gtfspy . GTFS
 Returns
 dict : dict [ int , networkx . DiGraph ]
 keys should be one of route _ types . ALL _ ROUTE _ TYPES ( i . e . GTFS route _ types )"""

implementation:
route_type_to_network = dict ( )
for route_type in route_types . ALL_ROUTE_TYPES :
    if route_type == route_types . WALK :
        net = walk_transfer_stop_to_stop_network ( gtfs )
    else :
        net = stop_to_stop_network_for_route_type ( gtfs , route_type )
    route_type_to_network [ route_type ] = net
assert len ( route_type_to_network ) == len ( route_types . ALL_ROUTE_TYPES )
return route_type_to_network


signature:
def combined_stop_to_stop_transit_network ( gtfs , start_time_ut = None , end_time_ut = None ) :
    """Compute stop - to - stop networks for all travel modes and combine them into a single network .
 The modes of transport are encoded to a single network .
 The network consists of multiple links corresponding to each travel mode .
 Walk mode is not included .
 Parameters
 gtfs : gtfspy . GTFS
 Returns
 net : networkx . MultiDiGraph
 keys should be one of route _ types . TRANSIT _ ROUTE _ TYPES ( i . e . GTFS route _ types )"""

implementation:
multi_di_graph = networkx . MultiDiGraph ( )
for route_type in route_types . TRANSIT_ROUTE_TYPES :
    graph = stop_to_stop_network_for_route_type ( gtfs , route_type , start_time_ut = start_time_ut , end_time_ut = end_time_ut )
    for from_node , to_node , data in graph . edges ( data = True ) :
        data [ 'route_type' ] = route_type
    multi_di_graph . add_edges_from ( graph . edges ( data = True ) )
    multi_di_graph . add_nodes_from ( graph . nodes ( data = True ) )
return multi_di_graph


signature:
def _add_stops_to_net ( net , stops ) :
    """Add nodes to the network from the pandas dataframe describing ( a part of the ) stops table in the GTFS database .
 Parameters
 net : networkx . Graph
 stops : pandas . DataFrame"""

implementation:
for stop in stops . itertuples ( ) :
    data = { "lat" : stop . lat , "lon" : stop . lon , "name" : stop . name }
    net . add_node ( stop . stop_I , data )


signature:
def temporal_network ( gtfs , start_time_ut = None , end_time_ut = None , route_type = None ) :
    """Compute the temporal network of the data , and return it as a pandas . DataFrame
 Parameters
 gtfs : gtfspy . GTFS
 start _ time _ ut : int | None
 start time of the time span ( in unix time )
 end _ time _ ut : int | None
 end time of the time span ( in unix time )
 route _ type : int | None
 Specifies which mode of public transport are included , or whether all modes should be included .
 The int should be one of the standard GTFS route _ types :
 ( see also gtfspy . route _ types . TRANSIT _ ROUTE _ TYPES )
 If route _ type is not specified , all modes are included .
 Returns
 events _ df : pandas . DataFrame
 Columns : departure _ stop , arrival _ stop , departure _ time _ ut , arrival _ time _ ut , route _ type , route _ I , trip _ I"""

implementation:
events_df = gtfs . get_transit_events ( start_time_ut = start_time_ut , end_time_ut = end_time_ut , route_type = route_type )
events_df . drop ( 'to_seq' , 1 , inplace = True )
events_df . drop ( 'shape_id' , 1 , inplace = True )
events_df . drop ( 'duration' , 1 , inplace = True )
events_df . drop ( 'route_id' , 1 , inplace = True )
events_df . rename ( columns = { 'from_seq' : "seq" } , inplace = True )
return events_df


signature:
def route_to_route_network ( gtfs , walking_threshold , start_time , end_time ) :
    """Creates networkx graph where the nodes are bus routes and a edge indicates that there is a possibility to transfer
 between the routes
 : param gtfs :
 : param walking _ threshold :
 : param start _ time :
 : param end _ time :
 : return :"""

implementation:
graph = networkx . Graph ( )
routes = gtfs . get_table ( "routes" )
for i in routes . itertuples ( ) :
    graph . add_node ( i . route_id , attr_dict = { "type" : i . type , "color" : route_types . ROUTE_TYPE_TO_COLOR [ i . type ] } )
query = """SELECT stop1.route_id AS route_id1, stop1.type, stop2.route_id AS route_id2, stop2.type FROM
                (SELECT * FROM stop_distances WHERE d_walk < %s) sd,
                (SELECT * FROM stop_times, trips, routes
                WHERE stop_times.trip_I=trips.trip_I AND trips.route_I=routes.route_I
                AND stop_times.dep_time_ds > %s AND stop_times.dep_time_ds < %s) stop1,
                (SELECT * FROM stop_times, trips, routes
                WHERE stop_times.trip_I=trips.trip_I AND trips.route_I=routes.route_I
                AND stop_times.dep_time_ds > %s AND stop_times.dep_time_ds < %s) stop2
                WHERE sd.from_stop_I = stop1.stop_I AND sd.to_stop_I = stop2.stop_I AND stop1.route_id != stop2.route_id
                GROUP BY stop1.route_id, stop2.route_id""" % ( walking_threshold , start_time , end_time , start_time , end_time )
df = gtfs . execute_custom_query_pandas ( query )
for items in df . itertuples ( ) :
    graph . add_edge ( items . route_id1 , items . route_id2 )
graph . remove_nodes_from ( networkx . isolates ( graph ) )
return graph


signature:
def mean_temporal_distance ( self ) :
    """Get mean temporal distance ( in seconds ) to the target .
 Returns
 mean _ temporal _ distance : float"""

implementation:
total_width = self . end_time_dep - self . start_time_dep
total_area = sum ( [ block . area ( ) for block in self . _profile_blocks ] )
return total_area / total_width


signature:
def plot_temporal_distance_cdf ( self ) :
    """Plot the temporal distance cumulative density function .
 Returns
 fig : matplotlib . Figure"""

implementation:
xvalues , cdf = self . profile_block_analyzer . _temporal_distance_cdf ( )
fig = plt . figure ( )
ax = fig . add_subplot ( 111 )
xvalues = numpy . array ( xvalues ) / 60.0
ax . plot ( xvalues , cdf , "-k" )
ax . fill_between ( xvalues , cdf , color = "red" , alpha = 0.2 )
ax . set_ylabel ( "CDF(t)" )
ax . set_xlabel ( "Temporal distance t (min)" )
return fig


signature:
def plot_temporal_distance_pdf ( self , use_minutes = True , color = "green" , ax = None ) :
    """Plot the temporal distance probability density function .
 Returns
 fig : matplotlib . Figure"""

implementation:
from matplotlib import pyplot as plt
plt . rc ( 'text' , usetex = True )
temporal_distance_split_points_ordered , densities , delta_peaks = self . _temporal_distance_pdf ( )
xs = [ ]
for i , x in enumerate ( temporal_distance_split_points_ordered ) :
    xs . append ( x )
    xs . append ( x )
xs = numpy . array ( xs )
ys = [ 0 ]
for y in densities :
    ys . append ( y )
    ys . append ( y )
ys . append ( 0 )
ys = numpy . array ( ys )
# convert data to minutes :
xlabel = "Temporal distance (s)"
ylabel = "Probability density (t)"
if use_minutes :
    xs /= 60.0
    ys *= 60.0
    xlabel = "Temporal distance (min)"
    delta_peaks = { peak / 60.0 : mass for peak , mass in delta_peaks . items ( ) }
if ax is None :
    fig = plt . figure ( )
    ax = fig . add_subplot ( 111 )
ax . plot ( xs , ys , "k-" )
ax . fill_between ( xs , ys , color = "green" , alpha = 0.2 )
if delta_peaks :
    peak_height = max ( ys ) * 1.4
    max_x = max ( xs )
    min_x = min ( xs )
    now_max_x = max ( xs ) + 0.3 * ( max_x - min_x )
    now_min_x = min_x - 0.1 * ( max_x - min_x )
    text_x_offset = 0.1 * ( now_max_x - max_x )
    for loc , mass in delta_peaks . items ( ) :
        ax . plot ( [ loc , loc ] , [ 0 , peak_height ] , color = "green" , lw = 5 )
        ax . text ( loc + text_x_offset , peak_height * 0.99 , "$P(\\mathrm{walk}) = %.2f$" % ( mass ) , color = "green" )
    ax . set_xlim ( now_min_x , now_max_x )
    tot_delta_peak_mass = sum ( delta_peaks . values ( ) )
    transit_text_x = ( min_x + max_x ) / 2
    transit_text_y = min ( ys [ ys > 0 ] ) / 2.
    ax . text ( transit_text_x , transit_text_y , "$P(mathrm{PT}) = %.2f$" % ( 1 - tot_delta_peak_mass ) , color = "green" , va = "center" , ha = "center" )
ax . set_xlabel ( xlabel )
ax . set_ylabel ( ylabel )
ax . set_ylim ( bottom = 0 )
return ax . figure


signature:
def plot_temporal_distance_pdf_horizontal ( self , use_minutes = True , color = "green" , ax = None , duration_divider = 60.0 , legend_font_size = None , legend_loc = None ) :
    """Plot the temporal distance probability density function .
 Returns
 fig : matplotlib . Figure"""

implementation:
from matplotlib import pyplot as plt
plt . rc ( 'text' , usetex = True )
if ax is None :
    fig = plt . figure ( )
    ax = fig . add_subplot ( 111 )
temporal_distance_split_points_ordered , densities , delta_peaks = self . _temporal_distance_pdf ( )
xs = [ ]
for i , x in enumerate ( temporal_distance_split_points_ordered ) :
    xs . append ( x )
    xs . append ( x )
xs = numpy . array ( xs )
ys = [ 0 ]
for y in densities :
    ys . append ( y )
    ys . append ( y )
ys . append ( 0 )
ys = numpy . array ( ys )
# convert data to minutes :
xlabel = "Temporal distance (s)"
ylabel = "Probability density $P(\\tau)$"
if use_minutes :
    xs /= duration_divider
    ys *= duration_divider
    xlabel = "Temporal distance (min)"
    delta_peaks = { peak / 60.0 : mass for peak , mass in delta_peaks . items ( ) }
if delta_peaks :
    peak_height = max ( ys ) * 1.4
    max_x = max ( xs )
    min_x = min ( xs )
    now_max_x = max ( xs ) + 0.3 * ( max_x - min_x )
    now_min_x = min_x - 0.1 * ( max_x - min_x )
    text_x_offset = 0.1 * ( now_max_x - max_x )
    for loc , mass in delta_peaks . items ( ) :
        text = "$P(\\mathrm{walk}) = " + ( "%.2f$" % ( mass ) )
        ax . plot ( [ 0 , peak_height ] , [ loc , loc ] , color = color , lw = 5 , label = text )
ax . plot ( ys , xs , "k-" )
if delta_peaks :
    tot_delta_peak_mass = sum ( delta_peaks . values ( ) )
    fill_label = "$P(\\mathrm{PT}) = %.2f$" % ( 1 - tot_delta_peak_mass )
else :
    fill_label = None
ax . fill_betweenx ( xs , ys , color = color , alpha = 0.2 , label = fill_label )
ax . set_ylabel ( xlabel )
ax . set_xlabel ( ylabel )
ax . set_xlim ( left = 0 , right = max ( ys ) * 1.2 )
if delta_peaks :
    if legend_font_size is None :
        legend_font_size = 12
    if legend_loc is None :
        legend_loc = "best"
    ax . legend ( loc = legend_loc , prop = { 'size' : legend_font_size } )
if True :
    line_tyles = [ "-." , "--" , "-" ] [ : : - 1 ]
    to_plot_funcs = [ self . max_temporal_distance , self . mean_temporal_distance , self . min_temporal_distance ]
    xmin , xmax = ax . get_xlim ( )
    for to_plot_func , ls in zip ( to_plot_funcs , line_tyles ) :
        y = to_plot_func ( ) / duration_divider
        assert y < float ( 'inf' )
        # factor of 10 just to be safe that the lines cover the whole region .
        ax . plot ( [ xmin , xmax * 10 ] , [ y , y ] , color = "black" , ls = ls , lw = 1 )
return ax . figure


signature:
def plot_temporal_distance_profile ( self , timezone = None , color = "black" , alpha = 0.15 , ax = None , lw = 2 , label = "" , plot_tdist_stats = False , plot_trip_stats = False , format_string = "%Y-%m-%d %H:%M:%S" , plot_journeys = False , duration_divider = 60.0 , fill_color = "green" , journey_letters = None , return_letters = False ) :
    """Parameters
 timezone : str
 color : color
 format _ string : str , None
 if None , the original values are used
 plot _ journeys : bool , optional
 if True , small dots are plotted at the departure times"""

implementation:
if ax is None :
    fig = plt . figure ( )
    ax = fig . add_subplot ( 111 )
if timezone is None :
    warnings . warn ( "Warning: No timezone specified, defaulting to UTC" )
    timezone = pytz . timezone ( "Etc/UTC" )
def _ut_to_unloc_datetime ( ut ) :
    dt = datetime . datetime . fromtimestamp ( ut , timezone )
    return dt . replace ( tzinfo = None )
if format_string :
    x_axis_formatter = md . DateFormatter ( format_string )
    ax . xaxis . set_major_formatter ( x_axis_formatter )
else :
    _ut_to_unloc_datetime = lambda x : x
ax . set_xlim ( _ut_to_unloc_datetime ( self . start_time_dep ) , _ut_to_unloc_datetime ( self . end_time_dep ) )
if plot_tdist_stats :
    line_tyles = [ "-." , "--" , "-" ] [ : : - 1 ]
    # to _ plot _ labels = [ " maximum temporal distance " , " mean temporal distance " , " minimum temporal distance " ]
    to_plot_labels = [ "$\\tau_\\mathrm{max} \\;$ = " , "$\\tau_\\mathrm{mean}$ = " , "$\\tau_\\mathrm{min} \\:\\:$ = " ]
    to_plot_funcs = [ self . max_temporal_distance , self . mean_temporal_distance , self . min_temporal_distance ]
    xmin , xmax = ax . get_xlim ( )
    for to_plot_label , to_plot_func , ls in zip ( to_plot_labels , to_plot_funcs , line_tyles ) :
        y = to_plot_func ( ) / duration_divider
        assert y < float ( 'inf' ) , to_plot_label
        to_plot_label = to_plot_label + "%.1f min" % ( y )
        ax . plot ( [ xmin , xmax ] , [ y , y ] , color = "black" , ls = ls , lw = 1 , label = to_plot_label )
if plot_trip_stats :
    assert ( not plot_tdist_stats )
    line_tyles = [ "-" , "-." , "--" ]
    to_plot_labels = [ "min journey duration" , "max journey duration" , "mean journey duration" ]
    to_plot_funcs = [ self . min_trip_duration , self . max_trip_duration , self . mean_trip_duration ]
    xmin , xmax = ax . get_xlim ( )
    for to_plot_label , to_plot_func , ls in zip ( to_plot_labels , to_plot_funcs , line_tyles ) :
        y = to_plot_func ( ) / duration_divider
        if not numpy . math . isnan ( y ) :
            ax . plot ( [ xmin , xmax ] , [ y , y ] , color = "red" , ls = ls , lw = 2 )
            txt = to_plot_label + "\n = %.1f min" % y
            ax . text ( xmax + 0.01 * ( xmax - xmin ) , y , txt , color = "red" , va = "center" , ha = "left" )
    old_xmax = xmax
    xmax += ( xmax - xmin ) * 0.3
    ymin , ymax = ax . get_ylim ( )
    ax . fill_between ( [ old_xmax , xmax ] , ymin , ymax , color = "gray" , alpha = 0.1 )
    ax . set_xlim ( xmin , xmax )
# plot the actual profile
vertical_lines , slopes = self . profile_block_analyzer . get_vlines_and_slopes_for_plotting ( )
for i , line in enumerate ( slopes ) :
    xs = [ _ut_to_unloc_datetime ( x ) for x in line [ 'x' ] ]
    if i is 0 :
        label = u"profile"
    else :
        label = None
    ax . plot ( xs , numpy . array ( line [ 'y' ] ) / duration_divider , "-" , color = color , lw = lw , label = label )
for line in vertical_lines :
    xs = [ _ut_to_unloc_datetime ( x ) for x in line [ 'x' ] ]
    ax . plot ( xs , numpy . array ( line [ 'y' ] ) / duration_divider , ":" , color = color )
# , lw = lw )
assert ( isinstance ( ax , plt . Axes ) )
if plot_journeys :
    xs = [ _ut_to_unloc_datetime ( x ) for x in self . trip_departure_times ]
    ys = self . trip_durations
    ax . plot ( xs , numpy . array ( ys ) / duration_divider , "o" , color = "black" , ms = 8 , label = "journeys" )
    if journey_letters is None :
        journey_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        def cycle_journey_letters ( journey_letters ) : # cycle ( ' ABCD ' ) - - > A B C D A B C D A B C D . . .
            saved = [ ]
            for element in journey_letters :
                yield element
                saved . append ( element )
            count = 1
            while saved :
                for element in saved :
                    yield element + str ( count )
                count += 1
        journey_letters_iterator = cycle_journey_letters ( journey_letters )
    time_letters = { int ( time ) : letter for letter , time in zip ( journey_letters_iterator , self . trip_departure_times ) }
    for x , y , letter in zip ( xs , ys , journey_letters_iterator ) :
        walking = - self . _walk_time_to_target / 30 if numpy . isfinite ( self . _walk_time_to_target ) else 0
        ax . text ( x + datetime . timedelta ( seconds = ( self . end_time_dep - self . start_time_dep ) / 60 ) , ( y + walking ) / duration_divider , letter , va = "top" , ha = "left" )
fill_between_x = [ ]
fill_between_y = [ ]
for line in slopes :
    xs = [ _ut_to_unloc_datetime ( x ) for x in line [ 'x' ] ]
    fill_between_x . extend ( xs )
    fill_between_y . extend ( numpy . array ( line [ "y" ] ) / duration_divider )
ax . fill_between ( fill_between_x , y1 = fill_between_y , color = fill_color , alpha = alpha , label = label )
ax . set_ylim ( bottom = 0 )
ax . set_ylim ( ax . get_ylim ( ) [ 0 ] , ax . get_ylim ( ) [ 1 ] * 1.05 )
if rcParams [ 'text.usetex' ] :
    ax . set_xlabel ( r"Departure time $t_{\mathrm{dep}}$" )
else :
    ax . set_xlabel ( "Departure time" )
ax . set_ylabel ( r"Temporal distance $\tau$ (min)" )
if plot_journeys and return_letters :
    return ax , time_letters
else :
    return ax


signature:
def add_leg ( self , leg ) :
    """Parameters
 leg : Connection"""

implementation:
assert ( isinstance ( leg , Connection ) )
if not self . legs :
    self . departure_time = leg . departure_time
self . arrival_time = leg . arrival_time
if leg . trip_id and ( not self . legs or ( leg . trip_id != self . legs [ - 1 ] . trip_id ) ) :
    self . n_boardings += 1
self . arrival_time = leg . arrival_time
self . legs . append ( leg )


signature:
def get_transfer_stop_pairs ( self ) :
    """Get stop pairs through which transfers take place
 Returns
 transfer _ stop _ pairs : list"""

implementation:
transfer_stop_pairs = [ ]
previous_arrival_stop = None
current_trip_id = None
for leg in self . legs :
    if leg . trip_id is not None and leg . trip_id != current_trip_id and previous_arrival_stop is not None :
        transfer_stop_pair = ( previous_arrival_stop , leg . departure_stop )
        transfer_stop_pairs . append ( transfer_stop_pair )
    previous_arrival_stop = leg . arrival_stop
    current_trip_id = leg . trip_id
return transfer_stop_pairs


signature:
def _truncate_colormap ( cmap , minval = 0.0 , maxval = 1.0 , n = 100 ) :
    """Truncates a colormap to use .
 Code originall from http : / / stackoverflow . com / questions / 18926031 / how - to - extract - a - subset - of - a - colormap - as - a - new - colormap - in - matplotlib"""

implementation:
new_cmap = LinearSegmentedColormap . from_list ( 'trunc({n},{a:.2f},{b:.2f})' . format ( n = cmap . name , a = minval , b = maxval ) , cmap ( numpy . linspace ( minval , maxval , n ) ) )
return new_cmap


signature:
def get_time_profile_analyzer ( self , max_n_boardings = None ) :
    """Parameters
 max _ n _ boardings : int
 The maximum number of boardings allowed for the labels used to construct the " temporal distance profile "
 Returns
 analyzer : NodeProfileAnalyzerTime"""

implementation:
if max_n_boardings is None :
    max_n_boardings = self . max_trip_n_boardings ( )
# compute only if not yet computed
if not max_n_boardings in self . _n_boardings_to_simple_time_analyzers :
    if max_n_boardings == 0 :
        valids = [ ]
    else :
        candidate_labels = [ LabelTimeSimple ( label . departure_time , label . arrival_time_target ) for label in self . _node_profile_final_labels if ( ( self . start_time_dep <= label . departure_time ) and label . n_boardings <= max_n_boardings ) ]
        valids = compute_pareto_front ( candidate_labels )
    valids . sort ( key = lambda label : - label . departure_time )
    profile = NodeProfileSimple ( self . _walk_to_target_duration )
    for valid in valids :
        profile . update_pareto_optimal_tuples ( valid )
    npat = NodeProfileAnalyzerTime . from_profile ( profile , self . start_time_dep , self . end_time_dep )
    self . _n_boardings_to_simple_time_analyzers [ max_n_boardings ] = npat
return self . _n_boardings_to_simple_time_analyzers [ max_n_boardings ]


signature:
def median_temporal_distances ( self , min_n_boardings = None , max_n_boardings = None ) :
    """Returns
 mean _ temporal _ distances : list
 list indices encode the number of vehicle legs each element
 in the list tells gets the mean temporal distance"""

implementation:
if min_n_boardings is None :
    min_n_boardings = 0
if max_n_boardings is None :
    max_n_boardings = self . max_trip_n_boardings ( )
    if max_n_boardings is None :
        max_n_boardings = 0
median_temporal_distances = [ float ( 'inf' ) for _ in range ( min_n_boardings , max_n_boardings + 1 ) ]
for n_boardings in range ( min_n_boardings , max_n_boardings + 1 ) :
    simple_analyzer = self . get_time_profile_analyzer ( n_boardings )
    median_temporal_distances [ n_boardings ] = simple_analyzer . median_temporal_distance ( )
return median_temporal_distances


signature:
def from_directory_as_inmemory_db ( cls , gtfs_directory ) :
    """Instantiate a GTFS object by computing
 Parameters
 gtfs _ directory : str
 path to the directory for importing the database"""

implementation:
# this import is here to avoid circular imports ( which turned out to be a problem )
from gtfspy . import_gtfs import import_gtfs
conn = sqlite3 . connect ( ":memory:" )
import_gtfs ( gtfs_directory , conn , preserve_connection = True , print_progress = False )
return cls ( conn )


signature:
def get_main_database_path ( self ) :
    """Should return the path to the database
 Returns
 path : unicode
 path to the database , empty string for in - memory databases"""

implementation:
cur = self . conn . cursor ( )
cur . execute ( "PRAGMA database_list" )
rows = cur . fetchall ( )
for row in rows :
    if row [ 1 ] == str ( "main" ) :
        return row [ 2 ]


signature:
def get_shape_distance_between_stops ( self , trip_I , from_stop_seq , to_stop_seq ) :
    """Get the distance along a shape between stops
 Parameters
 trip _ I : int
 trip _ ID along which we travel
 from _ stop _ seq : int
 the sequence number of the ' origin ' stop
 to _ stop _ seq : int
 the sequence number of the ' destination ' stop
 Returns
 distance : float , None
 If the shape calculation succeeded , return a float , otherwise return None
 ( i . e . in the case where the shapes table is empty )"""

implementation:
query_template = "SELECT shape_break FROM stop_times WHERE trip_I={trip_I} AND seq={seq} "
stop_seqs = [ from_stop_seq , to_stop_seq ]
shape_breaks = [ ]
for seq in stop_seqs :
    q = query_template . format ( seq = seq , trip_I = trip_I )
    shape_breaks . append ( self . conn . execute ( q ) . fetchone ( ) )
query_template = "SELECT max(d) - min(d) " "FROM shapes JOIN trips ON(trips.shape_id=shapes.shape_id) " "WHERE trip_I={trip_I} AND shapes.seq>={from_stop_seq} AND shapes.seq<={to_stop_seq};"
distance_query = query_template . format ( trip_I = trip_I , from_stop_seq = from_stop_seq , to_stop_seq = to_stop_seq )
return self . conn . execute ( distance_query ) . fetchone ( ) [ 0 ]


signature:
def get_directly_accessible_stops_within_distance ( self , stop , distance ) :
    """Returns stops that are accessible without transfer from the stops that are within a specific walking distance
 : param stop : int
 : param distance : int
 : return :"""

implementation:
query = """SELECT stop.* FROM
                    (SELECT st2.* FROM
                    (SELECT * FROM stop_distances
                    WHERE from_stop_I = %s) sd,
                    (SELECT * FROM stop_times) st1,
                    (SELECT * FROM stop_times) st2
                    WHERE sd.d < %s AND sd.to_stop_I = st1.stop_I AND st1.trip_I = st2.trip_I
                    GROUP BY st2.stop_I) sq,
                    (SELECT * FROM stops) stop
                    WHERE sq.stop_I = stop.stop_I""" % ( stop , distance )
return pd . read_sql_query ( query , self . conn )


signature:
def get_timezone_name ( self ) :
    """Get name of the GTFS timezone
 Returns
 timezone _ name : str
 name of the time zone , e . g . " Europe / Helsinki " """

implementation:
tz_name = self . conn . execute ( 'SELECT timezone FROM agencies LIMIT 1' ) . fetchone ( )
if tz_name is None :
    raise ValueError ( "This database does not have a timezone defined." )
return tz_name [ 0 ]


signature:
def get_timezone_string ( self , dt = None ) :
    """Return the timezone of the GTFS database object as a string .
 The assumed time when the timezone ( difference ) is computed
 is the download date of the file .
 This might not be optimal in all cases .
 So this function should return values like :
 " + 0200 " or " - 1100"
 Parameters
 dt : datetime . datetime , optional
 The ( unlocalized ) date when the timezone should be computed .
 Defaults first to download _ date , and then to the runtime date .
 Returns
 timezone _ string : str"""

implementation:
if dt is None :
    download_date = self . meta . get ( 'download_date' )
    if download_date :
        dt = datetime . datetime . strptime ( download_date , '%Y-%m-%d' )
    else :
        dt = datetime . datetime . today ( )
loc_dt = self . _timezone . localize ( dt )
# get the timezone
timezone_string = loc_dt . strftime ( "%z" )
return timezone_string


signature:
def unlocalized_datetime_to_ut_seconds ( self , unlocalized_datetime ) :
    """Convert datetime ( in GTFS timezone ) to unixtime
 Parameters
 unlocalized _ datetime : datetime . datetime
 ( tz coerced to GTFS timezone , should NOT be UTC . )
 Returns
 output : int ( unixtime )"""

implementation:
loc_dt = self . _timezone . localize ( unlocalized_datetime )
unixtime_seconds = calendar . timegm ( loc_dt . utctimetuple ( ) )
return unixtime_seconds


signature:
def get_day_start_ut ( self , date ) :
    """Get day start time ( as specified by GTFS ) as unix time in seconds
 Parameters
 date : str | unicode | datetime . datetime
 something describing the date
 Returns
 day _ start _ ut : int
 start time of the day in unixtime"""

implementation:
if isinstance ( date , string_types ) :
    date = datetime . datetime . strptime ( date , '%Y-%m-%d' )
date_noon = datetime . datetime ( date . year , date . month , date . day , 12 , 0 , 0 )
ut_noon = self . unlocalized_datetime_to_ut_seconds ( date_noon )
return ut_noon - 12 * 60 * 60


signature:
def get_trip_trajectories_within_timespan ( self , start , end , use_shapes = True , filter_name = None ) :
    """Get complete trip data for visualizing public transport operation based on gtfs .
 Parameters
 start : number
 Earliest position data to return ( in unix time )
 end : number
 Latest position data to return ( in unix time )
 use _ shapes : bool , optional
 Whether or not shapes should be included
 filter _ name : str
 Pick only routes having this name .
 Returns
 trips : dict
 trips [ ' trips ' ] is a list whose each element ( e . g . el = trips [ ' trips ' ] [ 0 ] )
 is a dict with the following properties :
 el [ ' lats ' ] - - list of latitudes
 el [ ' lons ' ] - - list of longitudes
 el [ ' times ' ] - - list of passage _ times
 el [ ' route _ type ' ] - - type of vehicle as specified by GTFS
 el [ ' name ' ] - - name of the route"""

implementation:
trips = [ ]
trip_df = self . get_tripIs_active_in_range ( start , end )
print ( "gtfs_viz.py: fetched " + str ( len ( trip_df ) ) + " trip ids" )
shape_cache = { }
# loop over all trips :
for row in trip_df . itertuples ( ) :
    trip_I = row . trip_I
    day_start_ut = row . day_start_ut
    shape_id = row . shape_id
    trip = { }
    name , route_type = self . get_route_name_and_type_of_tripI ( trip_I )
    trip [ 'route_type' ] = int ( route_type )
    trip [ 'name' ] = str ( name )
    if filter_name and ( name != filter_name ) :
        continue
    stop_lats = [ ]
    stop_lons = [ ]
    stop_dep_times = [ ]
    shape_breaks = [ ]
    stop_seqs = [ ]
    # get stop _ data and store it :
    stop_time_df = self . get_trip_stop_time_data ( trip_I , day_start_ut )
    for stop_row in stop_time_df . itertuples ( ) :
        stop_lats . append ( float ( stop_row . lat ) )
        stop_lons . append ( float ( stop_row . lon ) )
        stop_dep_times . append ( float ( stop_row . dep_time_ut ) )
        try :
            stop_seqs . append ( int ( stop_row . seq ) )
        except TypeError :
            stop_seqs . append ( None )
        if use_shapes :
            try :
                shape_breaks . append ( int ( stop_row . shape_break ) )
            except ( TypeError , ValueError ) :
                shape_breaks . append ( None )
    if use_shapes : # get shape data ( from cache , if possible )
        if shape_id not in shape_cache :
            shape_cache [ shape_id ] = shapes . get_shape_points2 ( self . conn . cursor ( ) , shape_id )
        shape_data = shape_cache [ shape_id ]
        # noinspection PyBroadException
        try :
            trip [ 'times' ] = shapes . interpolate_shape_times ( shape_data [ 'd' ] , shape_breaks , stop_dep_times )
            trip [ 'lats' ] = shape_data [ 'lats' ]
            trip [ 'lons' ] = shape_data [ 'lons' ]
            start_break = shape_breaks [ 0 ]
            end_break = shape_breaks [ - 1 ]
            trip [ 'times' ] = trip [ 'times' ] [ start_break : end_break + 1 ]
            trip [ 'lats' ] = trip [ 'lats' ] [ start_break : end_break + 1 ]
            trip [ 'lons' ] = trip [ 'lons' ] [ start_break : end_break + 1 ]
        except : # In case interpolation fails :
            trip [ 'times' ] = stop_dep_times
            trip [ 'lats' ] = stop_lats
            trip [ 'lons' ] = stop_lons
    else :
        trip [ 'times' ] = stop_dep_times
        trip [ 'lats' ] = stop_lats
        trip [ 'lons' ] = stop_lons
    trips . append ( trip )
return { "trips" : trips }


signature:
def get_stop_count_data ( self , start_ut , end_ut ) :
    """Get stop count data .
 Parameters
 start _ ut : int
 start time in unixtime
 end _ ut : int
 end time in unixtime
 Returns
 stopData : pandas . DataFrame
 each row in the stopData dataFrame is a dictionary with the following elements
 stop _ I , count , lat , lon , name
 with data types
 ( int , int , float , float , str )"""

implementation:
# TODO ! this function could perhaps be made a single sql query now with the new tables ?
trips_df = self . get_tripIs_active_in_range ( start_ut , end_ut )
# stop _ I - > count , lat , lon , name
stop_counts = Counter ( )
# loop over all trips :
for row in trips_df . itertuples ( ) : # get stop _ data and store it :
    stops_seq = self . get_trip_stop_time_data ( row . trip_I , row . day_start_ut )
    for stop_time_row in stops_seq . itertuples ( index = False ) :
        if ( stop_time_row . dep_time_ut >= start_ut ) and ( stop_time_row . dep_time_ut <= end_ut ) :
            stop_counts [ stop_time_row . stop_I ] += 1
all_stop_data = self . stops ( )
counts = [ stop_counts [ stop_I ] for stop_I in all_stop_data [ "stop_I" ] . values ]
all_stop_data . loc [ : , "count" ] = pd . Series ( counts , index = all_stop_data . index )
return all_stop_data


signature:
def get_segment_count_data ( self , start , end , use_shapes = True ) :
    """Get segment data including PTN vehicle counts per segment that are
 fully _ contained _ within the interval ( start , end )
 Parameters
 start : int
 start time of the simulation in unix time
 end : int
 end time of the simulation in unix time
 use _ shapes : bool , optional
 whether to include shapes ( if available )
 Returns
 seg _ data : list
 each element in the list is a dict containing keys :
 " trip _ I " , " lats " , " lons " , " shape _ id " , " stop _ seqs " , " shape _ breaks " """

implementation:
cur = self . conn . cursor ( )
# get all possible trip _ ids that take place between start and end
trips_df = self . get_tripIs_active_in_range ( start , end )
# stop _ I - > count , lat , lon , name
segment_counts = Counter ( )
seg_to_info = { }
# tripI _ to _ seq = " inverted segToShapeData "
tripI_to_seq = defaultdict ( list )
# loop over all trips :
for row in trips_df . itertuples ( ) : # get stop _ data and store it :
    stops_df = self . get_trip_stop_time_data ( row . trip_I , row . day_start_ut )
    for i in range ( len ( stops_df ) - 1 ) :
        ( stop_I , dep_time_ut , s_lat , s_lon , s_seq , shape_break ) = stops_df . iloc [ i ]
        ( stop_I_n , dep_time_ut_n , s_lat_n , s_lon_n , s_seq_n , shape_break_n ) = stops_df . iloc [ i + 1 ]
        # test if _ contained _ in the interval
        # overlap would read :
        # ( dep _ time _ ut < = end ) and ( start < = dep _ time _ ut _ n )
        if ( dep_time_ut >= start ) and ( dep_time_ut_n <= end ) :
            seg = ( stop_I , stop_I_n )
            segment_counts [ seg ] += 1
            if seg not in seg_to_info :
                seg_to_info [ seg ] = { u"trip_I" : row . trip_I , u"lats" : [ s_lat , s_lat_n ] , u"lons" : [ s_lon , s_lon_n ] , u"shape_id" : row . shape_id , u"stop_seqs" : [ s_seq , s_seq_n ] , u"shape_breaks" : [ shape_break , shape_break_n ] }
                tripI_to_seq [ row . trip_I ] . append ( seg )
stop_names = { }
for ( stop_I , stop_J ) in segment_counts . keys ( ) :
    for s in [ stop_I , stop_J ] :
        if s not in stop_names :
            stop_names [ s ] = self . stop ( s ) [ u'name' ] . values [ 0 ]
seg_data = [ ]
for seg , count in segment_counts . items ( ) :
    segInfo = seg_to_info [ seg ]
    shape_breaks = segInfo [ u"shape_breaks" ]
    seg_el = { }
    if use_shapes and shape_breaks and shape_breaks [ 0 ] and shape_breaks [ 1 ] :
        shape = shapes . get_shape_between_stops ( cur , segInfo [ u'trip_I' ] , shape_breaks = shape_breaks )
        seg_el [ u'lats' ] = segInfo [ u'lats' ] [ : 1 ] + shape [ u'lat' ] + segInfo [ u'lats' ] [ 1 : ]
        seg_el [ u'lons' ] = segInfo [ u'lons' ] [ : 1 ] + shape [ u'lon' ] + segInfo [ u'lons' ] [ 1 : ]
    else :
        seg_el [ u'lats' ] = segInfo [ u'lats' ]
        seg_el [ u'lons' ] = segInfo [ u'lons' ]
    seg_el [ u'name' ] = stop_names [ seg [ 0 ] ] + u"-" + stop_names [ seg [ 1 ] ]
    seg_el [ u'count' ] = count
    seg_data . append ( seg_el )
return seg_data


signature:
def get_all_route_shapes ( self , use_shapes = True ) :
    """Get the shapes of all routes .
 Parameters
 use _ shapes : bool , optional
 by default True ( i . e . use shapes as the name of the function indicates )
 if False ( fall back to lats and longitudes )
 Returns
 routeShapes : list of dicts that should have the following keys
 name , type , agency , lats , lons
 with types
 list , list , str , list , list"""

implementation:
cur = self . conn . cursor ( )
# all shape _ id : s corresponding to a route _ I :
# query = " SELECT DISTINCT name , shape _ id , trips . route _ I , route _ type
# FROM trips LEFT JOIN routes USING ( route _ I ) "
# data1 = pd . read _ sql _ query ( query , self . conn )
# one ( arbitrary ) shape _ id per route _ I ( " one direction " ) - > less than half of the routes
query = "SELECT routes.name as name, shape_id, route_I, trip_I, routes.type, " "        agency_id, agencies.name as agency_name, max(end_time_ds-start_time_ds) as trip_duration " "FROM trips " "LEFT JOIN routes " "USING(route_I) " "LEFT JOIN agencies " "USING(agency_I) " "GROUP BY routes.route_I"
data = pd . read_sql_query ( query , self . conn )
routeShapes = [ ]
for i , row in enumerate ( data . itertuples ( ) ) :
    datum = { "name" : str ( row . name ) , "type" : int ( row . type ) , "route_I" : row . route_I , "agency" : str ( row . agency_id ) , "agency_name" : str ( row . agency_name ) }
    # this function should be made also non - shape friendly ( at this point )
    if use_shapes and row . shape_id :
        shape = shapes . get_shape_points2 ( cur , row . shape_id )
        lats = shape [ 'lats' ]
        lons = shape [ 'lons' ]
    else :
        stop_shape = self . get_trip_stop_coordinates ( row . trip_I )
        lats = list ( stop_shape [ 'lat' ] )
        lons = list ( stop_shape [ 'lon' ] )
    datum [ 'lats' ] = [ float ( lat ) for lat in lats ]
    datum [ 'lons' ] = [ float ( lon ) for lon in lons ]
    routeShapes . append ( datum )
return routeShapes


signature:
def get_tripIs_active_in_range ( self , start , end ) :
    """Obtain from the ( standard ) GTFS database , list of trip _ IDs ( and other trip _ related info )
 that are active between given ' start ' and ' end ' times .
 The start time of a trip is determined by the departure time at the last stop of the trip .
 The end time of a trip is determined by the arrival time at the last stop of the trip .
 Parameters
 start , end : int
 the start and end of the time interval in unix time seconds
 Returns
 active _ trips : pandas . DataFrame with columns
 trip _ I , day _ start _ ut , start _ time _ ut , end _ time _ ut , shape _ id"""

implementation:
to_select = "trip_I, day_start_ut, start_time_ut, end_time_ut, shape_id "
query = "SELECT " + to_select + "FROM day_trips " "WHERE " "(end_time_ut > {start_ut} AND start_time_ut < {end_ut})" . format ( start_ut = start , end_ut = end )
return pd . read_sql_query ( query , self . conn )


signature:
def get_trip_counts_per_day ( self ) :
    """Get trip counts per day between the start and end day of the feed .
 Returns
 trip _ counts : pandas . DataFrame
 Has columns " date _ str " ( dtype str ) " trip _ counts " ( dtype int )"""

implementation:
query = "SELECT date, count(*) AS number_of_trips FROM day_trips GROUP BY date"
# this yields the actual data
trip_counts_per_day = pd . read_sql_query ( query , self . conn , index_col = "date" )
# the rest is simply code for filling out " gaps " in the time span
# ( necessary for some visualizations )
max_day = trip_counts_per_day . index . max ( )
min_day = trip_counts_per_day . index . min ( )
min_date = datetime . datetime . strptime ( min_day , '%Y-%m-%d' )
max_date = datetime . datetime . strptime ( max_day , '%Y-%m-%d' )
num_days = ( max_date - min_date ) . days
dates = [ min_date + datetime . timedelta ( days = x ) for x in range ( num_days + 1 ) ]
trip_counts = [ ]
date_strings = [ ]
for date in dates :
    date_string = date . strftime ( "%Y-%m-%d" )
    date_strings . append ( date_string )
    try :
        value = trip_counts_per_day . loc [ date_string , 'number_of_trips' ]
    except KeyError : # set value to 0 if dsut is not present , i . e . when no trips
    # take place on that day
        value = 0
    trip_counts . append ( value )
# check that all date _ strings are included ( move this to tests ? )
for date_string in trip_counts_per_day . index :
    assert date_string in date_strings
data = { "date" : dates , "date_str" : date_strings , "trip_counts" : trip_counts }
return pd . DataFrame ( data )


signature:
def get_suitable_date_for_daily_extract ( self , date = None , ut = False ) :
    """Parameters
 date : str
 ut : bool
 Whether to return the date as a string or as a an int ( seconds after epoch ) .
 Returns
 Selects suitable date for daily extract
 Iterates trough the available dates forward and backward from the download date accepting the first day that has
 at least 90 percent of the number of trips of the maximum date . The condition can be changed to something else .
 If the download date is out of range , the process will look through the dates from first to last ."""

implementation:
daily_trips = self . get_trip_counts_per_day ( )
max_daily_trips = daily_trips [ u'trip_counts' ] . max ( axis = 0 )
if date in daily_trips [ u'date_str' ] :
    start_index = daily_trips [ daily_trips [ u'date_str' ] == date ] . index . tolist ( ) [ 0 ]
    daily_trips [ u'old_index' ] = daily_trips . index
    daily_trips [ u'date_dist' ] = abs ( start_index - daily_trips . index )
    daily_trips = daily_trips . sort_values ( by = [ u'date_dist' , u'old_index' ] ) . reindex ( )
for row in daily_trips . itertuples ( ) :
    if row . trip_counts >= 0.9 * max_daily_trips :
        if ut :
            return self . get_day_start_ut ( row . date_str )
        else :
            return row . date_str


signature:
def get_weekly_extract_start_date ( self , ut = False , weekdays_at_least_of_max = 0.9 , verbose = False , download_date_override = None ) :
    """Find a suitable weekly extract start date ( monday ) .
 The goal is to obtain as ' usual ' week as possible .
 The weekdays of the weekly extract week should contain
 at least 0.9 of the total maximum of trips .
 Parameters
 ut : return unixtime ?
 weekdays _ at _ least _ of _ max : float
 download _ date _ override : str , semi - optional
 Download - date in format % Y - % m - % d , weeks close to this .
 Overrides the ( possibly ) recorded downloaded date in the database
 Returns
 date : int or str
 Raises
 error : RuntimeError
 If no download date could be found ."""

implementation:
daily_trip_counts = self . get_trip_counts_per_day ( )
if isinstance ( download_date_override , str ) :
    search_start_date = datetime . datetime . strptime ( download_date_override , "%Y-%m-%d" )
elif isinstance ( download_date_override , datetime . datetime ) :
    search_start_date = download_date_override
else :
    assert download_date_override is None
    download_date_str = self . meta [ 'download_date' ]
    if download_date_str == "" :
        warnings . warn ( "Download date is not speficied in the database. " "Download date used in GTFS." + self . get_weekly_extract_start_date . __name__ + "() defaults to the smallest date when any operations take place." )
        search_start_date = daily_trip_counts [ 'date' ] . min ( )
    else :
        search_start_date = datetime . datetime . strptime ( download_date_str , "%Y-%m-%d" )
feed_min_date = daily_trip_counts [ 'date' ] . min ( )
feed_max_date = daily_trip_counts [ 'date' ] . max ( )
assert ( feed_max_date - feed_min_date >= datetime . timedelta ( days = 7 ) ) , "Dataset is not long enough for providing week long extracts"
# get first a valid monday where the search for the week can be started :
next_monday_from_search_start_date = search_start_date + timedelta ( days = ( 7 - search_start_date . weekday ( ) ) )
if not ( feed_min_date <= next_monday_from_search_start_date <= feed_max_date ) :
    warnings . warn ( "The next monday after the (possibly user) specified download date is not present in the database." "Resorting to first monday after the beginning of operations instead." )
    next_monday_from_search_start_date = feed_min_date + timedelta ( days = ( 7 - feed_min_date . weekday ( ) ) )
max_trip_count = daily_trip_counts [ 'trip_counts' ] . quantile ( 0.95 )
# Take 95th percentile to omit special days , if any exist .
threshold = weekdays_at_least_of_max * max_trip_count
threshold_fulfilling_days = daily_trip_counts [ 'trip_counts' ] > threshold
# look forward first
# get the index of the trip :
search_start_monday_index = daily_trip_counts [ daily_trip_counts [ 'date' ] == next_monday_from_search_start_date ] . index [ 0 ]
# get starting point
while_loop_monday_index = search_start_monday_index
while len ( daily_trip_counts . index ) >= while_loop_monday_index + 7 :
    if all ( threshold_fulfilling_days [ while_loop_monday_index : while_loop_monday_index + 5 ] ) :
        row = daily_trip_counts . iloc [ while_loop_monday_index ]
        if ut :
            return self . get_day_start_ut ( row . date_str )
        else :
            return row [ 'date' ]
    while_loop_monday_index += 7
while_loop_monday_index = search_start_monday_index - 7
# then backwards
while while_loop_monday_index >= 0 :
    if all ( threshold_fulfilling_days [ while_loop_monday_index : while_loop_monday_index + 5 ] ) :
        row = daily_trip_counts . iloc [ while_loop_monday_index ]
        if ut :
            return self . get_day_start_ut ( row . date_str )
        else :
            return row [ 'date' ]
    while_loop_monday_index -= 7
raise RuntimeError ( "No suitable weekly extract start date could be determined!" )


signature:
def get_spreading_trips ( self , start_time_ut , lat , lon , max_duration_ut = 4 * 3600 , min_transfer_time = 30 , use_shapes = False ) :
    """Starting from a specific point and time , get complete single source
 shortest path spreading dynamics as trips , or " events " .
 Parameters
 start _ time _ ut : number
 Start time of the spreading .
 lat : float
 latitude of the spreading seed location
 lon : float
 longitude of the spreading seed location
 max _ duration _ ut : int
 maximum duration of the spreading process ( in seconds )
 min _ transfer _ time : int
 minimum transfer time in seconds
 use _ shapes : bool
 whether to include shapes
 Returns
 trips : dict
 trips [ ' trips ' ] is a list whose each element ( e . g . el = trips [ ' trips ' ] [ 0 ] )
 is a dict with the following properties :
 el [ ' lats ' ] : list of latitudes
 el [ ' lons ' ] : list of longitudes
 el [ ' times ' ] : list of passage _ times
 el [ ' route _ type ' ] : type of vehicle as specified by GTFS , or - 1 if walking
 el [ ' name ' ] : name of the route"""

implementation:
from gtfspy . spreading . spreader import Spreader
spreader = Spreader ( self , start_time_ut , lat , lon , max_duration_ut , min_transfer_time , use_shapes )
return spreader . spread ( )


signature:
def get_closest_stop ( self , lat , lon ) :
    """Get closest stop to a given location .
 Parameters
 lat : float
 latitude coordinate of the location
 lon : float
 longitude coordinate of the location
 Returns
 stop _ I : int
 the index of the stop in the database"""

implementation:
cur = self . conn . cursor ( )
min_dist = float ( "inf" )
min_stop_I = None
rows = cur . execute ( "SELECT stop_I, lat, lon FROM stops" )
for stop_I , lat_s , lon_s in rows :
    dist_now = wgs84_distance ( lat , lon , lat_s , lon_s )
    if dist_now < min_dist :
        min_dist = dist_now
        min_stop_I = stop_I
return min_stop_I


signature:
def get_route_name_and_type_of_tripI ( self , trip_I ) :
    """Get route short name and type
 Parameters
 trip _ I : int
 short trip index created when creating the database
 Returns
 name : str
 short name of the route , eg . 195N
 type : int
 route _ type according to the GTFS standard"""

implementation:
cur = self . conn . cursor ( )
results = cur . execute ( "SELECT name, type FROM routes JOIN trips USING(route_I) WHERE trip_I={trip_I}" . format ( trip_I = trip_I ) )
name , rtype = results . fetchone ( )
return u"%s" % str ( name ) , int ( rtype )


signature:
def get_route_name_and_type ( self , route_I ) :
    """Get route short name and type
 Parameters
 route _ I : int
 route index ( database specific )
 Returns
 name : str
 short name of the route , eg . 195N
 type : int
 route _ type according to the GTFS standard"""

implementation:
cur = self . conn . cursor ( )
results = cur . execute ( "SELECT name, type FROM routes WHERE route_I=(?)" , ( route_I , ) )
name , rtype = results . fetchone ( )
return name , int ( rtype )