diff --git a/rrtimetable/rrtimetable/exporter/timetable3.py b/rrtimetable/rrtimetable/exporter/timetable3.py index c524fe9..810a85d 100644 --- a/rrtimetable/rrtimetable/exporter/timetable3.py +++ b/rrtimetable/rrtimetable/exporter/timetable3.py @@ -99,7 +99,7 @@ def make_idx(tdata): index.journey_patterns_at_stop_point[jpp.stop_point.uri] = set([]) index.journey_patterns_at_stop_point[jpp.stop_point.uri].add(vj.journey_pattern.uri) if jpp.stop_point.stop_area.uri not in index.idx_for_stop_area_uri: - index.idx_for_stop_point_uri[jpp.stop_point.stop_area.uri] = len(index.stop_areas) + index.idx_for_stop_area_uri[jpp.stop_point.stop_area.uri] = len(index.stop_areas) index.stop_areas.append(jpp.stop_point.stop_area) for conn in tdata.connections.values(): diff --git a/rrtimetable/rrtimetable/exporter/timetable4.py b/rrtimetable/rrtimetable/exporter/timetable4.py new file mode 100644 index 0000000..ce903b1 --- /dev/null +++ b/rrtimetable/rrtimetable/exporter/timetable4.py @@ -0,0 +1,528 @@ +import helper +from utils import * +import operator +import sys + +NUMBER_OF_DAYS = 32 + +class Index(): + def __init__(self): + self.operators = [] + self.idx_for_operator_uri = {} + self.lines = [] + self.idx_for_line_uri = {} + self.routes = [] + self.idx_for_route_uri = {} + self.journey_patterns = [] + self.idx_for_journey_pattern_uri = {} + self.stop_points = [] + self.idx_for_stop_point_uri = {} + self.stop_areas = [] + self.idx_for_stop_area_uri = {} + self.validity_pattern_for_journey_pattern_uri = {} + self.timedemandgroups = [] + self.idx_for_timedemandgroup_uri = {} + self.journey_patterns_at_stop_point = {} + self.vehicle_journeys_in_journey_pattern = {} + self.connections_from_stop_point = {} + self.connections_point_to_point = {} + + self.idx_for_productcategory = {} + self.productcategories = [] + + self.idx_for_linecode = {} + self.linecodes = [] + + self.idx_for_operator = {} + self.operators = [] + + self.loc_for_string = {} + self.strings = [] + self.string_length = 0 + + def put_operator(self,operator): + if operator in self.idx_for_operator: + return self.idx_for_operator[operator] + self.idx_for_operator[operator] = len(self.idx_for_operator) + self.operators.append(operator) + return self.idx_for_operator[operator] + + def put_string(self,string): + if string in self.loc_for_string: + return self.loc_for_string[string] + self.loc_for_string[string] = self.string_length + self.string_length += (len(string) + 1) + self.strings.append(string) + return self.loc_for_string[string] + + def put_productcategory(self,productcategory): + if productcategory in self.idx_for_productcategory: + return self.idx_for_productcategory[productcategory] + self.idx_for_productcategory[productcategory] = len(self.idx_for_productcategory) + self.productcategories.append(productcategory) + return self.idx_for_productcategory[productcategory] + + def put_linecode(self,linecode): + if linecode in self.idx_for_linecode: + return self.idx_for_linecode[linecode] + self.idx_for_linecode[linecode] = len(self.idx_for_linecode) + self.linecodes.append(linecode) + return self.idx_for_linecode[linecode] + +def make_idx(tdata): + index = Index() + for vj in sorted(tdata.vehicle_journeys.values(), key= lambda vj: (vj.route.line.operator.uri,vj.route.line.uri,vj.route.uri,vj.departure_time)): + if len(vj.validity_pattern) == 0 or min(vj.validity_pattern) >= NUMBER_OF_DAYS: + continue + if vj.journey_pattern.uri not in index.validity_pattern_for_journey_pattern_uri: + index.validity_pattern_for_journey_pattern_uri[vj.journey_pattern.uri] = set([]) + index.validity_pattern_for_journey_pattern_uri[vj.journey_pattern.uri].update(vj.validity_pattern) + + if vj.journey_pattern.route.line.operator.uri not in index.idx_for_operator_uri: + index.idx_for_operator_uri[vj.journey_pattern.route.line.operator.uri] = len(index.idx_for_operator_uri) + index.operators.append(vj.journey_pattern.route.line.operator) + + if vj.journey_pattern.route.line.uri not in index.idx_for_line_uri: + index.idx_for_line_uri[vj.journey_pattern.route.line.uri] = len(index.idx_for_line_uri) + index.lines.append(vj.journey_pattern.route.line) + + if vj.journey_pattern.route.uri not in index.idx_for_route_uri: + index.idx_for_route_uri[vj.journey_pattern.route.uri] = len(index.idx_for_route_uri) + index.routes.append(vj.journey_pattern.route) + + if vj.journey_pattern.uri not in index.idx_for_journey_pattern_uri: + index.idx_for_journey_pattern_uri[vj.journey_pattern.uri] = len(index.idx_for_journey_pattern_uri) + index.journey_patterns.append(vj.journey_pattern) + + if vj.journey_pattern.uri not in index.vehicle_journeys_in_journey_pattern: + index.vehicle_journeys_in_journey_pattern[vj.journey_pattern.uri] = [] + index.vehicle_journeys_in_journey_pattern[vj.journey_pattern.uri].append(vj) + + if vj.timedemandgroup.uri not in index.idx_for_timedemandgroup_uri: + index.idx_for_timedemandgroup_uri[vj.timedemandgroup.uri] = len(index.idx_for_timedemandgroup_uri) + index.timedemandgroups.append(vj.timedemandgroup) + for jpp in vj.journey_pattern.points: + if jpp.stop_point.uri not in index.idx_for_stop_point_uri: + index.idx_for_stop_point_uri[jpp.stop_point.uri] = len(index.stop_points) + index.stop_points.append(jpp.stop_point) + if jpp.stop_point.uri not in index.journey_patterns_at_stop_point: + index.journey_patterns_at_stop_point[jpp.stop_point.uri] = set([]) + index.journey_patterns_at_stop_point[jpp.stop_point.uri].add(vj.journey_pattern.uri) + if jpp.stop_point.stop_area.uri not in index.idx_for_stop_area_uri: + index.idx_for_stop_area_uri[jpp.stop_point.stop_area.uri] = len(index.stop_areas) + index.stop_areas.append(jpp.stop_point.stop_area) + + for conn in tdata.connections.values(): + if conn.from_stop_point.uri not in index.idx_for_stop_point_uri or conn.to_stop_point.uri not in index.idx_for_stop_point_uri: + continue #connection to or from unknown stop_point + if conn.from_stop_point.uri not in index.connections_from_stop_point: + index.connections_from_stop_point[conn.from_stop_point.uri] = [] + index.connections_from_stop_point[conn.from_stop_point.uri].append(conn) + if len(index.journey_patterns) == 0: + print "No valid journey_patterns found to export to this timetable. Exiting..." + sys.exit(1) + print '--------------------------' + return index + +def write_stop_point_idx(out,index,stop_point_uri): + if len(index.stop_points) <= 65535: + writeshort(out,index.idx_for_stop_point_uri[stop_point_uri]) + else: + writeint(out,index.idx_for_stop_point_uri[stop_point_uri]) + +def write_stop_area_idx(out,index,stop_area_uri): + if len(index.stop_points) <= 65535: + writeshort(out,index.idx_for_stop_area_uri[stop_area_uri]) + else: + writeint(out,index.idx_for_stop_area_uri[stop_area_uri]) + +def export_sp_coords(tdata,index,out): + write_text_comment(out,"STOP POINT COORDS") + index.loc_stop_point_coords = out.tell() + for sp in index.stop_points: + write2floats(out,sp.latitude or 0.0, sp.longitude or 0.0) + +def export_sa_coords(tdata,index,out): + write_text_comment(out,"STOP AREA COORDS") + index.loc_stop_area_coords = out.tell() + for sa in index.stop_areas: + write2floats(out,sa.latitude or 0.0, sa.longitude or 0.0) + +def export_journey_pattern_point_stop(tdata,index,out): + write_text_comment(out,"JOURNEY_PATTERN_POINT STOP") + index.loc_journey_pattern_points = tell(out) + index.offset_jpp = [] + offset = 0 + index.n_jpp = 0 + for jp in index.journey_patterns: + index.offset_jpp.append(offset) + for jpp in jp.points: + index.n_jpp += 1 + write_stop_point_idx(out,index,jpp.stop_point.uri) + offset += 1 + +def export_journey_pattern_point_attributes(tdata,index,out): + write_text_comment(out,"STOPS ATTRIBUTES BY JOURNEY_PATTERN") + index.loc_journey_pattern_point_attributes = tell(out) + index.offset_jpp_attributes = [] + offset = 0 + for jp in index.journey_patterns: + index.offset_jpp_attributes.append(offset) + for jpp in jp.points: + attr = 0 + if jpp.timingpoint: + attr |= 1 + if jpp.forboarding: + attr |= 2 + if jpp.foralighting: + attr |= 4 + writebyte(out,attr) + offset += 1 + +timedemandgroup_t = Struct('HH') +def export_timedemandgroups(tdata,index,out): + write_text_comment(out,"TIMEDEMANDGROUPS") + index.loc_timedemandgroups = tell(out) + index.offset_for_timedemandgroup_uri = {} + tp_offset = 0 + for tp in index.timedemandgroups: + index.offset_for_timedemandgroup_uri[tp.uri] = tp_offset + for tpp in tp.points: + out.write(timedemandgroup_t.pack(tpp.drivetime >> 2, tpp.totaldrivetime >> 2)) + tp_offset += 1 + index.n_tpp = tp_offset + +def export_vj_in_jp(tdata,index,out): + write_text_comment(out,"VEHICLE JOURNEYS IN JOURNEY_PATTERN") + index.loc_vehicle_journeys = tell(out) + tioffset = 0 + index.vj_ids_offsets = [] + vj_t = Struct('IHH') + for jp in index.journey_patterns: + index.vj_ids_offsets.append(tioffset) + for vj in index.vehicle_journeys_in_journey_pattern[jp.uri]: + vj_attr = 0 + out.write(vj_t.pack(index.offset_for_timedemandgroup_uri[vj.timedemandgroup.uri], vj.departure_time >> 2, vj_attr)) + tioffset += 1 + +def export_jpp_at_sp(tdata,index,out): + write_text_comment(out,"JOURNEY_PATTERNS AT STOP") + index.loc_jp_at_sp = tell(out) + index.jpp_at_sp_offsets = [] + n_offset = 0 + for sp in index.stop_points: + jp_uris = index.journey_patterns_at_stop_point[sp.uri] + index.jpp_at_sp_offsets.append(n_offset) + for jp_uri in jp_uris: + writeint(out,index.idx_for_journey_pattern_uri[jp_uri]) + n_offset += 1 + index.jpp_at_sp_offsets.append(n_offset) #sentinel + index.n_jpp_at_sp = n_offset + +def export_sa_for_sp(tdata,index,out): + write_text_comment(out,"STOP_POINT -> STOP_AREA") + index.loc_sa_for_sp = tell(out) + for sp in index.stop_points: + write_stop_area_idx(out,index,sp.stop_area.uri) + +def export_transfers(tdata,index,out): + print "saving transfer stops (footpaths)" + write_text_comment(out,"TRANSFER TARGET STOPS") + index.loc_transfer_target_stop_points = tell(out) + + index.transfers_offsets = [] + offset = 0 + transfertimes = [] + for sp in index.stop_points: + index.transfers_offsets.append(offset) + if sp.uri not in index.connections_from_stop_point: + continue + for conn in index.connections_from_stop_point[sp.uri]: + if (int(conn.min_transfer_time) >> 2) > 255: + continue + write_stop_point_idx(out,index,conn.to_stop_point.uri) + transfertimes.append(conn.min_transfer_time) + offset += 1 + assert len(transfertimes) == offset + index.transfers_offsets.append(offset) #sentinel + index.n_connections = offset + + print "saving transfer times (footpaths)" + write_text_comment(out,"TRANSFER TIMES") + index.loc_transfer_dist_meters = tell(out) + + for transfer_time in transfertimes: + writebyte(out,(int(transfer_time) >> 2)) + +def export_stop_indices(tdata,index,out): + print "saving stop indexes" + write_text_comment(out,"STOP STRUCTS") + index.loc_stop_points = tell(out) + struct_2i = Struct('II') + print len(index.jpp_at_sp_offsets),len(index.transfers_offsets) + assert len(index.jpp_at_sp_offsets) == len(index.transfers_offsets) + for stop in zip (index.jpp_at_sp_offsets, index.transfers_offsets) : + out.write(struct_2i.pack(*stop)); + +def export_stop_point_attributes(tdata,index,out): + print "saving stop attributes" + write_text_comment(out,"STOP Attributes") + index.loc_stop_point_attributes = tell(out) + for sp in index.stop_points: + attr = 0 + writebyte(out,attr) + +def export_jp_structs(tdata,index,out): + print "saving route indexes" + write_text_comment(out,"ROUTE STRUCTS") + index.loc_journey_patterns = tell(out) + route_t = Struct('3I8H') + jpp_offsets = index.offset_jpp + trip_ids_offsets = index.vj_ids_offsets + jp_attributes = [] + + nroutes = len(index.journey_patterns) + + jp_n_jpp = [] + jp_n_vj = [] + + index.idx_for_operator = {} + index.jp_operators = [] + operator_offsets = [] + + linecode_offsets = [] + productcategory_offsets = [] + headsign_offsets=[] + jp_min_time = [] + jp_max_time = [] + for jp in index.journey_patterns: + jp_n_jpp.append(len(jp.points)) + jp_n_vj.append(len(index.vehicle_journeys_in_journey_pattern[jp.uri])) + jp_min_time.append(min([vj.departure_time for vj in index.vehicle_journeys_in_journey_pattern[jp.uri]]) >> 2) + jp_max_time.append(max([vj.departure_time+vj.timedemandgroup.points[-1].totaldrivetime for vj in index.vehicle_journeys_in_journey_pattern[jp.uri]]) >> 2) + + productcategory_offsets.append(index.put_productcategory(jp.productcategory or '')) + headsign_offsets.append(index.put_string(jp.headsign or '')) + linecode_offsets.append(index.put_linecode(jp.route.line.code or '')) + operator_offsets.append(index.put_operator(jp.route.line.operator)) + + jp_attributes.append(1 << jp.route.route_type) + jp_t_fields = [jpp_offsets, trip_ids_offsets,headsign_offsets, jp_n_jpp, jp_n_vj,jp_attributes,operator_offsets,linecode_offsets,productcategory_offsets,jp_min_time, jp_max_time] + for l in jp_t_fields : + # the extra last route is a sentinel so we can derive list lengths for the last true route. + assert len(l) == nroutes + for route in zip (*jp_t_fields) : + # print route + out.write(route_t.pack(*route)); + out.write(route_t.pack(jpp_offsets[-1]+1,0,0,0,0,0,0,0,0,0, 0)) #Sentinel + +def validity_mask(days): + mask = 0 + for day in days: + if day < NUMBER_OF_DAYS: + mask |= 1 << day + return mask + +def export_vj_validities(tdata,index,out): + print "writing bitfields indicating which days each trip is active" + # note that bitfields are ordered identically to the trip_ids table, and offsets into that table can be reused + write_text_comment(out,"VJ ACTIVE BITFIELDS") + index.loc_vj_active = tell(out) + + for jp in index.journey_patterns: + for vj in index.vehicle_journeys_in_journey_pattern[jp.uri]: + writeint(out,validity_mask(vj.validity_pattern)) + +def export_jp_validities(tdata,index,out): + print "writing bitfields indicating which days each trip is active" + # note that bitfields are ordered identically to the trip_ids table, and offsets into that table can be reused + write_text_comment(out,"JP ACTIVE BITFIELDS") + index.loc_jp_active = tell(out) + n_zeros = 0 + for jp in index.journey_patterns: + writeint(out,validity_mask(index.validity_pattern_for_journey_pattern_uri[jp.uri])) + +def export_platform_codes(tdata,index,out): + print "writing out platformcodes for stops" + write_text_comment(out,"PLATFORM CODES") + index.loc_platformcodes = write_string_table(out,[sp.platformcode or '' for sp in index.stop_points]) + +def export_stop_pointnames(tdata,index,out): + print "writing out locations for stopnames" + write_text_comment(out,"STOP NAME LOCATIONS") + index.loc_stop_nameidx = tell(out) + for sp in index.stop_points: + writeint(out,index.put_string(sp.name or '')) + writeint(out,0) + +def export_stop_areanames(tdata,index,out): + print "writing out locations for stopareas" + write_text_comment(out,"STOP AREA LOCATIONS") + index.loc_stop_areaidx = tell(out) + for sa in index.stop_areas: + writeint(out,index.put_string(sa.name or '')) + writeint(out,0) + +def export_operators(tdata,index,out): + print "writing out agencies to string table" + write_text_comment(out,"OPERATOR IDS") + index.loc_operator_ids = write_string_table(out,[op.uri or '' for op in index.operators]) + write_text_comment(out,"OPERATOR NAMES") + index.loc_operator_names = write_string_table(out,[op.name or '' for op in index.operators]) + write_text_comment(out,"OPERATOR URLS") + index.loc_operator_urls = write_string_table(out,[op.url or '' for op in index.operators]) + +def export_stringpool(tdata,index,out): + print "writing out sheadsignstringpool" + write_text_comment(out,"STIRNGPOOL") + index.loc_stringpool = tell(out) + written_length = 0 + for string in index.strings: + out.write(string+'\0') + written_length += len(string) + 1 + assert written_length == index.string_length + + +def export_linecodes(tdata,index,out): + write_text_comment(out,"LINE CODES") + index.loc_line_codes = write_string_table(out,index.linecodes) + +def export_productcategories(tdata,index,out): + write_text_comment(out,"PRODUCT CATEGORIES") + index.loc_productcategories = write_string_table(out,index.productcategories) + +def export_line_uris(tdata,index,out): + # maybe no need to store route IDs: report trip ids and look them up when reconstructing the response + print "writing line ids to string table" + write_text_comment(out,"LINE IDS") + index.loc_line_uris = write_string_table(out,[jp.route.line.uri for jp in index.journey_patterns]) + +def export_sp_uris(tdata,index,out): + print "writing out sorted stop ids to string table" + # stopid index was several times bigger than the string table. it's probably better to just store fixed-width ids. + write_text_comment(out,"STOP IDS") + index.loc_stop_point_uris = write_string_table(out,[sp.uri for sp in index.stop_points]) + +def export_vj_uris(tdata,index,out): + all_vj_ids = [] + for jp in index.journey_patterns: + for vj in index.vehicle_journeys_in_journey_pattern[jp.uri]: + all_vj_ids.append(vj.uri) + index.n_vj = len(all_vj_ids) + print "writing trip ids to string table" + # note that trip_ids are ordered by departure time within trip bundles (routes), which are themselves in arbitrary order. + write_text_comment(out,"VJ IDS") + index.loc_vj_uris = write_string_table(out,all_vj_ids) + index.n_vj = len(all_vj_ids) + +def write_header (out,index) : + """ Write out a file header containing offsets to the beginning of each subsection. + Must match struct transit_data_header in transitdata.c """ + out.seek(0) + htext = "TTABLEV4" + + packed = struct_header.pack(htext, + index.calendar_start_time, + index.dst_mask, + index.n_stops, # n_stops + len(index.stop_areas), #n_stop_areas + index.n_stops, # n_stop_attributes + index.n_stops, # n_stop_point_coords + len(index.stop_areas), # n_stop_area_coords + len(index.stop_points), # n_sa_for_ap + index.n_jp, # n_routes + index.n_jpp, # n_route_stops + index.n_jpp, # n_route_stop_attributes + index.n_tpp, # n_stop_times + index.n_vj, # n_vjs + index.n_jpp_at_sp, # n_stop_routes + index.n_connections, #n_transfer_target_stop + index.n_connections, #n_transfer_dist_meters + index.n_vj, #n_trip_active + index.n_jp, # n_route_active + index.n_stops, # n_platformcodes + len(index.stop_points), # n_stop_nameidx + len(index.stop_areas), # n_stop_nameidx + len(index.operators), # n_operator_id + len(index.operators), # n_operator_names + len(index.operators), # n_operator_urls + index.string_length, # n_headsigns (length of the object) + len(index.idx_for_linecode), # n_route_shortnames + len(index.idx_for_productcategory), # n_productcategories + len(index.journey_patterns), # n_route_ids + index.n_stops, # n_stop_ids + index.n_vj, # n_trip_ids + + index.loc_stop_points, + index.loc_stop_point_attributes, + index.loc_stop_point_coords, + index.loc_journey_patterns, + index.loc_journey_pattern_points, + index.loc_journey_pattern_point_attributes, + index.loc_timedemandgroups, + index.loc_vehicle_journeys, + index.loc_jp_at_sp, + index.loc_transfer_target_stop_points, + index.loc_transfer_dist_meters, + index.loc_vj_active, + index.loc_jp_active, + index.loc_platformcodes, + index.loc_stop_nameidx, + index.loc_stop_areaidx, + index.loc_operator_ids, + index.loc_operator_names, + index.loc_operator_urls, + index.loc_stringpool, + index.loc_line_codes, + index.loc_productcategories, + index.loc_line_uris, + index.loc_stop_point_uris, + index.loc_vj_uris, + index.loc_stop_area_coords, + index.loc_sa_for_sp, + ) + out.write(packed) + +struct_header = Struct('8sQ56I') + +def export(tdata): + index = make_idx(tdata) + index.dst_mask = 0 + index.calendar_start_time = time.mktime((tdata.validfrom).timetuple()) + index.n_stops = len(index.stop_points) + index.n_jp = len(index.journey_patterns) + out = open('timetable4.dat','wb') + out.seek(struct_header.size) + + export_sp_coords(tdata,index,out) + export_journey_pattern_point_stop(tdata,index,out) + export_journey_pattern_point_attributes(tdata,index,out) + export_timedemandgroups(tdata,index,out) + export_vj_in_jp(tdata,index,out) + export_jpp_at_sp(tdata,index,out) + export_transfers(tdata,index,out) + export_stop_indices(tdata,index,out) + export_stop_point_attributes(tdata,index,out) + export_jp_structs(tdata,index,out) + export_vj_validities(tdata,index,out) + export_jp_validities(tdata,index,out) + export_platform_codes(tdata,index,out) + export_stop_pointnames(tdata,index,out) + export_stop_areanames(tdata,index,out) + export_operators(tdata,index,out) + export_stringpool(tdata,index,out) + export_linecodes(tdata,index,out) + export_productcategories(tdata,index,out) + export_line_uris(tdata,index,out) + export_sp_uris(tdata,index,out) + export_vj_uris(tdata,index,out) + export_sa_coords(tdata,index,out) + export_sa_for_sp(tdata,index,out) + print "reached end of timetable file" + write_text_comment(out,"END TTABLEV4") + index.loc_eof = tell(out) + print "rewinding and writing header... ", + write_header(out,index) + + out.flush() + out.close() diff --git a/rrtimetable/rrtimetable/fusio_dbexport.py b/rrtimetable/rrtimetable/fusio_dbexport.py index 7debb94..4a93d1d 100644 --- a/rrtimetable/rrtimetable/fusio_dbexport.py +++ b/rrtimetable/rrtimetable/fusio_dbexport.py @@ -1,6 +1,7 @@ import psycopg2 from model.transit import * from exporter.timetable3 import export +import exporter.timetable4 def parse_gtfs_time(timestr): return (lambda x:int(x[0])*3600+int(x[1])*60+int(x[2]))(timestr.split(":")) #oh yes I did @@ -72,3 +73,4 @@ def convert(dbname): return tdata tdata = convert('ridprod') export(tdata) +exporter.timetable4.export(tdata) diff --git a/rrtimetable/rrtimetable/gtfs2rrrr.py b/rrtimetable/rrtimetable/gtfs2rrrr.py index ac7c589..320e41c 100644 --- a/rrtimetable/rrtimetable/gtfs2rrrr.py +++ b/rrtimetable/rrtimetable/gtfs2rrrr.py @@ -2,6 +2,7 @@ from gtfsdb import GTFSDatabase import sys from exporter.timetable3 import export +import exporter.timetable4 from datetime import timedelta, date MAX_DAYS = 32 @@ -104,6 +105,7 @@ def main(): print "No valid trips in this GTFS file!" sys.exit(1) export(tdata) + exporter.timetable4.export(tdata) if __name__=='__main__': main() diff --git a/tdata.c b/tdata.c index 11b885f..181a7ba 100644 --- a/tdata.c +++ b/tdata.c @@ -68,7 +68,7 @@ const char *tdata_agency_url_for_index(tdata_t *td, uint32_t agency_index) { } const char *tdata_headsign_for_offset(tdata_t *td, uint32_t headsign_offset) { - return td->headsigns + headsign_offset; + return td->string_pool + headsign_offset; } const char *tdata_line_code_for_index(tdata_t *td, uint32_t line_code_index) { @@ -95,7 +95,7 @@ spidx_t tdata_stop_pointidx_by_stop_point_name(tdata_t *td, char *stop_point_nam for (sp_index = sp_index_offset; sp_index < td->n_stop_points; ++sp_index) { - if (strcasestr(td->stop_point_names + td->stop_point_nameidx[sp_index], + if (strcasestr(td->string_pool + td->stop_point_nameidx[sp_index], stop_point_name)) { return sp_index; } @@ -103,6 +103,23 @@ spidx_t tdata_stop_pointidx_by_stop_point_name(tdata_t *td, char *stop_point_nam return STOP_NONE; } +spidx_t tdata_stop_areaidx_for_index(tdata_t *td, spidx_t sp_index) { + return td->stop_area_for_stop_point[sp_index]; +} + +spidx_t tdata_stop_areaidx_by_stop_area_name(tdata_t *td, char *stop_point_name, spidx_t sa_index_offset) { + spidx_t sa_index; + for (sa_index = sa_index_offset; + sa_index < td->n_stop_areas; + ++sa_index) { + if (strcasestr(td->string_pool + td->stop_area_nameidx[sa_index], + stop_point_name)) { + return sa_index; + } + } + return STOP_NONE; +} + spidx_t tdata_stop_pointidx_by_stop_point_idx(tdata_t *td, char *stop_point_id, spidx_t sp_index_offset) { spidx_t sp_index; for (sp_index = sp_index_offset; @@ -146,7 +163,7 @@ calendar_t *tdata_vj_masks_for_journey_pattern(tdata_t *td, uint32_t jp_index) { const char *tdata_headsign_for_journey_pattern(tdata_t *td, uint32_t jp_index) { if (jp_index == NONE) return "NONE"; - return td->headsigns + (td->journey_patterns)[jp_index].headsign_offset; + return td->string_pool + (td->journey_patterns)[jp_index].headsign_offset; } const char *tdata_line_code_for_journey_pattern(tdata_t *td, uint32_t jp_index) { @@ -250,10 +267,14 @@ const char *tdata_stop_point_name_for_index(tdata_t *td, spidx_t sp_index) { case ONBOARD : return "ONBOARD"; default : - return td->stop_point_names + td->stop_point_nameidx[sp_index]; + return td->string_pool + td->stop_point_nameidx[sp_index]; } } +const char *tdata_stop_area_name_for_index(tdata_t *td, spidx_t sa_index) { + return td->string_pool + td->stop_area_nameidx[sa_index]; +} + /* Rather than reserving a place to store the transfers used to create the initial state, we look them up as needed. */ rtime_t transfer_duration (tdata_t *tdata, router_request_t *req, spidx_t sp_index_from, spidx_t sp_index_to) { UNUSED(req); diff --git a/tdata.h b/tdata.h index 11cb426..9005815 100644 --- a/tdata.h +++ b/tdata.h @@ -109,8 +109,11 @@ struct tdata { /* Dates within the active calendar which have DST. */ calendar_t dst_active; uint32_t n_stop_points; + uint32_t n_stop_areas; uint32_t n_stop_point_attributes; uint32_t n_stop_point_coords; + uint32_t n_stop_area_coords; + uint32_t n_stop_area_for_stop_point; uint32_t n_journey_patterns; uint32_t n_journey_pattern_points; uint32_t n_journey_pattern_point_attributes; @@ -124,10 +127,11 @@ struct tdata { uint32_t n_platformcodes; uint32_t n_stop_point_names; uint32_t n_stop_point_nameidx; + uint32_t n_stop_area_nameidx; uint32_t n_agency_ids; uint32_t n_agency_names; uint32_t n_agency_urls; - uint32_t n_headsigns; + uint32_t n_string_pool; uint32_t n_line_codes; uint32_t n_productcategories; uint32_t n_line_ids; @@ -147,17 +151,19 @@ struct tdata { /* optional data: * NULL pointer means it is not available */ latlon_t *stop_point_coords; + latlon_t *stop_area_coords; + spidx_t *stop_area_for_stop_point; uint32_t platformcodes_width; char *platformcodes; - char *stop_point_names; uint32_t *stop_point_nameidx; + uint32_t *stop_area_nameidx; uint32_t agency_ids_width; char *agency_ids; uint32_t agency_names_width; char *agency_names; uint32_t agency_urls_width; char *agency_urls; - char *headsigns; + char *string_pool; uint32_t line_codes_width; char *line_codes; uint32_t productcategories_width; @@ -230,10 +236,14 @@ const char *tdata_productcategory_for_index(tdata_t *td, uint32_t productcategor const char *tdata_stop_point_name_for_index(tdata_t *td, spidx_t sp_index); +const char *tdata_stop_area_name_for_index(tdata_t *td, spidx_t sp_index); + const char *tdata_platformcode_for_index(tdata_t *td, spidx_t sp_index); spidx_t tdata_stop_pointidx_by_stop_point_name(tdata_t *td, char *stop_point_name, spidx_t sp_index_offset); +spidx_t tdata_stop_pointidx_by_stop_area_name(tdata_t *td, char *stop_point_name, spidx_t sp_index_offset); + spidx_t tdata_stop_pointidx_by_stop_point_idx(tdata_t *td, char *stop_point_id, spidx_t sp_index_offset); uint32_t tdata_journey_pattern_idx_by_line_id(tdata_t *td, char *line_id, uint32_t start_index); diff --git a/tdata_io_v3.h b/tdata_io_v3.h index 00bf57f..d632841 100644 --- a/tdata_io_v3.h +++ b/tdata_io_v3.h @@ -4,13 +4,16 @@ /* file-visible struct */ typedef struct tdata_header tdata_header_t; struct tdata_header { - /* Contents must read "TTABLEV3" */ + /* Contents must read "TTABLEV4" */ char version_string[8]; uint64_t calendar_start_time; calendar_t dst_active; uint32_t n_stop_points; + uint32_t n_stop_areas; uint32_t n_stop_point_attributes; uint32_t n_stop_point_coords; + uint32_t n_stop_area_coords; + uint32_t n_stop_area_for_stop_point; uint32_t n_journey_patterns; uint32_t n_journey_pattern_points; uint32_t n_journey_pattern_point_attributes; @@ -22,16 +25,15 @@ struct tdata_header { uint32_t n_vj_active; uint32_t n_journey_pattern_active; uint32_t n_platformcodes; - /* length of the object in bytes */ - uint32_t n_stop_point_names; uint32_t n_stop_point_nameidx; + uint32_t n_stop_area_nameidx; uint32_t n_agency_ids; uint32_t n_agency_names; uint32_t n_agency_urls; /* length of the object in bytes */ - uint32_t n_headsigns; + uint32_t n_string_pool; uint32_t n_line_codes; uint32_t n_productcategories; uint32_t n_line_ids; @@ -51,17 +53,19 @@ struct tdata_header { uint32_t loc_vj_active; uint32_t loc_journey_pattern_active; uint32_t loc_platformcodes; - uint32_t loc_stop_point_names; uint32_t loc_stop_point_nameidx; + uint32_t loc_stop_area_nameidx; uint32_t loc_agency_ids; uint32_t loc_agency_names; uint32_t loc_agency_urls; - uint32_t loc_headsigns; + uint32_t loc_string_pool; uint32_t loc_line_codes; uint32_t loc_productcategories; uint32_t loc_line_ids; uint32_t loc_stop_point_ids; uint32_t loc_vj_ids; + uint32_t loc_stop_area_coords; + uint32_t loc_stop_area_for_stop_point; }; bool tdata_io_v3_load(tdata_t *td, char* filename); diff --git a/tdata_io_v3_dynamic.c b/tdata_io_v3_dynamic.c index 73c39d7..e813ff6 100644 --- a/tdata_io_v3_dynamic.c +++ b/tdata_io_v3_dynamic.c @@ -63,15 +63,18 @@ bool tdata_io_v3_load(tdata_t *td, char *filename) { td->base = NULL; td->size = 0; - if( strncmp("TTABLEV3", header->version_string, 8) ) { + if( strncmp("TTABLEV4", header->version_string, 8) ) { fprintf(stderr, "The input file %s does not appear to be a timetable or is of the wrong version.\n", filename); goto fail_close_fd; } /* More input validation in the dynamic loading case. */ if ( !( header->n_stop_points < ((spidx_t) -2) && + header->n_stop_areas < ((spidx_t) -2) && header->n_stop_point_attributes < ((spidx_t) -2) && header->n_stop_point_coords < ((spidx_t) -2) && + header->n_stop_area_coords < ((spidx_t) -2) && + header->n_stop_area_coords < ((spidx_t) -2) && header->n_journey_patterns < (UINT32_MAX - 1) && header->n_journey_pattern_points < (UINT32_MAX) && header->n_journey_pattern_point_attributes < (UINT32_MAX) && @@ -83,12 +86,11 @@ bool tdata_io_v3_load(tdata_t *td, char *filename) { header->n_vj_active < (UINT32_MAX) && header->n_journey_pattern_active < (UINT32_MAX) && header->n_platformcodes < (UINT32_MAX) && - header->n_stop_point_names < (UINT32_MAX) && header->n_stop_point_nameidx < ((spidx_t) -2) && header->n_agency_ids < (UINT16_MAX) && header->n_agency_names < (UINT16_MAX) && header->n_agency_urls < (UINT16_MAX) && - header->n_headsigns < (UINT32_MAX) && + header->n_string_pool < (UINT32_MAX) && header->n_line_codes < (UINT16_MAX) && header->n_productcategories < (UINT16_MAX) && header->n_line_ids < (UINT32_MAX) && @@ -101,10 +103,12 @@ bool tdata_io_v3_load(tdata_t *td, char *filename) { td->calendar_start_time = header->calendar_start_time; td->dst_active = header->dst_active; + td->n_stop_areas = header->n_stop_areas; load_dynamic (fd, stop_points, stop_point_t); load_dynamic (fd, stop_point_attributes, uint8_t); load_dynamic (fd, stop_point_coords, latlon_t); + load_dynamic (fd, stop_area_coords, latlon_t); load_dynamic (fd, journey_patterns, journey_pattern_t); load_dynamic (fd, journey_pattern_points, spidx_t); load_dynamic (fd, journey_pattern_point_attributes, uint8_t); @@ -115,9 +119,9 @@ bool tdata_io_v3_load(tdata_t *td, char *filename) { load_dynamic (fd, transfer_dist_meters, uint8_t); load_dynamic (fd, vj_active, calendar_t); load_dynamic (fd, journey_pattern_active, calendar_t); - load_dynamic (fd, headsigns, char); - load_dynamic (fd, stop_point_names, char); + load_dynamic (fd, string_pool, char); load_dynamic (fd, stop_point_nameidx, uint32_t); + load_dynamic (fd, stop_area_nameidx, uint32_t); load_dynamic_string (fd, platformcodes); load_dynamic_string (fd, stop_point_ids); @@ -144,6 +148,7 @@ void tdata_io_v3_close(tdata_t *td) { free (td->stop_points); free (td->stop_point_attributes); free (td->stop_point_coords); + free (td->stop_area_coords); free (td->journey_patterns); free (td->journey_pattern_points); free (td->journey_pattern_point_attributes); @@ -154,9 +159,9 @@ void tdata_io_v3_close(tdata_t *td) { free (td->transfer_dist_meters); free (td->vj_active); free (td->journey_pattern_active); - free (td->headsigns); - free (td->stop_point_names); + free (td->string_pool); free (td->stop_point_nameidx); + free (td->stop_area_nameidx); free (td->platformcodes); free (td->stop_point_ids); diff --git a/tdata_io_v3_mmap.c b/tdata_io_v3_mmap.c index fd96248..2ce6d13 100644 --- a/tdata_io_v3_mmap.c +++ b/tdata_io_v3_mmap.c @@ -64,17 +64,19 @@ bool tdata_io_v3_load(tdata_t *td, char *filename) { } header = (tdata_header_t *) td->base; - if( strncmp("TTABLEV3", header->version_string, 8) ) { + if( strncmp("TTABLEV4", header->version_string, 8) ) { fprintf(stderr, "The input file %s does not appear to be a timetable or is of the wrong version.\n", filename); goto fail_munmap_base; } td->calendar_start_time = header->calendar_start_time; td->dst_active = header->dst_active; + td->n_stop_areas = header->n_stop_areas; load_mmap (td->base, stop_points, stop_point_t); load_mmap (td->base, stop_point_attributes, uint8_t); load_mmap (td->base, stop_point_coords, latlon_t); + load_mmap (td->base, stop_area_coords, latlon_t); load_mmap (td->base, journey_patterns, journey_pattern_t); load_mmap (td->base, journey_pattern_points, spidx_t); load_mmap (td->base, journey_pattern_point_attributes, uint8_t); @@ -85,9 +87,10 @@ bool tdata_io_v3_load(tdata_t *td, char *filename) { load_mmap (td->base, transfer_dist_meters, uint8_t); load_mmap (td->base, vj_active, calendar_t); load_mmap (td->base, journey_pattern_active, calendar_t); - load_mmap (td->base, headsigns, char); - load_mmap (td->base, stop_point_names, char); + load_mmap (td->base, string_pool, char); load_mmap (td->base, stop_point_nameidx, uint32_t); + load_mmap (td->base, stop_area_nameidx, uint32_t); + load_mmap (td->base, stop_area_for_stop_point, spidx_t); load_mmap_string (td->base, platformcodes); load_mmap_string (td->base, stop_point_ids);