diff --git a/src/asim/scripts/resident/2zoneSkim.py b/src/asim/scripts/resident/2zoneSkim.py index 1adcd60ad..8bedf4e01 100644 --- a/src/asim/scripts/resident/2zoneSkim.py +++ b/src/asim/scripts/resident/2zoneSkim.py @@ -41,6 +41,9 @@ nodes['X'] = nodes.geometry.x nodes['Y'] = nodes.geometry.y +# TEST: nodes +# nodes.to_csv(r"C:/abm_runs/sar/2022_ABM3_tned/src/asim/scripts/resident/nodes.csv") + links = gpd.read_file(sf) links = links.to_crs(epsg=2230) @@ -58,33 +61,48 @@ centroids['Y'] = nodes[nodes[parms['maz_shape_maz_id']]!=0].Y centroids['MAZ'] = nodes[nodes[parms['maz_shape_maz_id']]!=0].MGRA centroids['MAZ_centroid_id'] = nodes[nodes[parms['maz_shape_maz_id']]!=0].index + centroids = pd.merge(centroids, maz_closest_network_node_id, left_on='MAZ_centroid_id', right_on=parms['mmms']["mmms_link_ref_id"], how='left') centroids = centroids.rename(columns={parms['mmms']["mmms_link_nref_id"]:'network_node_id'}) centroids["network_node_x"] = nodes["X"].loc[centroids["network_node_id"]].tolist() centroids["network_node_y"] = nodes["Y"].loc[centroids["network_node_id"]].tolist() +# TEST: centroids +# centroids.to_csv(r"C:/abm_runs/sar/2022_ABM3_tned/src/asim/scripts/resident/centroids.csv") + # %% ## read transit stop and route file (KK) ============ stops = pd.read_csv(os.path.join(model_inputs, parms['stop_attributes']['file'])) routes = pd.read_csv(os.path.join(model_inputs, parms['route_attributes']['file'])) routes = routes.filter(['Route_ID','Route_Name', 'Mode']) +# TEST: stops 1 +# stops.to_csv(r"C:/abm_runs/sar/2022_ABM3_tned/src/asim/scripts/resident/stops_1.csv") + # add mode from route file & convert lat.long to stateplane(KK)===== -stops = stops.merge(routes, left_on='Route_ID', right_on='Route_ID') # +stops = stops.merge(routes, left_on='Route_ID', right_on='Route_ID') # + +# TEST: stops 2 +# stops.to_csv(r"C:/abm_runs/sar/2022_ABM3_tned/src/asim/scripts/resident/stops_2.csv") -stops.rename(columns={' Latitude': 'Latitude'}, inplace=True) -stops["Longitude1"] = stops["Longitude"]/1000000 -stops["Latitude1"] = stops["Latitude"]/1000000 +# stops.rename(columns={' Latitude': 'Latitude'}, inplace=True) +# stops["Longitude1"] = stops["Longitude"]/1000000 +# stops["Latitude1"] = stops["Latitude"]/1000000 -gpd_stops = gpd.GeoDataFrame(stops, geometry = gpd.points_from_xy(stops.Longitude1, stops.Latitude1)) -gpd_stops = gpd_stops.set_crs('epsg:4326') -gpd_stops = gpd_stops.to_crs(epsg=2230) +# TEST: stops 3 +# stops.to_csv(r"C:/abm_runs/sar/2022_ABM3_tned/src/asim/scripts/resident/stops_3.csv") + +gpd_stops = gpd.GeoDataFrame(stops, geometry = gpd.points_from_xy(stops.Longitude, stops.Latitude, crs='epsg:4326')) +gpd_stops = gpd_stops.to_crs('epsg:2230') + +# TEST: stops 4 +# gpd_stops.to_file(r"C:/abm_runs/sar/2022_ABM3_tned/src/asim/scripts/resident/gpd_stops.shp") pd.set_option('display.float_format', lambda x: '%.9f' % x) -gpd_stops['Longitude'] = gpd_stops['geometry'].x -gpd_stops['Latitude'] = gpd_stops['geometry'].y +gpd_stops['Longitude'] = gpd_stops['geometry'].x +gpd_stops['Latitude'] = gpd_stops['geometry'].y stops["network_node_id"] = net.get_node_ids(gpd_stops['Longitude'], gpd_stops['Latitude']) stops["network_node_x"] = nodes["X"].loc[stops["network_node_id"]].tolist() @@ -94,6 +112,9 @@ np.where((stops['Mode']==4) | (stops['Mode']==5) | (stops['Mode']==8) | (stops['Mode']==9) | (stops['Mode']==6) | (stops['Mode']==7), 'E', 'N')) +# TEST: stops 4 +# stops.to_csv(r"C:/abm_runs/sar/2022_ABM3_tned/src/asim/scripts/resident/stops_4.csv") + # %% # MAZ-to-MAZ Walk print(f"{datetime.now().strftime('%H:%M:%S')} Build MAZ to MAZ Walk Table...") @@ -162,7 +183,6 @@ print(f"{datetime.now().strftime('%H:%M:%S')} Remove Maz Stop Pairs Beyond Max Walk Distance...") - maz_to_stop_walk_cost_out = maz_to_stop_walk_cost[(maz_to_stop_walk_cost["DISTANCE"] <= max_maz_local_bus_stop_walk_dist_feet / 5280.0) & (maz_to_stop_walk_cost['MODE'] == 'L') | (maz_to_stop_walk_cost["DISTANCE"] <= max_maz_premium_transit_stop_walk_dist_feet / 5280.0) & (maz_to_stop_walk_cost['MODE'] == 'E')].copy() @@ -189,7 +209,7 @@ maz_stop_walk['DISTWALK'].fillna(999999, inplace = True) maz_stop_walk.rename({'MAZ': 'maz', 'DISTWALK': 'walk_dist_' + output}, axis='columns', inplace=True) maz_stop_walk0 = maz_stop_walk0.merge(maz_stop_walk, left_on='maz', right_on='maz') - + maz_stop_walk0.sort_values(by=['maz'], inplace=True) print(f"{datetime.now().strftime('%H:%M:%S')} Write Results...") maz_stop_walk0.to_csv(path + '/output/skims/' + "maz_stop_walk.csv", index=False) diff --git a/src/main/emme/toolbox/assignment/build_transit_scenario.py b/src/main/emme/toolbox/assignment/build_transit_scenario.py index 0a59b4a5d..82949e324 100644 --- a/src/main/emme/toolbox/assignment/build_transit_scenario.py +++ b/src/main/emme/toolbox/assignment/build_transit_scenario.py @@ -118,6 +118,7 @@ def __init__(self): self.attributes = [ "period", "scenario_id", "base_scenario_id", "data_table_name", "scenario_title", "overwrite"] + self._node_id_tracker = None def page(self): if not self.data_table_name: @@ -218,6 +219,7 @@ def __call__(self, period, base_scenario, transit_emmebank, scenario_id, scenari for field in base_scenario.network_fields(): scenario.create_network_field(field.type, field.name, field.atype, field.description) network = base_scenario.get_network() + self._node_id_tracker = gen_utils.AvailableNodeIDTracker(network) new_attrs = [ ("TRANSIT_LINE", "@xfer_from_day", "Fare for xfer from daypass/trolley"), ("TRANSIT_LINE", "@xfer_from_premium", "Fare for first xfer from premium"), @@ -235,7 +237,6 @@ def __call__(self, period, base_scenario, transit_emmebank, scenario_id, scenari attr.description = desc network.create_attribute(elem, name) network.create_attribute("TRANSIT_LINE", "xfer_from_bus") - self._init_node_id(network) transit_passes = gen_utils.DataTableProc("%s_transit_passes" % data_table_name) transit_passes = {row["pass_type"]: row["cost"] for row in transit_passes} @@ -322,28 +323,23 @@ def __call__(self, period, base_scenario, transit_emmebank, scenario_id, scenari self.timed_transfers(network, timed_transfers_with_walk, period) #self.connect_circle_lines(network) #self.duplicate_tap_adajcent_stops(network) - # The fixed guideway travel times are stored in "@trtime_link_xx" + # The fixed guideway travel times are stored in "@trtime" # and copied to data2 (ul2) for the ttf # The congested auto times for mixed traffic are in "@auto_time" # (output from traffic assignment) which needs to be copied to auto_time (a.k.a. timau) # (The auto_time attribute is generated from the VDF values which include reliability factor) + ## also copying auto_time to ul1, so it does not get wiped when transit connectors are created. + src_attrs = [params["fixed_link_time"]] dst_attrs = ["data2"] if scenario.has_traffic_results and "@auto_time" in scenario.attributes("LINK"): - src_attrs.append("@auto_time") - dst_attrs.append("auto_time") + src_attrs.extend(["@auto_time", "@auto_time"]) + dst_attrs.extend(["auto_time", "data1"]) values = network.get_attribute_values("LINK", src_attrs) network.set_attribute_values("LINK", dst_attrs, values) scenario.publish_network(network) - - ##copying auto_time to ul1, so it does not get wiped when transit connectors are created. - if scenario.has_traffic_results and "@auto_time" in scenario.attributes("LINK"): - copy_att(from_attribute_name='timau', - to_attribute_name='ul1', - from_scenario=scenario, - to_scenario=scenario) - + self._node_id_tracker = None return scenario @_m.logbook_trace("Add timed-transfer links", save_arguments=True) @@ -449,13 +445,11 @@ def split_link(link, node_id, lines, split_links, stop_attr, waits=None): if near_side_stop: in_link.length = length out_link.length = 0 - for p in ["ea", "am", "md", "pm", "ev"]: - out_link["@trtime_link_" + p] = 0 + out_link["@trtime"] = 0 else: out_link.length = length in_link.length = 0 - for p in ["ea", "am", "md", "pm", "ev"]: - in_link["@trtime_link_" + p] = 0 + in_link["@trtime"] = 0 for seg in in_link.segments(): if not near_side_stop: @@ -476,10 +470,10 @@ def split_link(link, node_id, lines, split_links, stop_attr, waits=None): split_links = {} for transfer in network_transfers: new_alight_node = split_link( - transfer["from_link"], self._get_node_id(), transfer["from_lines"], + transfer["from_link"], self._node_id_tracker.get_id(), transfer["from_lines"], split_links, "allow_alightings") new_board_node = split_link( - transfer["to_link"], self._get_node_id(), transfer["to_lines"], + transfer["to_link"], self._node_id_tracker.get_id(), transfer["to_lines"], split_links, "allow_boardings", waits=transfer["wait"]) walk_link = transfer["walk_link"] transfer_link = network.create_link( @@ -508,7 +502,7 @@ def offset_coords(node): if first_seg.i_node == last_seg.i_node: # Add new node, offset from existing node start_node = line.segment(0).i_node - xfer_node = network.create_node(self._get_node_id(), False) + xfer_node = network.create_node(self._node_id_tracker.get_id(), False) xfer_node["@network_adj"] = 2 xfer_node.x, xfer_node.y = offset_coords(start_node) network.create_link(start_node, xfer_node, [line.vehicle.mode]) @@ -556,11 +550,3 @@ def offset_coords(node): seg[k] = v network.delete_attribute("NODE", "circle_lines") - - def _init_node_id(self, network): - new_node_id = max(n.number for n in network.nodes()) - self._new_node_id = math.ceil(new_node_id / 10000.0) * 10000 - - def _get_node_id(self): - self._new_node_id += 1 - return self._new_node_id diff --git a/src/main/emme/toolbox/assignment/create_transit_connector.py b/src/main/emme/toolbox/assignment/create_transit_connector.py index baa4edc8f..d93e9ea90 100644 --- a/src/main/emme/toolbox/assignment/create_transit_connector.py +++ b/src/main/emme/toolbox/assignment/create_transit_connector.py @@ -207,7 +207,7 @@ def create_tr_connectors(self, period, create_connector_flag, main_directory): delete_existing=True, selection={ "centroid":"all", - "node": "@ipark=1,9 and %s" % self.line_haul_mode_specs[i], + "node": "@park=1,9 and %s" % self.line_haul_mode_specs[i], "link":"none", "exclude_split_links":False, "only_midblock_nodes": False}, @@ -289,7 +289,7 @@ def create_tr_connectors(self, period, create_connector_flag, main_directory): delete_existing=True, selection={ "centroid":"all", - "node": "@ipark=1,9 and %s" % self.line_haul_mode_specs[i], + "node": "@park=1,9 and %s" % self.line_haul_mode_specs[i], "link":"none", "exclude_split_links":False, "only_midblock_nodes": False}, @@ -323,7 +323,7 @@ def create_tr_connectors(self, period, create_connector_flag, main_directory): delete_existing=True, selection={ "centroid":"all", - "node": "@ipark=1,9 and %s" % self.line_haul_mode_specs[i], + "node": "@park=1,9 and %s" % self.line_haul_mode_specs[i], "link":"none", "exclude_split_links":False, "only_midblock_nodes": False}, @@ -336,7 +336,7 @@ def create_tr_connectors(self, period, create_connector_flag, main_directory): delete_existing=True, selection={ "centroid":"i=1,4", - "node": "@ipark=1,9 and %s" % self.line_haul_mode_specs[i], + "node": "@park=1,9 and %s" % self.line_haul_mode_specs[i], "link":"none", "exclude_split_links":False, "only_midblock_nodes": False}, diff --git a/src/main/emme/toolbox/assignment/traffic_assignment.py b/src/main/emme/toolbox/assignment/traffic_assignment.py index ca11c066f..4fcfdc39d 100644 --- a/src/main/emme/toolbox/assignment/traffic_assignment.py +++ b/src/main/emme/toolbox/assignment/traffic_assignment.py @@ -565,15 +565,15 @@ def run_assignment(self, period, relative_gap, max_iterations, num_processors, s # create_attribute("LINK", "@cost_hov2_%s" % p, "toll (non-mngd) + cost for HOV2", # 0, overwrite=True, scenario=scenario) # net_calc("@cost_hov2_%s" % p, "@cost_hov_%s" % p, "modes=d") - # net_calc("@cost_hov2_%s" % p, "@cost_auto_%s" % p, "@lane_restriction=3") + # net_calc("@cost_hov2_%s" % p, "@cost_auto_%s" % p, "@hov=3") with _m.logbook_trace("Transit line headway and background traffic"): # set headway for the period - hdw = {"ea": "@headway_op", + hdw = {"ea": "@headway_ea", "am": "@headway_am", - "md": "@headway_op", + "md": "@headway_md", "pm": "@headway_pm", - "ev": "@headway_op"} + "ev": "@headway_ev"} net_calc("hdw", hdw[p], {"transit_line": "all"}) # transit vehicle as background flow with periods @@ -734,15 +734,15 @@ def run_stochastic_assignment( # create_attribute("LINK", "@cost_hov2_%s" % p, "toll (non-mngd) + cost for HOV2", # 0, overwrite=True, scenario=scenario) # net_calc("@cost_hov2_%s" % p, "@cost_hov_%s" % p, "modes=d") - # net_calc("@cost_hov2_%s" % p, "@cost_auto_%s" % p, "@lane_restriction=3") + # net_calc("@cost_hov2_%s" % p, "@cost_auto_%s" % p, "@hov=3") with _m.logbook_trace("Transit line headway and background traffic"): # set headway for the period: format is (attribute_name, period duration in hours) - hdw = {"ea": ("@headway_op", 3), + hdw = {"ea": ("@headway_ea", 3), "am": ("@headway_am", 3), - "md": ("@headway_op", 6.5), + "md": ("@headway_md", 6.5), "pm": ("@headway_pm", 3.5), - "ev": ("@headway_op", 5)} + "ev": ("@headway_ev", 5)} net_calc('ul2', '0', {'link': 'all'}) net_calc('hdw', '9999.99', {'transit_line': 'all'}) net_calc( @@ -789,11 +789,11 @@ def run_stochastic_assignment( with _m.logbook_trace("Reset transit line headways"): # set headway for the period - hdw = {"ea": "@headway_op", + hdw = {"ea": "@headway_ea", "am": "@headway_am", - "md": "@headway_op", + "md": "@headway_md", "pm": "@headway_pm", - "ev": "@headway_op"} + "ev": "@headway_ev"} net_calc("hdw", hdw[p], {"transit_line": "all"}) return @@ -829,17 +829,17 @@ def calc_network_results(self, period, num_processors, scenario): create_attribute("TURN", "@auto_time_turn", "traffic turn time (ptimau)", overwrite=True, scenario=scenario) - net_calc("@hovdist", "length", {"link": "@lane_restriction=2,3"}) + net_calc("@hovdist", "length", {"link": "@hov=2,3"}) net_calc("@tollcost", "@cost_auto_%s - @cost_operating" % p) - net_calc("@h2tollcost", "@cost_hov2_%s - @cost_operating" % p, {"link": "@lane_restriction=3,4"}) - net_calc("@h3tollcost", "@cost_hov3_%s - @cost_operating" % p, {"link": "@lane_restriction=4"}) + net_calc("@h2tollcost", "@cost_hov2_%s - @cost_operating" % p, {"link": "@hov=3,4"}) + net_calc("@h3tollcost", "@cost_hov3_%s - @cost_operating" % p, {"link": "@hov=4"}) net_calc("@trk_ltollcost", "@cost_lgt_truck_%s - @cost_operating" % p) net_calc("@trk_mtollcost", "@cost_med_truck_%s - @cost_operating" % p) net_calc("@trk_htollcost", "@cost_hvy_truck_%s - @cost_operating" % p) - net_calc("@mlcost", "@toll_%s" % p, {"link": "not @lane_restriction=4"}) - net_calc("@tolldist", "length", {"link": "@lane_restriction=2,4"}) - net_calc("@h2tolldist", "length", {"link": "@lane_restriction=3,4"}) - net_calc("@h3tolldist", "length", {"link": "@lane_restriction=4"}) + net_calc("@mlcost", "@toll_%s" % p, {"link": "not @hov=4"}) + net_calc("@tolldist", "length", {"link": "@hov=2,4"}) + net_calc("@h2tolldist", "length", {"link": "@hov=3,4"}) + net_calc("@h3tolldist", "length", {"link": "@hov=4"}) net_calc("@auto_volume", "volau", {"link": "modes=d"}) net_calc("ul2", "volau+volad", {"link": "modes=d"}) vdfs = [f for f in emmebank.functions() if f.type == "VOLUME_DELAY"] @@ -1026,7 +1026,7 @@ def change_mode_sovntp(self, scenario): gen_sov_mode = 's' sov_mode = scenario.mode(gen_sov_mode) change_link_modes(modes=[sov_mode], action="ADD", - selection="@lane_restriction=4", scenario=scenario) + selection="@hov=4", scenario=scenario) def report(self, period, scenario, classes): emmebank = scenario.emmebank diff --git a/src/main/emme/toolbox/assignment/transit_assignment.py b/src/main/emme/toolbox/assignment/transit_assignment.py index 4d4832dbf..2affceda2 100644 --- a/src/main/emme/toolbox/assignment/transit_assignment.py +++ b/src/main/emme/toolbox/assignment/transit_assignment.py @@ -263,11 +263,11 @@ def get_perception_parameters(self, period): "init_wait": 1.5, "xfer_wait": 3.0, "walk": 2.0, - "init_headway": "@headway_rev_op", - "xfer_headway": "@headway_op", + "init_headway": "@headway_rev_ea", + "xfer_headway": "@headway_ea", "fare": "@fare_per_op", "in_vehicle": "@vehicle_per_op", - "fixed_link_time": "@trtime_link_ea" + "fixed_link_time": "@trtime" }, "AM": { "access" : access, @@ -280,7 +280,7 @@ def get_perception_parameters(self, period): "xfer_headway": "@headway_am", "fare": "@fare_per_pk", "in_vehicle": "@vehicle_per_pk", - "fixed_link_time": "@trtime_link_am" + "fixed_link_time": "@trtime" }, "MD": { "access" : access, @@ -289,11 +289,11 @@ def get_perception_parameters(self, period): "init_wait": 1.5, "xfer_wait": 3.0, "walk": 2.0, - "init_headway": "@headway_rev_op", - "xfer_headway": "@headway_op", + "init_headway": "@headway_rev_md", + "xfer_headway": "@headway_md", "fare": "@fare_per_op", "in_vehicle": "@vehicle_per_op", - "fixed_link_time": "@trtime_link_md" + "fixed_link_time": "@trtime" }, "PM": { "access" : access, @@ -306,7 +306,7 @@ def get_perception_parameters(self, period): "xfer_headway": "@headway_pm", "fare": "@fare_per_pk", "in_vehicle": "@vehicle_per_pk", - "fixed_link_time": "@trtime_link_pm" + "fixed_link_time": "@trtime" }, "EV": { "access" : access, @@ -315,11 +315,11 @@ def get_perception_parameters(self, period): "init_wait": 1.5, "xfer_wait": 3.0, "walk": 2.0, - "init_headway": "@headway_rev_night", - "xfer_headway": "@headway_night", + "init_headway": "@headway_rev_ev", + "xfer_headway": "@headway_ev", "fare": "@fare_per_op", "in_vehicle": "@vehicle_per_op", - "fixed_link_time": "@trtime_link_ev" + "fixed_link_time": "@trtime" } } return perception_parameters[period] diff --git a/src/main/emme/toolbox/export/export_data_loader_network.py b/src/main/emme/toolbox/export/export_data_loader_network.py index de4585537..798606f0f 100644 --- a/src/main/emme/toolbox/export/export_data_loader_network.py +++ b/src/main/emme/toolbox/export/export_data_loader_network.py @@ -172,6 +172,7 @@ def export_traffic_attribute(self, base_scenario, export_path, traffic_emmebank, # items are ("column name", "attribute name") or ("column name", ("attribute name", default)) hwylink_attrs = [ ("ID", "@tcov_id"), + ("HWYSegGUID", "#hwyseg_guid"), ("Length", "length"), ("Dir", "is_one_way"), ("hwycov-id:1", "@tcov_id"), @@ -205,20 +206,20 @@ def export_traffic_attribute(self, base_scenario, export_path, traffic_emmebank, ("FFC", "type"), ("CLASS", "zero"), ("ASPD", "@speed_adjusted"), - ("IYR", "@year_open_traffic"), - ("IPROJ", "@project_code"), - ("IJUR", "@jurisdiction_type"), - ("IFC", "type"), - ("IHOV", "@lane_restriction"), - ("ITRUCK", "@truck_restriction"), - ("ISPD", "@speed_posted"), - ("ITSPD", "zero"), - ("IWAY", "iway"), - ("IMED", "@median"), + ("YR", "@year_open_traffic"), + ("PROJ", "@project_code"), + ("FC", "type"), + ("HOV", "@hov"), + ("EATRUCK", "@truck_ea"), + ("AMTRUCK", "@truck_am"), + ("MDTRUCK", "@truck_md"), + ("PMTRUCK", "@truck_pm"), + ("EVTRUCK", "@truck_ev"), + ("SPD", "@speed_posted"), + ("TSPD", "zero"), + ("WAY", "way"), + ("MED", "@median"), ("COST", "@cost_operating"), - ("ITOLLO", "@toll_md"), - ("ITOLLA", "@toll_am"), - ("ITOLLP", "@toll_pm"), ] directional_attrs = [ ("ABLNO", "@lane_md", "0"), @@ -236,15 +237,6 @@ def export_traffic_attribute(self, base_scenario, export_path, traffic_emmebank, ("ABLLB", "zero", "0"), ("ABGC", "@green_to_cycle_init", "0"), ("ABPLC", "per_lane_capacity", "1900"), - ("ABCPO", "@capacity_link_md", "999999"), - ("ABCPA", "@capacity_link_am", "999999"), - ("ABCPP", "@capacity_link_pm", "999999"), - ("ABCXO", "@capacity_inter_md", "999999"), - ("ABCXA", "@capacity_inter_am", "999999"), - ("ABCXP", "@capacity_inter_pm", "999999"), - ("ABCHO", "@capacity_hourly_op", "0"), - ("ABCHA", "@capacity_hourly_am", "0"), - ("ABCHP", "@capacity_hourly_pm", "0"), ("ABTMO", "@time_link_md", "999"), ("ABTMA", "@time_link_am", "999"), ("ABTMP", "@time_link_pm", "999"), @@ -263,13 +255,14 @@ def export_traffic_attribute(self, base_scenario, export_path, traffic_emmebank, hwylink_attrs.append(("relifac", "relifac")) time_period_atts = [ - ("ITOLL2", "@toll"), - ("ITOLL3", "@cost_auto"), - ("ITOLL4", "@cost_med_truck"), - ("ITOLL5", "@cost_hvy_truck"), - ("ITOLL", "toll_hov"), + ("TOLL2", "@toll"), + ("TOLL3", "@cost_auto"), + ("TOLL4", "@cost_med_truck"), + ("TOLL5", "@cost_hvy_truck"), + ("TOLL", "toll_hov"), ("ABCP", "@capacity_link", "999999"), ("ABCX", "@capacity_inter", "999999"), + ("ABCH", "@capacity_hourly", "0"), ("ABTM", "@time_link", "999"), ("ABTX", "@time_inter", "0"), ("ABLN", "@lane", "0"), @@ -334,7 +327,7 @@ def export_traffic_attribute(self, base_scenario, export_path, traffic_emmebank, network.set_attribute_values("LINK", dst_attrs, values) # add in and calculate additional columns new_attrs = [ - ("zero", 0), ("is_one_way", 0), ("iway", 2), ("length_feet", 0), + ("zero", 0), ("is_one_way", 0), ("way", 2), ("length_feet", 0), ("toll_hov", 0), ("per_lane_capacity", 1900), ("progression_factor", 1.0), ("alpha1", 0.8), ("beta1", 4.0), ("alpha2", 4.5), ("beta2", 2.0), ("relifac", 1.0), @@ -348,7 +341,7 @@ def export_traffic_attribute(self, base_scenario, export_path, traffic_emmebank, network.create_attribute("LINK", "hov3_total_gencost" + period, 0) for link in network.links(): link.is_one_way = 1 if link.reverse_link else 0 - link.iway = 2 if link.reverse_link else 1 + link.way = 2 if link.reverse_link else 1 link.length_feet = link.length * 5280 for period in periods: link["toll_hov" + period] = link["@cost_hov2" + period] - link["@cost_operating"] @@ -502,8 +495,9 @@ def export_transit_results(self, export_path, input_path, transit_emmebank_dict, # Note: Node analysis for transfers is VERY time consuming # this implementation will be replaced when new Emme version is available - trrt_atts = ["Route_ID","Route_Name","Mode","AM_Headway","PM_Headway","OP_Headway","Night_Headway","Night_Hours","Config","Fare"] - trstop_atts = ["Stop_ID","Route_ID","Link_ID","Pass_Count","Milepost","Longitude","Latitude","NearNode","FareZone","StopName"] + trrt_atts = ["Route_ID","Route_Name","Mode","AM_Headway","PM_Headway","Midday_Headway","Evening_Headway","EarlyAM_Headway", + "Evening_Hours", "EarlyAM_Hours", "Config","Fare"] + trstop_atts = ["Stop_ID","Route_ID","Link_ID","Link_GUID","Pass_Count","Milepost","Longitude","Latitude","NearNode","StopName"] #transit route file trrt_infile = os.path.join(input_path, "trrt.csv") @@ -518,7 +512,7 @@ def export_transit_results(self, export_path, input_path, transit_emmebank_dict, #transit stop file trstop_infile = os.path.join(input_path, "trstop.csv") trstop = pd.read_csv(trstop_infile) - trstop = trstop.rename(columns={"HwyNode":"NearNode"}) + trstop = trstop.rename(columns={"Node":"NearNode"}) trstop = trstop.rename(columns=lambda x:x.strip()) trstop_out = trstop[trstop_atts] trstop_outfile = os.path.join(export_path, "trstop.csv") @@ -970,8 +964,7 @@ def collapse_network_adjustments(self, network, segment_results, link_results): link_result_attrs = link_results.values() + ["aux_transit_volume"] link_attrs = network.attributes("LINK") link_modified_attrs = [ - "length", "@trtime_link_ea", "@trtime_link_am", "@trtime_link_md", - "@trtime_link_pm", "@trtime_link_ev", link_results["link_transit_flow"]] + "length", "@trtime", link_results["link_transit_flow"]] seg_attrs = network.attributes("TRANSIT_SEGMENT") line_attrs = network.attributes("TRANSIT_LINE") @@ -1018,8 +1011,8 @@ def get_xfer_link(node, timed_xfer_link, is_outgoing=True): elif node["@network_adj"] == 3: orig_node = network.node(node["@network_adj_src"]) # Remove transfer walk links and copy data to source walk link - for link in node.outgoing_links(): - if xfer_mode in link.modes and link.j_node["@network_adj"] == 3: + for link in _chain(node.incoming_links(), node.outgoing_links()): + if xfer_mode in link.modes and link.j_node["@network_adj"] == 3 and link.i_node["@network_adj"] == 3: orig_xfer_link = get_xfer_link(orig_node, link) for attr in link_result_attrs: orig_xfer_link[attr] += link[attr] diff --git a/src/main/emme/toolbox/export/export_for_transponder.py b/src/main/emme/toolbox/export/export_for_transponder.py index 17300c6ae..7f16293e6 100644 --- a/src/main/emme/toolbox/export/export_for_transponder.py +++ b/src/main/emme/toolbox/export/export_for_transponder.py @@ -122,7 +122,7 @@ def ml_facility_dist(self, network): ml_link_coords = [] ml_links = [] for link in network.links(): - if link["type"] == 1 and link["@lane_restriction"] in (2,3) and ( + if link["type"] == 1 and link["@hov"] in (2,3) and ( link["@toll_am"] + link["@toll_md"] + link["@toll_pm"]) > 0: ml_link_coords.append(LineString(link.shape)) ml_links.append(link) @@ -201,7 +201,7 @@ def percent_detour(self, scenario, network, props, num_processors): ml_link_coords = [] freeway_links = [] for link in network.links(): - if link["@lane_restriction"] in [2, 3] and link["type"] == 1 and ( + if link["@hov"] in [2, 3] and link["type"] == 1 and ( link["@toll_am"] + link["@toll_md"] + link["@toll_pm"]) > 0: ml_link_coords.append(LineString(link.shape)) if sov_non_toll_mode in link.modes: diff --git a/src/main/emme/toolbox/import/import_network.py b/src/main/emme/toolbox/import/import_network.py index d40a52f24..9819cd794 100644 --- a/src/main/emme/toolbox/import/import_network.py +++ b/src/main/emme/toolbox/import/import_network.py @@ -16,7 +16,7 @@ # # # Inputs: -# source: path to the location of the input network files +# source: path to the location of the input network geodatabase # traffic_scenario_id: optional scenario to store the imported network from the traffic files only # transit_scenario_id: optional scenario to store the imported network from the transit files only # merged_scenario_id: scenario to store the combined traffic and transit data from all network files @@ -25,18 +25,24 @@ # data_table_name: prefix to use to identify all data tables # overwrite: check to overwrite any existing data tables or scenarios with the same ID or name # emmebank: the Emme database in which to create the scenario. Default is the current open database +# create_time_periods: if True (default), also create per-time period scenarios (required to run assignments) # # Files referenced: -# hwycov.e00: base nodes and links for traffic network with traffic attributes in ESRI input exchange format -# linktypeturns.dbf: fixed turn travel times by to/from link type (field IFC) pairs -# turns.csv: turn bans and fixed costs by link from/to ID (field HWYCOV-ID) -# trcov.e00: base nodes and links for transit network in ESRI input exchange format -# trrt.csv: transit routes and their attributes -# trlink.csv: itineraries for each route as sequence of link IDs (TRCOV-ID field) -# trstop.csv: transit stop attributes -# timexfer_period.csv: table of timed transfer pairs of lines, by period +# +# *.gdb: A Geodatabase file with the network data for both highway and transit. The following tables are used +# - TNED_HwyNet +# - TNED_HwyNodes +# - TNED_RailNet +# - TNED_RailNodes +# - Turns +# The following files are also used (in the same directory as the *.gdb) +# +# trrt.csv: header data for the transit lines +# trlink.csv: sequence of links (routing) of transit lines +# trstop.csv: stop data for the transit lines # mode5tod.csv: global (per-mode) transit cost and perception attributes -# special_fares.txt: table listing special fares in terms of boarding and incremental in-vehicle costs. +# timexfer_.csv (optional): table of timed transfer pairs of lines, by period +# special_fares.txt (optional): table listing special fares in terms of boarding and incremental in-vehicle costs. # off_peak_toll_factors.csv (optional): factors to calculate the toll for EA, MD, and EV periods from the OP toll input for specified facilities # vehicle_class_toll_factors.csv (optional): factors to adjust the toll cost by facility name and class (DA, S2, S3, TRK_L, TRK_M, TRK_H) # @@ -46,10 +52,10 @@ import os modeller = inro.modeller.Modeller() main_directory = os.path.dirname(os.path.dirname(modeller.desktop.project.path)) - source_dir = os.path.join(main_directory, "input") + source_file = os.path.join(main_directory, "input", "EMMEOutputs.gdb") title = "Base 2012 scenario" import_network = modeller.tool("sandag.import.import_network") - import_network(output_dir, merged_scenario_id=100, title=title, + import_network(source_file, merged_scenario_id=100, title=title, data_table_name="2012_base", overwrite=True) """ @@ -85,22 +91,22 @@ FILE_NAMES = { "FARES": "special_fares.txt", + "TIMEXFER": "timexfer_%s.csv", "OFF_PEAK": "off_peak_toll_factors.csv", "VEHICLE_CLASS": "vehicle_class_toll_factors.csv", - "node_taz_map": "node_taz_map.csv", + "MODE5TOD": "MODE5TOD.csv", } class ImportNetwork(_m.Tool(), gen_utils.Snapshot): source = _m.Attribute(unicode) - traffic_scenario_id = _m.Attribute(int) - transit_scenario_id = _m.Attribute(int) - merged_scenario_id = _m.Attribute(int) + scenario_id = _m.Attribute(int) overwrite = _m.Attribute(bool) title = _m.Attribute(unicode) save_data_tables = _m.Attribute(bool) data_table_name = _m.Attribute(unicode) + create_time_periods = _m.Attribute(bool) tool_run_msg = "" @@ -116,37 +122,43 @@ def __init__(self): self.overwrite = False self.title = "" self.data_table_name = "" + self.create_time_periods = True self.attributes = [ - "source", "traffic_scenario_id", "transit_scenario_id", "merged_scenario_id", - "overwrite", "title", "save_data_tables", "data_table_name"] + "source", "scenario_id", "overwrite", "title", "save_data_tables", "data_table_name", "create_time_periods" + ] def page(self): if not self.data_table_name: - load_properties = _m.Modeller().tool('sandag.utilities.properties') - props = load_properties(_join(_dir(self.source), "conf", "sandag_abm.properties")) - self.data_table_name = props["scenarioYear"] + try: + load_properties = _m.Modeller().tool('sandag.utilities.properties') + props = load_properties(_join(_dir(self.source), "conf", "sandag_abm.properties")) + self.data_table_name = props["scenarioYear"] + except: + pass pb = _m.ToolPageBuilder(self) pb.title = "Import network" pb.description = """ - Create an Emme network from the E00 and associated files - generated from TCOVED. - The timed transfer is stored in data tables with the suffix "_timed_xfers_period". -
-
- The following files are used: + Create an Emme network from TNED geodatabase (*.gdb) and associated files. +
+
+ The following layers in the gdb are used: + + The following files are also used (in the same directory as the *.gdb): @@ -158,16 +170,15 @@ def page(self): pb.tool_run_status(self.tool_run_msg_status) pb.add_select_file("source", window_type="directory", file_filter="", - title="Source directory:",) + title="Source gdb:",) - pb.add_text_box("traffic_scenario_id", size=6, title="Scenario ID for traffic (optional):") - pb.add_text_box("transit_scenario_id", size=6, title="Scenario ID for transit (optional):") - pb.add_text_box("merged_scenario_id", size=6, title="Scenario ID for merged network:") + pb.add_text_box("scenario_id", size=6, title="Scenario ID for imported network:") pb.add_text_box("title", size=80, title="Scenario title:") pb.add_checkbox("save_data_tables", title=" ", label="Save reference data tables of file data") pb.add_text_box("data_table_name", size=80, title="Name for data tables:", note="Prefix name to use for all saved data tables") pb.add_checkbox("overwrite", title=" ", label="Overwrite existing scenarios and data tables") + pb.add_checkbox("create_time_periods", title=" ", label="Copy base scenario to all time periods and set modes (required for assignments)") return pb.render() @@ -186,15 +197,12 @@ def run(self): error, _traceback.format_exc()) raise - def __call__(self, source, - traffic_scenario_id=None, transit_scenario_id=None, merged_scenario_id=None, + def __call__(self, source, scenario_id, title="", save_data_tables=False, data_table_name="", overwrite=False, - emmebank=None): + emmebank=None, create_time_periods=True): self.source = source - self.traffic_scenario_id = traffic_scenario_id - self.transit_scenario_id = transit_scenario_id - self.merged_scenario_id = merged_scenario_id + self.scenario_id = scenario_id self.title = title self.save_data_tables = save_data_tables self.data_table_name = data_table_name @@ -203,11 +211,12 @@ def __call__(self, source, self.emmebank = _m.Modeller().emmebank else: self.emmebank = emmebank + self.create_time_periods = create_time_periods with self.setup(): self.execute() - return self.emmebank.scenario(merged_scenario_id) + return self.emmebank.scenario(scenario_id) @_context def setup(self): @@ -217,13 +226,12 @@ def setup(self): attributes = OrderedDict([ ("self", str(self)), ("source", self.source), - ("traffic_scenario_id", self.traffic_scenario_id), - ("transit_scenario_id", self.transit_scenario_id), - ("merged_scenario_id", self.merged_scenario_id), + ("scenario_id", self.scenario_id), ("title", self.title), ("save_data_tables", self.save_data_tables), ("data_table_name", self.data_table_name), ("overwrite", self.overwrite), + ("create_time_periods", self.create_time_periods) ]) self._log = [{ "content": attributes.items(), @@ -232,6 +240,8 @@ def setup(self): }] with _m.logbook_trace("Import network", attributes=attributes) as trace: gen_utils.log_snapshot("Import network", str(self), attributes) + load_properties = _m.Modeller().tool('sandag.utilities.properties') + self._props = load_properties(_join(_dir(_dir(self.source)), "conf", "sandag_abm.properties")) try: yield except Exception as error: @@ -242,657 +252,514 @@ def setup(self): fatal_error = True raise finally: + self._props = None self.log_report() + self._auto_mode_lookup = None + self._transit_mode_lookup = None if self._error: if fatal_error: - trace.write("Import network failed (%s errors)" % len(self._error), attributes=attributes) + trace.write("Import network failed (%s errors)" % len(self._error), attributes=attributes) else: trace.write("Import network completed (%s non-fatal errors)" % len(self._error), attributes=attributes) def execute(self): - traffic_attr_map = { - "NODE": { - "interchange": ("@interchange", "DERIVED", "EXTRA", "is interchange node"), - "HNODE": ("@hnode_hwy", "DERIVED","EXTRA", "HNODE label from hwycov" ), - "zone_id": ("@zone_id", "DERIVED", "EXTRA", "TAZ number a node is in"), - }, - "LINK": OrderedDict([ - ("HWYCOV-ID", ("@tcov_id", "TWO_WAY", "EXTRA", "SANDAG-assigned link ID")), - ("SPHERE", ("@sphere", "TWO_WAY", "EXTRA", "Jurisdiction sphere of influence")), - ("NM", ("#name", "TWO_WAY", "STRING", "Street name")), - ("FXNM", ("#name_from", "TWO_WAY", "STRING", "Cross street at the FROM end")), - ("TXNM", ("#name_to", "TWO_WAY", "STRING", "Cross street name at the TO end")), - ("DIR", ("@direction_cardinal", "TWO_WAY", "EXTRA", "Link direction")), - ("ASPD", ("@speed_adjusted", "TWO_WAY", "EXTRA", "Adjusted link speed (miles/hr)")), - ("IYR", ("@year_open_traffic", "TWO_WAY", "EXTRA", "The year the link opened to traffic")), - ("IPROJ", ("@project_code", "TWO_WAY", "EXTRA", "Project number for use with hwyproj.xls")), - ("IJUR", ("@jurisdiction_type", "TWO_WAY", "EXTRA", "Link jurisdiction type")), - ("IFC", ("type", "TWO_WAY", "STANDARD", "")), - ("IHOV", ("@lane_restriction", "TWO_WAY", "EXTRA", "Link operation type")), - ("ITRUCK", ("@truck_restriction", "TWO_WAY", "EXTRA", "Truck restriction code (ITRUCK)")), - ("ISPD", ("@speed_posted", "TWO_WAY", "EXTRA", "Posted speed limit (mph)")), - ("IMED", ("@median", "TWO_WAY", "EXTRA", "Median type")), - ("AU", ("@lane_auxiliary", "ONE_WAY", "EXTRA", "Number of auxiliary lanes")), - ("CNT", ("@traffic_control", "ONE_WAY", "EXTRA", "Intersection control type")), - ("TL", ("@turn_thru", "ONE_WAY", "EXTRA", "Intersection approach through lanes")), - ("RL", ("@turn_right", "ONE_WAY", "EXTRA", "Intersection approach right-turn lanes")), - ("LL", ("@turn_left", "ONE_WAY", "EXTRA", "Intersection approach left-turn lanes")), - ("GC", ("@green_to_cycle_init", "ONE_WAY", "EXTRA", "Initial green-to-cycle ratio")), - ("CHO", ("@capacity_hourly_op", "ONE_WAY", "EXTRA", "Off-Peak hourly mid-link capacity")), - ("CHA", ("@capacity_hourly_am", "ONE_WAY", "EXTRA", "AM Peak hourly mid-link capacity")), - ("CHP", ("@capacity_hourly_pm", "ONE_WAY", "EXTRA", "PM Peak hourly mid-link capacity")), - # These attributes are expanded from 3 time periods to 5 - ("ITOLLO", ("toll_op", "TWO_WAY", "INTERNAL", "Expanded to EA, MD and EV")), - ("ITOLLA", ("toll_am", "TWO_WAY", "INTERNAL", "")), - ("ITOLLP", ("toll_pm", "TWO_WAY", "INTERNAL", "")), - ("LNO", ("lane_op", "ONE_WAY", "INTERNAL", "Expanded to EA, MD and EV")), - ("LNA", ("lane_am", "ONE_WAY", "INTERNAL", "")), - ("LNP", ("lane_pm", "ONE_WAY", "INTERNAL", "")), - ("CPO", ("capacity_link_op", "ONE_WAY", "INTERNAL", "Expanded to EA, MD and EV")), - ("CPA", ("capacity_link_am", "ONE_WAY", "INTERNAL", "")), - ("CPP", ("capacity_link_pm", "ONE_WAY", "INTERNAL", "")), - ("CXO", ("capacity_inter_op", "ONE_WAY", "INTERNAL", "Expanded to EA, MD and EV")), - ("CXA", ("capacity_inter_am", "ONE_WAY", "INTERNAL", "")), - ("CXP", ("capacity_inter_pm", "ONE_WAY", "INTERNAL", "")), - ("TMO", ("time_link_op", "ONE_WAY", "INTERNAL", "Expanded to EA, MD and EV")), - ("TMA", ("time_link_am", "ONE_WAY", "INTERNAL", "")), - ("TMP", ("time_link_pm", "ONE_WAY", "INTERNAL", "")), - ("TXO", ("time_inter_op", "ONE_WAY", "INTERNAL", "Expanded to EA, MD and EV")), - ("TXA", ("time_inter_am", "ONE_WAY", "INTERNAL", "")), - ("TXP", ("time_inter_pm", "ONE_WAY", "INTERNAL", "")), - # These three attributes are used to cross-reference the turn directions - ("TLB", ("through_link", "ONE_WAY", "INTERNAL", "")), - ("RLB", ("right_link", "ONE_WAY", "INTERNAL", "")), - ("LLB", ("left_link", "ONE_WAY", "INTERNAL", "")), - ("@cost_operating", ("@cost_operating","DERIVED", "EXTRA", "Fuel and maintenance cost")), - ("INTDIST_UP", ("@intdist_up", "DERIVED", "EXTRA", "Upstream major intersection distance")), - ("INTDIST_DOWN", ("@intdist_down", "DERIVED", "EXTRA", "Downstream major intersection distance")), - ]) - } - time_period_attrs = OrderedDict([ - ("@cost_auto", "toll + cost autos"), - ("@cost_hov2", "toll (non-mngd) + cost HOV2"), - ("@cost_hov3", "toll (non-mngd) + cost HOV3+"), - ("@cost_lgt_truck", "toll + cost light trucks"), - ("@cost_med_truck", "toll + cost medium trucks"), - ("@cost_hvy_truck", "toll + cost heavy trucks"), - ("@cycle", "cycle length (minutes)"), - ("@green_to_cycle", "green to cycle ratio"), - ("@capacity_link", "mid-link capacity"), - ("@capacity_inter", "approach capacity"), - ("@toll", "toll cost (cent)"), - ("@lane", "number of lanes"), - ("@time_link", "link time in minutes"), - ("@time_inter", "intersection delay time"), - ("@sta_reliability", "static reliability") - ]) - time_name = { - "_ea": "Early AM ", "_am": "AM Peak ", "_md": "Mid-day ", "_pm": "PM Peak ", "_ev": "Evening " - } - time_periods = ["_ea", "_am", "_md", "_pm", "_ev"] - for attr, desc_tmplt in time_period_attrs.iteritems(): - for time in time_periods: - traffic_attr_map["LINK"][attr + time] = \ - (attr + time, "DERIVED", "EXTRA", time_name[time] + desc_tmplt) - - transit_attr_map = { - "NODE": OrderedDict([ - ("IPARK", ("@ipark", "DERIVED", "EXTRA", "parking indicator" )), - ("HNODE", ("@hnode_tr", "DERIVED", "EXTRA", "HNODE label from trcov" )), + attr_map = { + "NODE": OrderedDict([ + ("HNODE", ("@hnode", "BOTH", "EXTRA", "HNODE label from TNED" )), + ("TAP", ("@tap_id", "BOTH", "EXTRA", "TAP number")), + ("PARK", ("@park", "BOTH", "EXTRA", "parking indicator" )), + ("STOPTYPE", ("@stoptype", "BOTH", "EXTRA", "stop type indicator" )), + ("ELEV", ("@elev", "BOTH", "EXTRA", "station/stop elevation in feet")), + ("interchange", ("@interchange", "DERIVED", "EXTRA", "is interchange node")), ]), "LINK": OrderedDict([ - ("TRCOV-ID", ("@tcov_id", "TWO_WAY", "EXTRA", "SANDAG-assigned link ID")), - ("NM", ("#name", "TWO_WAY", "STRING", "Street name")), - ("FXNM", ("#name_from", "TWO_WAY", "STRING", "Cross street at the FROM end")), - ("TXNM", ("#name_to", "TWO_WAY", "STRING", "Cross street name at the TO end")), - ("DIR", ("@direction_cardinal", "TWO_WAY", "EXTRA", "Link direction")), - ("OSPD", ("@speed_observed", "TWO_WAY", "EXTRA", "Observed speed")), - ("IYR", ("@year_open_traffic", "TWO_WAY", "EXTRA", "The year the link opened to traffic ")), - ("IFC", ("type", "TWO_WAY", "STANDARD", "")), - ("IHOV", ("@lane_restriction_tr", "TWO_WAY", "EXTRA", "Link operation type")), - ("ISPD", ("@speed_posted_tr_l", "TWO_WAY", "EXTRA", "Posted speed limit (mph)")), - ("IMED", ("@median", "TWO_WAY", "EXTRA", "Median type")), - ("TMO", ("trtime_link_op", "ONE_WAY", "INTERNAL", "Expanded to EA, MD and EV")), - ("TMEA", ("@trtime_link_ea", "DERIVED", "EXTRA", "Early AM transit link time in minutes")), - ("TMA", ("@trtime_link_am", "ONE_WAY", "EXTRA", "AM Peak transit link time in minutes")), - ("TMMD", ("@trtime_link_md", "DERIVED", "EXTRA", "Mid-day transit link time in minutes")), - ("TMP", ("@trtime_link_pm", "ONE_WAY", "EXTRA", "PM Peak transit link time in minutes")), - ("TMEV", ("@trtime_link_ev", "DERIVED", "EXTRA", "Evening transit link time in minutes")), - ("MINMODE", ("@mode_hierarchy", "TWO_WAY", "EXTRA", "Transit mode type")), + ("HWYCOV0_ID",("@tcov_id", "TWO_WAY", "EXTRA", "SANDAG-assigned link ID")), + ("SPHERE", ("@sphere", "HWY_TWO_WAY", "EXTRA", "Jurisdiction sphere of influence")), + ("HWYSegGUID",("#hwyseg_guid", "TWO_WAY", "STRING", "HWYSegGUID")), + ("NM", ("#name", "TWO_WAY", "STRING", "Street name")), + ("FXNM", ("#name_from", "TWO_WAY", "STRING", "Cross street at the FROM end")), + ("TXNM", ("#name_to", "TWO_WAY", "STRING", "Cross street name at the TO end")), + ("DIR", ("@direction_cardinal", "TWO_WAY", "EXTRA", "Link direction")), + ("ASPD", ("@speed_adjusted", "HWY_TWO_WAY", "EXTRA", "Adjusted link speed (miles/hr)")), + ("YR", ("@year_open_traffic", "HWY_TWO_WAY", "EXTRA", "The year the link opened to traffic")), + ("PROJ", ("@project_code", "HWY_TWO_WAY", "EXTRA", "Project number for use with hwyproj.xls")), + ("FC", ("type", "TWO_WAY", "STANDARD", "")), + ("HOV", ("@hov", "TWO_WAY", "EXTRA", "Link operation type")), + ("MINMODE", ("@minmode", "TWO_WAY", "EXTRA", "Transit mode type")), + ("EATRUCK", ("@truck_ea", "HWY_TWO_WAY", "EXTRA", "Early AM truck restriction code ")), + ("AMTRUCK", ("@truck_am", "HWY_TWO_WAY", "EXTRA", "AM Peak truck restriction code ")), + ("MDTRUCK", ("@truck_md", "HWY_TWO_WAY", "EXTRA", "Mid-day truck restriction code ")), + ("PMTRUCK", ("@truck_pm", "HWY_TWO_WAY", "EXTRA", "PM Peak truck restriction code ")), + ("EVTRUCK", ("@truck_ev", "HWY_TWO_WAY", "EXTRA", "Evening truck restriction code ")), + ("TOLLEA", ("@toll_ea", "HWY_TWO_WAY", "EXTRA", "Early AM toll cost (cent)")), + ("TOLLA", ("@toll_am", "HWY_TWO_WAY", "EXTRA", "AM Peak toll cost (cent)")), + ("TOLLMD", ("@toll_md", "HWY_TWO_WAY", "EXTRA", "Mid-day toll cost (cent)")), + ("TOLLP", ("@toll_pm", "HWY_TWO_WAY", "EXTRA", "PM Peak toll cost (cent)")), + ("TOLLEV", ("@toll_ev", "HWY_TWO_WAY", "EXTRA", "Evening toll cost (cent)")), + + ("SPD", ("@speed_posted", "HWY_TWO_WAY", "EXTRA", "Posted speed limit (mph)")), + ("MED", ("@median", "TWO_WAY", "EXTRA", "Median type")), + ("AU", ("@lane_auxiliary", "HWY_ONE_WAY", "EXTRA", "Number of auxiliary lanes")), + ("CNT", ("@traffic_control", "HWY_ONE_WAY", "EXTRA", "Intersection control type")), + ("TL", ("@turn_thru", "HWY_ONE_WAY", "EXTRA", "Intersection approach through lanes")), + ("RL", ("@turn_right", "HWY_ONE_WAY", "EXTRA", "Intersection approach right-turn lanes")), + ("LL", ("@turn_left", "HWY_ONE_WAY", "EXTRA", "Intersection approach left-turn lanes")), + ("GC", ("@green_to_cycle_init", "HWY_ONE_WAY", "EXTRA", "Initial green-to-cycle ratio")), + ("WAY", ("way", "HWY_TWO_WAY", "INTERNAL", "")), + ("TRANSIT_MODES", ("transit_modes", "DERIVED", "INTERNAL", "")), + ("@cost_operating", ("@cost_operating", "DERIVED", "EXTRA", "Fuel and maintenance cost")), + ("INTDIST_UP", ("@intdist_up", "DERIVED", "EXTRA", "Upstream major intersection distance")), + ("INTDIST_DOWN", ("@intdist_down", "DERIVED", "EXTRA", "Downstream major intersection distance")), + + ("TMO", ("@trtime", "RAIL_TWO_WAY", "EXTRA", "link time in minutes")), + ("OSPD", ("@speed_observed", "RAIL_TWO_WAY", "EXTRA", "Observed speed")), + ]), "TRANSIT_LINE": OrderedDict([ - ("AM_Headway", ("@headway_am", "TRRT", "EXTRA", "AM Peak actual headway")), - ("PM_Headway", ("@headway_pm", "TRRT", "EXTRA", "PM Peak actual headway")), - ("OP_Headway", ("@headway_op", "TRRT", "EXTRA", "Off-Peak actual headway")), - ("Night_Headway", ("@headway_night", "TRRT", "EXTRA", "Night actual headway")), - ("Night_Headway_rev", ("@headway_rev_night", "DERIVED", "EXTRA", "Night Peak revised headway")), - ("AM_Headway_rev", ("@headway_rev_am", "DERIVED", "EXTRA", "AM Peak revised headway")), - ("PM_Headway_rev", ("@headway_rev_pm", "DERIVED", "EXTRA", "PM Peak revised headway")), - ("OP_Headway_rev", ("@headway_rev_op", "DERIVED", "EXTRA", "Off-Peak revised headway")), - ("WT_IVTPK", ("@vehicle_per_pk", "MODE5TOD", "EXTRA", "Peak in-vehicle perception factor")), - ("WT_IVTOP", ("@vehicle_per_op", "MODE5TOD", "EXTRA", "Off-Peak in-vehicle perception factor")), - ("WT_FAREPK", ("@fare_per_pk", "MODE5TOD", "EXTRA", "Peak fare perception factor")), - ("WT_FAREOP", ("@fare_per_op", "MODE5TOD", "EXTRA", "Off-Peak fare perception factor")), + ("AM_Headway", ("@headway_am", "TRRT", "EXTRA", "AM Peak actual headway")), + ("PM_Headway", ("@headway_pm", "TRRT", "EXTRA", "PM Peak actual headway")), + ("Midday_Headway", ("@headway_md", "TRRT", "EXTRA", "Midday actual headway")), + ("Evening_Headway",("@headway_ev", "TRRT", "EXTRA", "Evening actual headway")), + ("EarlyAM_Headway",("@headway_ea", "TRRT", "EXTRA", "Early AM actual headway")), + ("AM_Headway_rev", ("@headway_rev_am", "DERIVED", "EXTRA", "AM Peak revised headway")), + ("PM_Headway_rev", ("@headway_rev_pm", "DERIVED", "EXTRA", "PM Peak revised headway")), + ("MD_Headway_rev", ("@headway_rev_md", "DERIVED", "EXTRA", "Midday revised headway")), + ("EV_Headway_rev", ("@headway_rev_ev", "DERIVED", "EXTRA", "Evening revised headway")), + ("EA_Headway_rev", ("@headway_rev_ea", "DERIVED", "EXTRA", "Early AM revised headway")), + ("WT_IVTPK", ("@vehicle_per_pk", "MODE5TOD", "EXTRA", "Peak in-vehicle perception factor")), + ("WT_IVTOP", ("@vehicle_per_op", "MODE5TOD", "EXTRA", "Off-Peak in-vehicle perception factor")), + ("WT_FAREPK", ("@fare_per_pk", "MODE5TOD", "EXTRA", "Peak fare perception factor")), + ("WT_FAREOP", ("@fare_per_op", "MODE5TOD", "EXTRA", "Off-Peak fare perception factor")), ("DWELLTIME", ("default_dwell_time", "MODE5TOD", "INTERNAL", "")), - ("Fare", ("@fare", "TRRT", "EXTRA", "Boarding fare ($)")), - ("@transfer_penalty",("@transfer_penalty","DERIVED", "EXTRA", "Transfer penalty (min)")), - ("Route_ID", ("@route_id", "TRRT", "EXTRA", "Transit line internal ID")), - ("Night_Hours", ("@night_hours", "TRRT", "EXTRA", "Night hours")), - ("Config", ("@config", "TRRT", "EXTRA", "Config ID (same as route name)")), + ("Fare", ("@fare", "TRRT", "EXTRA", "Boarding fare ($)")), + ("@transfer_penalty",("@transfer_penalty","DERIVED", "EXTRA", "Transfer penalty (min)")), + ("Route_ID", ("@route_id", "TRRT", "EXTRA", "Transit line internal ID")), + ("EarlyAM_Hours", ("@hours_ea", "TRRT", "EXTRA", "Early AM hours")), + ("Evening_Hours", ("@hours_ev", "TRRT", "EXTRA", "Evening hours")), + ("Config", ("@config", "TRRT", "EXTRA", "Config ID (same as route name)")), ]), "TRANSIT_SEGMENT": OrderedDict([ ("Stop_ID", ("@stop_id", "TRSTOP", "EXTRA", "Stop ID from trcov")), ("Pass_Count", ("@pass_count", "TRSTOP", "EXTRA", "Number of times this stop is passed")), ("Milepost", ("@milepost", "TRSTOP", "EXTRA", "Distance from start of line")), - ("FareZone", ("@fare_zone", "TRSTOP", "EXTRA", "Fare zone ID")), ("StopName", ("#stop_name", "TRSTOP", "STRING", "Name of stop")), ("@coaster_fare_board", ("@coaster_fare_board", "DERIVED", "EXTRA", "Boarding fare for coaster")), ("@coaster_fare_inveh", ("@coaster_fare_inveh", "DERIVED", "EXTRA", "Incremental fare for Coaster")), ]) } + time_name = { + "_ea": "Early AM ", "_am": "AM Peak ", "_md": "Mid-day ", "_pm": "PM Peak ", "_ev": "Evening " + } + time_name_dst = ["_ea", "_am", "_md", "_pm", "_ev"] + time_name_src = ["EA", "A", "MD", "P", "EV"] + time_period_attrs = [ + ("CP", "@capacity_link", "mid-link capacity"), + ("CX", "@capacity_inter", "approach capacity"), + ("CH", "@capacity_hourly", "hourly mid-link capacity"), + ("LN", "@lane", "number of lanes"), + ("TM", "@time_link", "link time in minutes"), + ("TX", "@time_inter", "intersection delay time"), + ] + for src_attr, dst_attr, desc_tmplt in time_period_attrs: + for time_s, time_d in zip(time_name_src, time_name_dst): + attr_map["LINK"][src_attr + time_s] = \ + (dst_attr + time_d, "HWY_ONE_WAY", "EXTRA", time_name[time_d] + desc_tmplt) + derived_period_attrs = [ + ("@cost_auto", "toll + cost autos"), + ("@cost_hov2", "toll (non-mngd) + cost HOV2"), + ("@cost_hov3", "toll (non-mngd) + cost HOV3+"), + ("@cost_lgt_truck", "toll + cost light trucks"), + ("@cost_med_truck", "toll + cost medium trucks"), + ("@cost_hvy_truck", "toll + cost heavy trucks"), + ("@cycle", "cycle length (minutes)"), + ("@green_to_cycle", "green to cycle ratio"), + ("@sta_reliability", "static reliability") + ] + for attr, desc_tmplt in derived_period_attrs: + for time in time_name_dst: + attr_map["LINK"][attr + time] = \ + (attr + time, "DERIVED", "EXTRA", time_name[time] + desc_tmplt) + create_scenario = _m.Modeller().tool( "inro.emme.data.scenario.create_scenario") - file_names = [ - "hwycov.e00", "LINKTYPETURNS.DBF", "turns.csv", - "trcov.e00", "trrt.csv", "trlink.csv", "trstop.csv", - "timexfer_EA.csv", "timexfer_AM.csv","timexfer_MD.csv", - "timexfer_PM.csv","timexfer_EV.csv","MODE5TOD.csv", - ] - for name in file_names: - file_path = _join(self.source, name) - if not os.path.exists(file_path): - raise Exception("missing file '%s' in directory %s" % (name, self.source)) - title = self.title if not title: - existing_scenario = self.emmebank.scenario(self.merged_scenario_id) + existing_scenario = self.emmebank.scenario(self.scenario_id) if existing_scenario: title = existing_scenario.title - def create_attributes(scenario, attr_map): - for elem_type, mapping in attr_map.iteritems(): - for name, _tcoved_type, emme_type, desc in mapping.values(): - if emme_type == "EXTRA": - if not scenario.extra_attribute(name): - xatt = scenario.create_extra_attribute(elem_type, name) + scenario = create_scenario(self.scenario_id, title, overwrite=self.overwrite, emmebank=self.emmebank) + scenarios = [scenario] + if self.create_time_periods: + periods=["EA", "AM", "MD", "PM", "EV"] + period_ids = list(enumerate(periods, start=int(self.scenario_id) + 1)) + for ident, period in period_ids: + scenarios.append(create_scenario(ident, "%s - %s assign" % (title, period), + overwrite=self.overwrite, emmebank=self.emmebank)) + # create attributes in scenario + for elem_type, mapping in attr_map.iteritems(): + for name, _tcoved_type, emme_type, desc in mapping.values(): + if emme_type == "EXTRA": + for s in scenarios: + if not s.extra_attribute(name): + xatt = s.create_extra_attribute(elem_type, name) xatt.description = desc - elif emme_type == "STRING": - if not scenario.network_field(elem_type, name): - scenario.create_network_field(elem_type, name, 'STRING', description=desc) - - if self.traffic_scenario_id: - traffic_scenario = create_scenario( - self.traffic_scenario_id, title + " Traffic", - overwrite=self.overwrite, emmebank=self.emmebank) - create_attributes(traffic_scenario, traffic_attr_map) - else: - traffic_scenario = None - if self.transit_scenario_id: - transit_scenario = create_scenario( - self.transit_scenario_id, title + " Transit", - overwrite=self.overwrite, emmebank=self.emmebank) - create_attributes(transit_scenario, transit_attr_map) - else: - transit_scenario = None - if self.merged_scenario_id: - scenario = create_scenario( - self.merged_scenario_id, title, - overwrite=self.overwrite, emmebank=self.emmebank) - create_attributes(scenario, traffic_attr_map) - create_attributes(scenario, transit_attr_map) - else: - scenario = traffic_scenario or transit_scenario - - traffic_network = _network.Network() - transit_network = _network.Network() + elif emme_type == "STRING": + for s in scenarios: + if not s.network_field(elem_type, name): + s.create_network_field(elem_type, name, 'STRING', description=desc) + + log_content = [] + for k, v in mapping.iteritems(): + if v[3] == "DERIVED": + k = "--" + log_content.append([k] + list(v)) + self._log.append({ + "content": log_content, + "type": "table", + "header": ["TNED", "Emme", "Source", "Type", "Description"], + "title": "Network %s attributes" % elem_type.lower().replace("_", " "), + "disclosure": True + }) + + network = _network.Network() + for elem_type, mapping in attr_map.iteritems(): + for field, (attr, tcoved_type, emme_type, desc) in mapping.iteritems(): + if emme_type == "STANDARD": + continue + default = "" if emme_type == "STRING" else 0 + network.create_attribute(elem_type, attr, default) try: - if self.traffic_scenario_id or self.merged_scenario_id: - for elem_type, attrs in traffic_attr_map.iteritems(): - log_content = [] - for k, v in attrs.iteritems(): - if v[3] == "DERIVED": - k = "--" - log_content.append([k] + list(v)) - self._log.append({ - "content": log_content, - "type": "table", - "header": ["TCOVED", "Emme", "Source", "Type", "Description"], - "title": "Traffic %s attributes" % elem_type.lower().replace("_", " "), - "disclosure": True - }) - try: - self.create_traffic_base(traffic_network, traffic_attr_map) - self.create_turns(traffic_network) - self.calc_traffic_attributes(traffic_network) - self.check_zone_access(traffic_network, traffic_network.mode("d")) - finally: - if traffic_scenario: - traffic_scenario.publish_network(traffic_network, resolve_attributes=True) - - if self.transit_scenario_id or self.merged_scenario_id: - for elem_type, attrs in transit_attr_map.iteritems(): - log_content = [] - for k, v in attrs.iteritems(): - if v[3] == "DERIVED": - k = "--" - log_content.append([k] + list(v)) - self._log.append({ - "content": log_content, - "type": "table", - "header": ["TCOVED", "Emme", "Source", "Type", "Description"], - "title": "Transit %s attributes" % elem_type.lower().replace("_", " "), - "disclosure": True - }) - try: - self.create_transit_base(transit_network, transit_attr_map) - self.create_transit_lines(transit_network, transit_attr_map) - self.calc_transit_attributes(transit_network) - new_node_id = max( - max(n.number for n in traffic_network.nodes()), - max(n.number for n in transit_network.nodes()) - ) - new_node_id = int(_ceiling(new_node_id / 10000.0) * 10000) - new_node_id = self.renumber_transit_nodes(transit_network, new_node_id) - finally: - if transit_scenario: - for link in transit_network.links(): - if link.type <= 0: - link.type = 99 - transit_scenario.publish_network(transit_network, resolve_attributes=True) - if self.merged_scenario_id: - self.add_transit_to_traffic(traffic_network, transit_network, new_node_id) + self.create_modes(network) + self.create_road_base(network, attr_map) + self.create_turns(network) + self.calc_traffic_attributes(network) + self.check_zone_access(network, network.mode("d")) + self.create_rail_base(network, attr_map) + self.create_transit_lines(network, attr_map) + self.calc_transit_attributes(network) finally: - if self.merged_scenario_id: - scenario.publish_network(traffic_network, resolve_attributes=True) + # TAP connectors included in network, fix type setting and renumber node IDs + for link in network.links(): + if link.type <= 0: + link.type = 99 + self.renumber_base_nodes(network) + scenario.publish_network(network, resolve_attributes=True) self.set_functions(scenario) self.check_connectivity(scenario) - def create_traffic_base(self, network, attr_map): - self._log.append({"type": "header", "content": "Import traffic base network from hwycov.e00"}) - hwy_data = gen_utils.DataTableProc("ARC", _join(self.source, "hwycov.e00")) - - if self.save_data_tables: - hwy_data.save("%s_hwycov" % self.data_table_name, self.overwrite) - - for elem_type in "NODE", "TURN": - mapping = attr_map.get(elem_type) - if not mapping: - continue - for field, (attr, tcoved_type, emme_type, desc) in mapping.iteritems(): - default = "" if emme_type == "STRING" else 0 - network.create_attribute(elem_type, attr, default) - - # Create Modes - dummy_auto = network.create_mode("AUTO", "d") - hov2 = network.create_mode("AUX_AUTO", "h") - hov2_toll = network.create_mode("AUX_AUTO", "H") - hov3 = network.create_mode("AUX_AUTO", "i") - hov3_toll = network.create_mode("AUX_AUTO", "I") - sov = network.create_mode("AUX_AUTO", "s") - sov_toll = network.create_mode("AUX_AUTO", "S") - heavy_trk = network.create_mode("AUX_AUTO", "v") - heavy_trk_toll = network.create_mode("AUX_AUTO", "V") - medium_trk = network.create_mode("AUX_AUTO", "m") - medium_trk_toll = network.create_mode("AUX_AUTO", "M") - light_trk = network.create_mode("AUX_AUTO", "t") - light_trk_toll = network.create_mode("AUX_AUTO", "T") - - dummy_auto.description = "dummy auto" - sov.description = "SOV" - hov2.description = "HOV2" - hov3.description = "HOV3+" - light_trk.description = "TRKL" - medium_trk.description = "TRKM" - heavy_trk.description = "TRKH" - - sov_toll.description = "SOV TOLL" - hov2_toll.description = "HOV2 TOLL" - hov3_toll.description = "HOV3+ TOLL" - light_trk_toll.description = "TRKL TOLL" - medium_trk_toll.description = "TRKM TOLL" - heavy_trk_toll.description = "TRKH TOLL" - - is_centroid = lambda arc, node : (arc["IFC"] == 10) and (node == "AN") - - # Note: only truck types 1, 3, 4, and 7 found in 2012 base network - modes_gp_lanes= { - 1: set([dummy_auto, sov, hov2, hov3, light_trk, medium_trk, heavy_trk, - sov_toll, hov2_toll, hov3_toll, light_trk_toll, medium_trk_toll, - heavy_trk_toll]), - 2: set([dummy_auto, sov, hov2, hov3, light_trk, medium_trk, - sov_toll, hov2_toll, hov3_toll, light_trk_toll, medium_trk_toll]), - 3: set([dummy_auto, sov, hov2, hov3, light_trk, sov_toll, hov2_toll, - hov3_toll, light_trk_toll]), - 4: set([dummy_auto, sov, hov2, hov3, sov_toll, hov2_toll, hov3_toll]), - 5: set([dummy_auto, heavy_trk, heavy_trk_toll]), - 6: set([dummy_auto, medium_trk, heavy_trk, medium_trk_toll, heavy_trk_toll]), - 7: set([dummy_auto, light_trk, medium_trk, heavy_trk, light_trk_toll, - medium_trk_toll, heavy_trk_toll]), + if "modify_network.py" in os.listdir(os.getcwd()): + try: + with _m.logbook_trace("Modify network script"): + import modify_network + reload(modify_network) + modify_network.run(base_scenario) + except ImportError as e: + pass + network = base_scenario.get_network() + network.create_attribute("LINK", "transit_modes") + + if self.create_time_periods: + for link in network.links(): + link.transit_modes = link.modes + for ident, period in period_ids: + self.set_auto_modes(network, period) + scenario = self.emmebank.scenario(ident) + scenario.publish_network(network, resolve_attributes=True) + + def create_modes(self, network): + # combined traffic and transit mode creation + mode_table = { + "AUTO": [("d", "dummy auto")], + "AUX_AUTO": [ + ("s", "SOV"), + ("h", "HOV2"), + ("i", "HOV3+"), + ("t", "TRKL"), + ("m", "TRKM"), + ("v", "TRKH"), + ("S", "SOV TOLL"), + ("H", "HOV2 TOLL"), + ("I", "HOV3+ TOLL"), + ("T", "TRKL TOLL"), + ("M", "TRKM TOLL"), + ("V", "TRKH TOLL"), + ], + "TRANSIT": [ + ("b", "BUS" ), # (vehicle type 100, PCE=3.0) + ("e", "EXP BUS"), # (vehicle type 90 , PCE=3.0) + ("p", "LTDEXP BUS"), # (vehicle type 80 , PCE=3.0) + ("l", "LRT"), # (vehicle type 50) + ("y", "BRT YEL"), # (vehicle type 60 , PCE=3.0) + ("r", "BRT RED"), # (vehicle type 70 , PCE=3.0) + ("c", "CMR"), # (vehicle type 40) + ("o", "TIER1"), # (vehicle type 45) + ], + "AUX_TRANSIT": [ + ("a", "ACCESS", 3), + ("x", "TRANSFER", 3), + ("w", "WALK", 3), + ("u", "ACCESS_WLK", 3), + ("k", "EGRESS_WLK", 3), + ("f", "ACCESS_PNR", 25), + ("g", "EGRESS_PNR", 25), + ("q", "ACCESS_KNR", 25), + ("j", "EGRESS_KNR", 25), + ("Q", "ACCESS_TNC", 25), + ("J", "EGRESS_TNC", 25), + ], } - modes_toll_lanes = { - 1: set([dummy_auto, sov_toll, hov2_toll, hov3_toll, light_trk_toll, - medium_trk_toll, heavy_trk_toll]), - 2: set([dummy_auto, sov_toll, hov2_toll, hov3_toll, light_trk_toll, - medium_trk_toll]), - 3: set([dummy_auto, sov_toll, hov2_toll, hov3_toll, light_trk_toll]), - 4: set([dummy_auto, sov_toll, hov2_toll, hov3_toll]), - 5: set([dummy_auto, heavy_trk_toll]), - 6: set([dummy_auto, medium_trk_toll, heavy_trk_toll]), - 7: set([dummy_auto, light_trk_toll, medium_trk_toll, heavy_trk_toll]), + for mode_type, modes in mode_table.iteritems(): + for mode_info in modes: + mode = network.create_mode(mode_type, mode_info[0]) + mode.description = mode_info[1] + if len(mode_info) == 3: + mode.speed = mode_info[2] + self._transit_mode_lookup = { + 0: set([]), + 1: set([network.mode(m_id) for m_id in "x"]), # 1 = special transfer walk links between certain nearby stops + 2: set([network.mode(m_id) for m_id in "w"]), # 2 = walk links in the downtown area + 3: set([network.mode(m_id) for m_id in "a"]), # 3 = the special TAP connectors + 400: set([network.mode(m_id) for m_id in "c"]), # 4 = Coaster Rail Line + 500: set([network.mode(m_id) for m_id in "l"]), # 5 = Trolley & Light Rail Transit (LRT) + 600: set([network.mode(m_id) for m_id in "bpeyr"]), # 6 = Yellow Car Bus Rapid Transit (BRT) + 700: set([network.mode(m_id) for m_id in "bpeyr"]), # 7 = Red Car Bus Rapid Transit (BRT) + 800: set([network.mode(m_id) for m_id in "bpe"]), # 8 = Limited Express Bus + 900: set([network.mode(m_id) for m_id in "bpe"]), # 9 = Express Bus + 1000: set([network.mode(m_id) for m_id in "bpe"]), # 10 = Local Bus + 11: set([network.mode(m_id) for m_id in "u"]), # = access walk links + 12: set([network.mode(m_id) for m_id in "k"]), # = egress walk links + 13: set([network.mode(m_id) for m_id in "f"]), # = access PNR links + 14: set([network.mode(m_id) for m_id in "g"]), # = egress PNR links + 15: set([network.mode(m_id) for m_id in "q"]), # = access KNR links + 16: set([network.mode(m_id) for m_id in "j"]), # = egress KNR links + 17: set([network.mode(m_id) for m_id in "Q"]), # = access TNC links + 18: set([network.mode(m_id) for m_id in "J"]), # = egress TNC links + } + modes_gp_lanes = { + 0: set([]), + 1: set([network.mode(m_id) for m_id in "dvmtshiVMTSHI"]), # all modes + 2: set([network.mode(m_id) for m_id in "dmtshiMTSHI"]), # no heavy truck + 3: set([network.mode(m_id) for m_id in "dtshiTSHI"]), # no heavy or medium truck + 4: set([network.mode(m_id) for m_id in "dshiSHI"]), # no truck + 5: set([network.mode(m_id) for m_id in "dvV"]), # only heavy trucks + 6: set([network.mode(m_id) for m_id in "dvmVM"]), # heavy and medium trucks + 7: set([network.mode(m_id) for m_id in "dvmtVMT"]), # all trucks only (no passenger cars) + } + non_toll_modes = set([network.mode(m_id) for m_id in "vmtshi"]) + self._auto_mode_lookup = { + "GP": modes_gp_lanes, + "TOLL": dict((k, v - non_toll_modes) for k, v in modes_gp_lanes.iteritems()), + "HOV2": set([network.mode(m_id) for m_id in "dhiHI"]), + "HOV3": set([network.mode(m_id) for m_id in "diI"]), } - modes_HOV2 = set([dummy_auto, hov2, hov3, hov2_toll, hov3_toll]) - modes_HOV3 = set([dummy_auto, hov3, hov3_toll]) - - def define_modes(arc): - if arc["IFC"] == 10: # connector - return modes_gp_lanes[1] - elif arc["IHOV"] == 1: - return modes_gp_lanes[arc["ITRUCK"]] - elif arc["IHOV"] == 2: + def set_auto_modes(self, network, period): + # time periods + # need to update the modes from the XTRUCK for their time of day + # Note: only truck types 1, 3, 4, and 7 found in 2012 base network + truck = "@truck_%s" % period.lower() + toll = "@toll_%s" % period.lower() + lookup = self._auto_mode_lookup + for link in network.links(): + auto_modes = set([]) + if link.type == 10: # connector + auto_modes = lookup["GP"][link[truck]] + elif link.type in [11, 12]: + pass # no auto modes, rail only (11) or bus only (12) + elif link["@hov"] == 1: + auto_modes = lookup["GP"][link[truck]] + elif link["@hov"] in [2, 3]: # managed lanes, free for HOV2 and HOV3+, tolls for SOV - if arc["ITOLLO"] + arc["ITOLLA"] + arc["ITOLLP"] > 0: - return modes_toll_lanes[arc["ITRUCK"]] | modes_HOV2 + if link[toll] > 0: + auto_modes = lookup["TOLL"][link[truck]] # special case of I-15 managed lanes base year and 2020, no build - elif arc["IFC"] == 1 and arc["IPROJ"] in [41, 42, 486, 373, 711]: - return modes_toll_lanes[arc["ITRUCK"]] | modes_HOV2 - elif arc["IFC"] == 8 or arc["IFC"] == 9: - return modes_toll_lanes[arc["ITRUCK"]] | modes_HOV2 - else: - return modes_HOV2 - elif arc["IHOV"] == 3: - # managed lanes, free for HOV3+, tolls for SOV and HOV2 - if arc["ITOLLO"] + arc["ITOLLA"] + arc["ITOLLP"] > 0: - return modes_toll_lanes[arc["ITRUCK"]] | modes_HOV3 - # special case of I-15 managed lanes for base year and 2020, no build - elif arc["IFC"] == 1 and arc["IPROJ"] in [41, 42, 486, 373, 711]: - return modes_toll_lanes[arc["ITRUCK"]] | modes_HOV3 - elif arc["IFC"] == 8 or arc["IFC"] == 9: - return modes_toll_lanes[arc["ITRUCK"]] | modes_HOV3 + elif link.type == 1 and link["@project_code"] in [41, 42, 486, 373, 711]: + auto_modes = lookup["TOLL"][link[truck]] + elif link.type == 8 or link.type == 9: + auto_modes = lookup["TOLL"][link[truck]] + if link["@hov"] == 2: + auto_modes = auto_modes | lookup["HOV2"] else: - return modes_HOV3 - elif arc["IHOV"] == 4: - return modes_toll_lanes[arc["ITRUCK"]] - else: - return modes_gp_lanes[arc["ITRUCK"]] + auto_modes = auto_modes | lookup["HOV3"] + elif link["@hov"] == 4: + auto_modes = lookup["TOLL"][link[truck]] + link.modes = link.transit_modes | auto_modes + + def create_road_base(self, network, attr_map): + self._log.append({"type": "header", "content": "Import roadway base network from TNED_HwyNet %s" % self.source}) + hwy_data = gen_utils.DataTableProc("TNED_HwyNet", self.source) + # TEMP workaround: BN field is string + bn_index = hwy_data._attr_names.index("BN") + hwy_data._values[bn_index] = hwy_data._values[bn_index].astype(int) + + if self.save_data_tables: + hwy_data.save("%s_TNED_HwyNet" % self.data_table_name, self.overwrite) + + is_centroid = lambda arc, node : (arc["FC"] == 10) and (node == "AN") + link_attr_map = {} + for field, (name, tcoved_type, emme_type, desc) in attr_map["LINK"].iteritems(): + if tcoved_type in ("TWO_WAY", "HWY_TWO_WAY", "ONE_WAY", "HWY_ONE_WAY"): + link_attr_map[field] = (name, tcoved_type.replace("HWY_", ""), emme_type, desc) + + auto_mode = network.mode("d") + + def define_modes(arc): + vehicle_index = int(arc["MINMODE"] / 100)*100 + aux_index = int(arc["MINMODE"] % 100) + veh_modes = self._transit_mode_lookup.get(vehicle_index, set([])) + aux_modes = self._transit_mode_lookup.get(aux_index, set([])) + modes = veh_modes | aux_modes + if arc["FC"] not in [11, 12, 99] and arc["HOV"] != 0: + modes |= set([auto_mode]) + return modes self._create_base_net( - hwy_data, network, mode_callback=define_modes, centroid_callback=is_centroid, - arc_id_name="HWYCOV-ID", link_attr_map=attr_map["LINK"]) - hwy_node_data = gen_utils.E00FileProc("HWYCOV.NAT", _join(self.source, "hwycov.e00")) + hwy_data, network, mode_callback=define_modes, centroid_callback=is_centroid, link_attr_map=link_attr_map) + + hwy_node_data = gen_utils.DataTableProc("TNED_HwyNodes", self.source) + node_attrs = [(k, v[0]) for k, v in attr_map["NODE"].iteritems() + if v[1] in ("BOTH", "HWY")] for record in hwy_node_data: - node = network.node(record["HWYCOV-ID"]) + node = network.node(record["HNODE"]) if node: - node["@hnode_hwy"] = record["HNODE"] + for src, dst in node_attrs: + node[dst] = record[src] + else: + self._log.append({"type": "text", "content": "Cannot find node %s" % record["HNODE"]}) self._log.append({"type": "text", "content": "Import traffic base network complete"}) - def create_transit_base(self, network, attr_map): - self._log.append({"type": "header", "content": "Import transit base network from trcov.e00"}) - load_properties = _m.Modeller().tool('sandag.utilities.properties') - props = load_properties(_join(_dir(self.source), "conf", "sandag_abm.properties")) - transit_data = gen_utils.DataTableProc("ARC", _join(self.source, "trcov.e00")) + def create_rail_base(self, network, attr_map): + self._log.append({"type": "header", "content": "Import rail base network from TNED_RailNet %s" % self.source}) + transit_data = gen_utils.DataTableProc("TNED_RailNet", self.source) if self.save_data_tables: - transit_data.save("%s_trcov" % self.data_table_name, self.overwrite) - - # aux mode speed is always 3 (miles/hr) - access = network.create_mode("AUX_TRANSIT", "a") - transfer = network.create_mode("AUX_TRANSIT", "x") - walk = network.create_mode("AUX_TRANSIT", "w") - AccWlk = network.create_mode("AUX_TRANSIT", "u") - EgrWlk = network.create_mode("AUX_TRANSIT", "k") - AccPNR = network.create_mode("AUX_TRANSIT", "f") - EgrPNR = network.create_mode("AUX_TRANSIT", "g") - AccKNR = network.create_mode("AUX_TRANSIT", "q") - EgrKNR = network.create_mode("AUX_TRANSIT", "j") - AccTNC = network.create_mode("AUX_TRANSIT", "Q") - EgrTNC = network.create_mode("AUX_TRANSIT", "J") - - bus = network.create_mode("TRANSIT", "b") - express_bus = network.create_mode("TRANSIT", "e") - ltdexp_bus = network.create_mode("TRANSIT", "p") - brt_red = network.create_mode("TRANSIT", "r") - brt_yellow = network.create_mode("TRANSIT", "y") - lrt = network.create_mode("TRANSIT", "l") - coaster_rail = network.create_mode("TRANSIT", "c") - tier1 = network.create_mode("TRANSIT", "o") - - access.description = "ACCESS" - transfer.description = "TRANSFER" - walk.description = "WALK" - AccWlk.description = "ACCESS_WLK" - EgrWlk.description = "EGRESS_WLK" - AccPNR.description = "ACCESS_PNR" - EgrPNR.description = "EGRESS_PNR" - AccKNR.description = "ACCESS_KNR" - EgrKNR.description = "EGRESS_KNR" - AccTNC.description = "ACCESS_TNC" - EgrTNC.description = "EGRESS_TNC" - bus.description = "BUS" # (vehicle type 100, PCE=3.0) - express_bus.description = "EXP BUS" # (vehicle type 90 , PCE=3.0) - ltdexp_bus.description = "LTDEXP BUS" # (vehicle type 80 , PCE=3.0) - lrt.description = "LRT" # (vehicle type 50) - brt_yellow.description = "BRT YEL" # (vehicle type 60 , PCE=3.0) - brt_red.description = "BRT RED" # (vehicle type 70 , PCE=3.0) - coaster_rail.description = "CMR" # (vehicle type 40) - tier1.description = "TIER1" # (vehicle type 45) - - access.speed = 3 - transfer.speed = 3 - walk.speed = 3 - AccWlk.speed = 3 - EgrWlk.speed = 3 - AccPNR.speed = 25 - EgrPNR.speed = 25 - AccKNR.speed = 25 - EgrKNR.speed = 25 - AccTNC.speed = 25 - EgrTNC.speed = 25 - - ## define TAP connectors as centroids - #is_centroid = lambda arc, node: (int(arc["MINMODE"]) == 3) and (node == "BN") - - #replaced with centroid definition of highway - is_centroid = lambda arc, node : (arc["IFC"] == 10) and (node == "AN") - ##Do not create any centroird related stuff - #is_centroid = lambda arc, node : False - #network.delete_node() - mode_setting = { - 1: set([transfer]), # 1 = special transfer walk links between certain nearby stops - 2: set([walk]), # 2 = walk links in the downtown area - 3: set([access]), # 3 = the special TAP connectors - 4: set([coaster_rail]), # 4 = Coaster Rail Line - 5: set([lrt]), # 5 = Light Rail Transit (LRT) Line - 6: set([brt_yellow, ltdexp_bus, express_bus, bus]), # 6 = Yellow Car Bus Rapid Transit (BRT) - 7: set([brt_red, ltdexp_bus, express_bus, bus]), # 7 = Red Car Bus Rapid Transit (BRT) - 8: set([ltdexp_bus, express_bus, bus]), # 8 = Limited Express Bus - 9: set([ltdexp_bus, express_bus, bus]), # 9 = Express Bus - 10: set([ltdexp_bus, express_bus, bus]), # 10 = Local Bus - 11: set([AccWlk]), # 4 = access walk links - 12: set([EgrWlk]), # 5 = egress walk links - 13: set([AccPNR]), # 6 = access PNR links - 14: set([EgrPNR]), # 7 = egress PNR links - 15: set([AccKNR]), # 8 = access KNR links - 16: set([EgrKNR]), # 9 = egress KNR links - 17: set([AccTNC]), # 8 = access TNC links - 18: set([EgrTNC]), # 9 = egress TNC links - } - tier1_rail_link_name = props["transit.newMode"] + transit_data.save("%s_TNED_RailNet" % self.data_table_name, self.overwrite) + + link_attr_map = {} + for field, (name, tcoved_type, emme_type, desc) in attr_map["LINK"].iteritems(): + if tcoved_type in ("TWO_WAY", "RAIL_TWO_WAY", "ONE_WAY", "RAIL_ONE_WAY"): + link_attr_map[field] = (name, tcoved_type.replace("RAIL_", ""), emme_type, desc) + + tier1_modes = set([network.mode(m_id) for m_id in "o"]) + tier1_rail_link_name = self._props["transit.newMode"] def define_modes(arc): if arc["NM"] == tier1_rail_link_name: - return set([tier1]) - return mode_setting[arc["MINMODE"]] - - arc_filter = lambda arc: (arc["MINMODE"] > 2) + return tier1_modes + vehicle_index = int(arc["MINMODE"] / 100)*100 + aux_index = int(arc["MINMODE"] % 100) + return self._transit_mode_lookup[vehicle_index] | self._transit_mode_lookup[aux_index] - # first pass to create the main base network for vehicles, xfer links and TAPs self._create_base_net( - transit_data, network, mode_callback=define_modes, centroid_callback=is_centroid, - arc_id_name="TRCOV-ID", link_attr_map=attr_map["LINK"], arc_filter=arc_filter) + transit_data, network, mode_callback=define_modes, link_attr_map=link_attr_map) - # second pass to add special walk links / modify modes on existing links - reverse_dir_map = {1:3, 3:1, 2:4, 4:2, 0:0} - - def set_reverse_link(link, modes): - reverse_link = link.reverse_link - if reverse_link: - reverse_link.modes |= modes - else: - reverse_link = network.create_link(link.j_node, link.i_node, modes) - for attr in network.attributes("LINK"): - reverse_link[attr] = link[attr] - reverse_link["@direction_cardinal"] = reverse_dir_map[link["@direction_cardinal"]] - reverse_link["@tcov_id"] = -1*link["@tcov_id"] - reverse_link.vertices = list(reversed(link.vertices)) - - def epsilon_compare(a, b, epsilon): - return abs((a - b) / (a if abs(a) > 1 else 1)) <= epsilon - - for arc in transit_data: - # possible improvement: snap walk nodes to nearby node if not matched and within distance - if arc_filter(arc): - continue - if float(arc["AN"]) == 0 or float(arc["BN"]) == 0: - self._log.append({"type": "text", - "content": "Node ID 0 in AN (%s) or BN (%s) for link ID %s." % - (arc["AN"], arc["BN"], arc["TRCOV-ID"])}) - continue - coordinates = arc["geo_coordinates"] - arc_length = arc["LENGTH"] / 5280.0 # convert feet to miles - i_node = get_node(network, arc['AN'], coordinates[0]) - j_node = get_node(network, arc['BN'], coordinates[-1]) - modes = define_modes(arc) - link = network.link(i_node, j_node) - split_link_case = False - if link: - link.modes |= modes - else: - # Note: additional cases of "tunnel" walk links could be - # considered to optimize network matching - # check if this a special "split" link case where - # we do not need to add a "tunnel" walk link - for link1 in i_node.outgoing_links(): - if split_link_case: - break - for link2 in link1.j_node.outgoing_links(): - if link2.j_node == j_node: - if epsilon_compare(link1.length + link2.length, arc_length, 10**-5): - self._log.append({"type": "text", - "content": "Walk link AN %s BN %s matched to two links TCOV-ID %s, %s" % - (arc['AN'], arc['BN'], link1["@tcov_id"], link2["@tcov_id"])}) - link1.modes |= modes - link2.modes |= modes - set_reverse_link(link1, modes) - set_reverse_link(link2, modes) - split_link_case = True - break - if not split_link_case: - link = network.create_link(i_node, j_node, modes) - link.length = arc_length - if len(coordinates) > 2: - link.vertices = coordinates[1:-1] - if not split_link_case: - set_reverse_link(link, modes) - - for attr, _, emme_type, _ in attr_map["NODE"].itervalues(): - default = "" if emme_type == "STRING" else 0 - network.create_attribute("NODE", attr, default) - transit_node_data = gen_utils.E00FileProc("TRCOV.NAT", _join(self.source, "trcov.e00")) - # Load IPARK data onto transit nodes + transit_node_data = gen_utils.DataTableProc("TNED_RailNodes", self.source) + # Load PARK, elevation, stop type data onto transit nodes + node_attrs = [(k, v[0]) for k, v in attr_map["NODE"].iteritems() + if v[1] in ("BOTH", "RAIL")] for record in transit_node_data: - node = network.node(record["TRCOV-ID"]) + node = network.node(record["HNODE"]) if node: - node["@ipark"] = record["IPARK"] - node["@hnode_tr"] = record["HNODE"] + for src, dst in node_attrs: + node[dst] = record[src] + else: + self._log.append({"type": "text", "content": "Cannot find node %s" % record["HNODE"]}) self._log.append({"type": "text", "content": "Import transit base network complete"}) - def _create_base_net(self, data, network, mode_callback, centroid_callback, arc_id_name, link_attr_map, arc_filter=None): + def _create_base_net(self, data, network, link_attr_map, mode_callback, centroid_callback=None): forward_attr_map = {} reverse_attr_map = {} + arc_id_name = "HWYCOV0_ID" + arc_guid_name = "HWYSegGUID" for field, (name, tcoved_type, emme_type, desc) in link_attr_map.iteritems(): - if emme_type != "STANDARD": - default = "" if emme_type == "STRING" else 0 - network.create_attribute("LINK", name, default) - - if field in [arc_id_name, "DIR"]: + if field in [arc_id_name, arc_guid_name, "DIR"]: # these attributes are special cases for reverse link forward_attr_map[field] = name - elif tcoved_type == "TWO_WAY": + elif tcoved_type in "TWO_WAY": forward_attr_map[field] = name reverse_attr_map[field] = name - elif tcoved_type == "ONE_WAY": + elif tcoved_type in "ONE_WAY": forward_attr_map["AB" + field] = name reverse_attr_map["BA" + field] = name emme_id_name = forward_attr_map[arc_id_name] + emme_guid_name = forward_attr_map[arc_guid_name] dir_name = forward_attr_map["DIR"] - reverse_dir_map = {1:3, 3:1, 2:4, 4:2, 0:0} + reverse_dir_map = {1: 3, 3: 1, 2: 4, 4: 2, 0: 0} new_node_id = max(data.values("AN").max(), data.values("BN").max()) + 1 - if arc_filter is None: - arc_filter = lambda arc : True + + if centroid_callback is None: + centroid_callback = lambda a,n: False # Create nodes and links for arc in data: - if not arc_filter(arc): - continue if float(arc["AN"]) == 0 or float(arc["BN"]) == 0: self._log.append({"type": "text", - "content": "Node ID 0 in AN (%s) or BN (%s) for link ID %s." % - (arc["AN"], arc["BN"], arc[arc_id_name])}) + "content": "Node ID 0 in AN (%s) or BN (%s) for link GUID/ID %s/%s." % + (arc["AN"], arc["BN"], arc[arc_guid_name], arc[arc_id_name])}) continue coordinates = arc["geo_coordinates"] i_node = get_node(network, arc['AN'], coordinates[0], centroid_callback(arc, "AN")) j_node = get_node(network, arc['BN'], coordinates[-1], centroid_callback(arc, "BN")) - existing_link = network.link(i_node, j_node) - if existing_link: - msg = "Duplicate link between AN %s and BN %s. Link IDs %s and %s." % \ - (arc["AN"], arc["BN"], existing_link[emme_id_name], arc[arc_id_name]) + link = network.link(i_node, j_node) + if link: + msg = "Duplicate link between AN %s and BN %s. Link GUID/IDs %s/%s and %s/%s." % \ + (arc["AN"], arc["BN"], link[emme_guid_name], link[emme_id_name], arc[arc_guid_name], arc[arc_id_name]) self._log.append({"type": "text", "content": msg}) - self._error.append(msg) - self._split_link(network, i_node, j_node, new_node_id) - new_node_id += 1 - - modes = mode_callback(arc) - link = network.create_link(i_node, j_node, modes) - link.length = arc["LENGTH"] / 5280.0 # convert feet to miles - if len(coordinates) > 2: - link.vertices = coordinates[1:-1] + if link[emme_guid_name] == arc[arc_guid_name]: + self._log.append({"type": "text", "content": "... but GUIDs match (not an error)"}) + else: + self._error.append(msg) + else: + modes = mode_callback(arc) + link = network.create_link(i_node, j_node, modes) + link.length = arc["LENGTH"] + if len(coordinates) > 2: + link.vertices = coordinates[1:-1] for field, attr in forward_attr_map.iteritems(): link[attr] = arc[field] - if arc["IWAY"] == 2 or arc["IWAY"] == 0: - reverse_link = network.create_link(j_node, i_node, modes) - reverse_link.length = link.length - reverse_link.vertices = list(reversed(link.vertices)) + if arc["WAY"] == 2 or arc["WAY"] == 0: + reverse_link = network.link(j_node, i_node) + if not reverse_link: + reverse_link = network.create_link(j_node, i_node, modes) + reverse_link.length = link.length + reverse_link.vertices = list(reversed(link.vertices)) for field, attr in reverse_attr_map.iteritems(): reverse_link[attr] = arc[field] reverse_link[emme_id_name] = -1*arc[arc_id_name] + reverse_link[emme_guid_name] = "-" + arc[arc_guid_name] reverse_link[dir_name] = reverse_dir_map[arc["DIR"]] def create_transit_lines(self, network, attr_map): self._log.append({"type": "header", "content": "Import transit lines"}) - load_properties = _m.Modeller().tool('sandag.utilities.properties') - props = load_properties(_join(_dir(self.source), "conf", "sandag_abm.properties")) fatal_errors = 0 - # Route_ID,Route_Name,Mode,AM_Headway,PM_Headway,OP_Headway,Night_Headway,Night_Hours,Config,Fare - transit_line_data = gen_utils.DataTableProc("trrt", _join(self.source, "trrt.csv")) - # Route_ID,Link_ID,Direction - transit_link_data = gen_utils.DataTableProc("trlink", _join(self.source, "trlink.csv")) - # Stop_ID,Route_ID,Link_ID,Pass_Count,Milepost,Longitude, Latitude,HwyNode,TrnNode,FareZone,StopName - transit_stop_data = gen_utils.DataTableProc("trstop", _join(self.source, "trstop.csv")) + # Route_ID,Route_Name,Mode,AM_Headway,PM_Headway,Midday_Headway,Evening_Headway,EarlyAM_Headway,Night_Headway,Night_Hours,Config,Fare + #transit_line_data = gen_utils.DataTableProc("trrt", self.source) + transit_line_data = gen_utils.DataTableProc("trrt", _join(_dir(self.source), "trrt.csv")) + # Route_ID,Link_ID,Link_GUID,Direction + #transit_link_data = gen_utils.DataTableProc("trlink", self.source) + transit_link_data = gen_utils.DataTableProc("trlink", _join(_dir(self.source), "trlink.csv")) + # Stop_ID,Route_ID,Link_ID,Pass_Count,Milepost,Longitude, Latitude,HwyNode,TrnNode,StopName + #transit_stop_data = gen_utils.DataTableProc("trstop", self.source) + transit_stop_data = gen_utils.DataTableProc("trstop", _join(_dir(self.source), "trstop.csv")) # From_line,To_line,Board_stop,Wait_time # Note: Board_stop is not used # Timed xfer data periods = ['EA', 'AM', 'MD', 'PM', 'EV'] timed_xfer_data = {} for period in periods: - timed_xfer_data[period] = gen_utils.DataTableProc("timexfer_"+period, _join(self.source, "timexfer_"+period+".csv")) + file_path = _join(_dir(self.source), FILE_NAMES["TIMEXFER"] % period) + if os.path.exists(file_path): + timed_xfer_data[period] = gen_utils.DataTableProc("timexfer_"+period, file_path) + else: + timed_xfer_data[period] = [] - mode_properties = gen_utils.DataTableProc("MODE5TOD", _join(self.source, "MODE5TOD.csv"), convert_numeric=True) + mode_properties = gen_utils.DataTableProc("MODE5TOD", _join(_dir(self.source), FILE_NAMES["MODE5TOD"]), convert_numeric=True) mode_details = {} for record in mode_properties: mode_details[int(record["MODE_ID"])] = record @@ -910,7 +777,7 @@ def create_transit_lines(self, network, attr_map): premium_bus = network.create_transit_vehicle(80, 'p') # 8 prem express express_bus = network.create_transit_vehicle(90, 'e') # 9 regular express local_bus = network.create_transit_vehicle(100, 'b') # 10 local bus - tier1 = network.create_transit_vehicle(45, 'o') # 11 Tier 1 + tier1 = network.create_transit_vehicle(45, 'o') # 11 Tier 1 brt_yellow.auto_equivalent = 3.0 brt_red.auto_equivalent = 3.0 @@ -932,55 +799,60 @@ def create_transit_lines(self, network, attr_map): for elem_type in "TRANSIT_LINE", "TRANSIT_SEGMENT": mapping = attr_map[elem_type] for field, (attr, tcoved_type, emme_type, desc) in mapping.iteritems(): - default = "" if emme_type == "STRING" else 0 - network.create_attribute(elem_type, attr, default) if tcoved_type == "TRRT": trrt_attrs.append((field, attr)) elif tcoved_type == "MODE5TOD": mode5tod_attrs.append((field, attr)) + network.create_attribute("TRANSIT_SEGMENT", "milepost") - # Pre-process transit line (trrt.csv) to know the route names for errors / warnings + # Pre-process transit line (trrt) to know the route names for errors / warnings transit_line_records = list(transit_line_data) line_names = {} for record in transit_line_records: - line_names[int(record["Route_ID"])] = record["Route_Name"].strip() + line_names[int(record["Route_ID"])] = str(record["Route_Name"]) - links = dict((link["@tcov_id"], link) for link in network.links()) + links = dict((link["#hwyseg_guid"], link) for link in network.links()) transit_routes = _defaultdict(lambda: []) for record in transit_link_data: line_ref = line_names.get(int(record["Route_ID"]), record["Route_ID"]) - link_id = int(record["Link_ID"]) - if "+" in record["Direction"]: - link = links.get(link_id) - else: - link = links.get(-1*link_id) - if not link: - link = links.get(link_id) - if link and not link.reverse_link: - reverse_link = network.create_link(link.j_node, link.i_node, link.modes) - reverse_link.vertices = list(reversed(link.vertices)) - for attr in network.attributes("LINK"): - if attr not in set(["vertices"]): - reverse_link[attr] = link[attr] - reverse_link["@tcov_id"] = -1 * link["@tcov_id"] - msg = "Transit line %s : Missing reverse link with ID %s (%s) (reverse link created)" % ( - line_ref, record["Link_ID"], link) - self._log.append({"type": "text", "content": msg}) - self._error.append("Transit route import: " + msg) - link = reverse_link + link_id = record["Link_GUID"] + if "-" in record["Direction"]: + link_id = "-" + link_id + link = links.get(link_id) + if not link: + if "-" in record["Direction"]: + reverse_link = links.get("-" + link_id) + else: + reverse_link = links.get(link_id[1:]) + if reverse_link: + link = network.create_link(reverse_link.j_node, reverse_link.i_node, reverse_link.modes) + link.vertices = list(reversed(reverse_link.vertices)) + for attr in network.attributes("LINK"): + if attr not in set(["vertices"]): + link[attr] = reverse_link[attr] + link["@tcov_id"] = -1 * reverse_link["@tcov_id"] + link["#hwyseg_guid"] = link_id + links[link_id] = link + msg = "Transit line %s : Missing reverse link with ID %s (%s) (reverse link created)" % ( + line_ref, record["Link_GUID"], link) + self._log.append({"type": "text", "content": msg}) + self._error.append("Transit route import: " + msg) + link = reverse_link if not link: - msg = "Transit line %s : No link with ID %s, line not created" % ( - line_ref, record["Link_ID"]) + msg = "Transit line %s : No link with GUID %s, routing may not be correct" % ( + line_ref, record["Link_GUID"]) self._log.append({"type": "text", "content": msg}) self._error.append("Transit route import: " + msg) fatal_errors += 1 continue + transit_routes[int(record["Route_ID"])].append(link) # lookup list of special tier 1 mode route names - tier1_rail_route_names = [str(n) for n in props["transit.newMode.route"]] + tier1_rail_route_names = [str(n) for n in self._props["transit.newMode.route"]] dummy_links = set([]) transit_lines = {} + auto_mode = network.mode("d") for record in transit_line_records: try: route = transit_routes[int(record["Route_ID"])] @@ -988,7 +860,6 @@ def create_transit_lines(self, network, attr_map): is_tier1_rail = False for name in tier1_rail_route_names: if str(record["Route_Name"]).startswith(name): - print('record["Route_Name"]2', record["Route_Name"]) is_tier1_rail = True break if is_tier1_rail: @@ -1001,8 +872,8 @@ def create_transit_lines(self, network, attr_map): itinerary = [prev_link] for link in route[1:]: if prev_link.j_node != link.i_node: # filling in the missing gap - msg = "line %s : Links not adjacent, shortest path interpolation used (%s and %s)" % ( - record["Route_Name"], prev_link["@tcov_id"], link["@tcov_id"]) + msg = "Transit line %s (index %s): Links not adjacent, shortest path interpolation used (%s and %s)" % ( + record["Route_Name"], record["Route_ID"], prev_link["#hwyseg_guid"], link["#hwyseg_guid"]) log_record = {"type": "text", "content": msg} self._log.append(log_record) sub_path = find_path(prev_link, link, mode) @@ -1012,17 +883,17 @@ def create_transit_lines(self, network, attr_map): prev_link = link node_itinerary = [itinerary[0].i_node] + [l.j_node for l in itinerary] - try: - tline = network.create_transit_line( - record["Route_Name"].strip(), vehicle_type, node_itinerary) - except: - msg = "Transit line %s : missing mode added to at least one link" % ( - record["Route_Name"]) - self._log.append({"type": "text", "content": msg}) - for link in itinerary: + missing_mode = 0 + for link in itinerary: + if mode not in link.modes: link.modes |= set([mode]) - tline = network.create_transit_line( - record["Route_Name"].strip(), vehicle_type, node_itinerary) + missing_mode += 1 + if missing_mode: + msg = "Transit line %s (index %s): missing mode added to %s link(s)" % ( + str(record["Route_Name"]), record["Route_ID"], missing_mode) + self._log.append({"type": "text", "content": msg}) + tline = network.create_transit_line( + str(record["Route_Name"]), vehicle_type, node_itinerary) for field, attr in trrt_attrs: tline[attr] = float(record[field]) @@ -1041,12 +912,18 @@ def create_transit_lines(self, network, attr_map): tline.layover_time = 5 transit_lines[int(record["Route_ID"])] = tline + milepost = 0 for segment in tline.segments(): + segment.milepost = milepost + milepost += segment.link.length segment.allow_boardings = False segment.allow_alightings = False - segment.transit_time_func = 2 - # ft2 = ul2 -> copied @trtime_link_XX - # segments on links matched to auto network (with auto mode) are changed to ft1 = timau + if auto_mode in segment.link.modes: + # segments on links with auto mode are ft1 = timau + segment.transit_time_func = 1 + else: + # ft2 = ul2 -> copied @trtime (fixed speed) + segment.transit_time_func = 2 except Exception as error: msg = "Transit line %s: %s %s" % (record["Route_Name"], type(error), error) self._log.append({"type": "text", "content": msg}) @@ -1065,8 +942,10 @@ def create_transit_lines(self, network, attr_map): except KeyError: self._log.append( {"type": "text", - "content": "Stop %s: could not find transit line by ID %s (link ID %s)" % ( - record["Stop_ID"], record["Route_ID"], record["Link_ID"])}) + "content": "Stop %s: could not find transit line by ID %s (link GUID %s)" % ( + record["Stop_ID"], record["Route_ID"], record["Link_GUID"])}) + for stops in line_stops.itervalues(): + stops.sort(key=lambda stop: float(stop["Milepost"])) seg_float_attr_map = [] seg_string_attr_map = [] @@ -1083,32 +962,34 @@ def create_transit_lines(self, network, attr_map): continue itinerary = tline.segments(include_hidden=True) segment = prev_segment = itinerary.next() - tcov_id = abs(segment.link["@tcov_id"]) for stop in stops: if "DUMMY" in stop["StopName"]: continue - link_id = int(stop['Link_ID']) - node_id = int(stop['TrnNode']) - while segment.link and tcov_id != link_id: + stop_link_id = stop['Link_GUID'] + stop_node_id = int(stop['Node']) + while segment.link and segment.link["#hwyseg_guid"].lstrip("-") != stop_link_id: segment = itinerary.next() - if segment.link is None: - break - tcov_id = abs(segment.link["@tcov_id"]) - if node_id == segment.i_node.number: + if stop_node_id == segment.i_node.number: pass - elif segment.j_node and node_id == segment.j_node.number: - segment = itinerary.next() # its the next segment + elif segment.j_node and stop_node_id == segment.j_node.number: + # if matches the J-node then the stop is on the next segment + segment = itinerary.next() else: next_segment = None if segment.j_node: next_segment = itinerary.next() - if next_segment and abs(next_segment.link["@tcov_id"]) == link_id and \ - node_id == next_segment.j_node.number: + if next_segment and next_segment.link["#hwyseg_guid"].lstrip("-") == stop_link_id and \ + stop_node_id == next_segment.j_node.number: # split link case, where stop is at the end of the next segment segment = next_segment else: - msg = "Transit line %s: could not find stop on link ID %s at node ID %s" % (line_name, link_id, node_id) + if segment.link and segment.link["#hwyseg_guid"].lstrip("-") == stop_link_id: + msg = "Transit line %s (index %s): found GUID %s (segment %s) but node ID %s does not match I or J node" % ( + line_name, stop["Route_ID"], segment, stop_link_id, stop_node_id) + else: + msg = "Transit line %s (index %s): did not found GUID %s for stop node ID %s" % ( + line_name, stop["Route_ID"], stop_link_id, stop_node_id) self._log.append({"type": "text", "content": msg}) self._error.append(msg) fatal_errors += 1 @@ -1137,9 +1018,9 @@ def lookup_line(ident): raise Exception("'%s' is not a route name or route ID" % ident) # Normalizing the case of the headers as different examples have been seen - for period in periods: + for period, data in timed_xfer_data.iteritems(): norm_data = [] - for record in timed_xfer_data[period]: + for record in data: norm_record = {} for key, val in record.iteritems(): norm_record[key.lower()] = val @@ -1165,78 +1046,23 @@ def lookup_line(ident): gen_utils.DataTableProc("%s_timed_xfer_%s" % (self.data_table_name, period), data=timed_xfer) if fatal_errors > 0: - raise Exception("Cannot create transit network, %s fatal errors found" % fatal_errors) + raise Exception("Import of transit lines: %s fatal errors found" % fatal_errors) self._log.append({"type": "text", "content": "Import transit lines complete"}) def calc_transit_attributes(self, network): - self._log.append({"type": "header", "content": "Calculate derived transit attributes"}) - # - TM by 5 TOD periods copied from TM for 3 time periods - # NOTE: the values of @trtime_link_## are only used for - # separate guideway. - # Links shared with the traffic network use the - # assignment results in timau - for link in network.links(): - for time in ["_ea", "_md", "_ev"]: - link["@trtime_link" + time] = link["trtime_link_op"] - if link.type == 0: # walk only links have IFC ==0 - link.type = 99 - + self._log.append({"type": "header", "content": "Calculate derived transit line attributes"}) # ON TRANSIT LINES - # Set 4-period headway based on revised headway calculation + # Set 3-period headway based on revised headway calculation for line in network.transit_lines(): - for period in ["am", "pm", "op", "night"]: + for period in ["ea", "am", "md", "pm", "ev"]: line["@headway_rev_" + period] = revised_headway(line["@headway_" + period]) + self._log.append({"type": "text", "content": "Revised headway calculation complete"}) - # Special incremental boarding and in-vehicle fares - # to recreate the coaster zone fares fares_file_name = FILE_NAMES["FARES"] - special_fare_path = _join(self.source, fares_file_name) - if os.path.isfile(special_fare_path): - with open(special_fare_path) as fare_file: - self._log.append({"type": "text", "content": "Using fare details (for coaster) from %s" % fares_file_name}) - special_fares = None - yaml_installed = True - try: - import yaml - special_fares = yaml.load(fare_file) - self._log.append({"type": "text", "content": yaml.dump(special_fares).replace("\n", "
")}) - except ImportError: - yaml_installed = False - except: - pass - if special_fares is None: - try: - import json - special_fares = json.load(fare_file) - self._log.append({"type": "text", "content": json.dumps(special_fares, indent=4).replace("\n", "
")}) - except: - pass - if special_fares is None: - msg = "YAML or JSON" if yaml_installed else "JSON (YAML parser not installed)" - raise Exception(fares_file_name + ": file could not be parsed as " + msg) - else: - # Default coaster fare for 2012 base year - special_fares = { - "boarding_cost": { - "base": [ - {"line": "398104", "cost" : 4.0}, - {"line": "398204", "cost" : 4.0} - ], - "stop_increment": [ - {"line": "398104", "stop": "SORRENTO VALLEY", "cost": 0.5}, - {"line": "398204", "stop": "SORRENTO VALLEY", "cost": 0.5} - ] - }, - "in_vehicle_cost": [ - {"line": "398104", "from": "SOLANA BEACH", "cost": 1.0}, - {"line": "398104", "from": "SORRENTO VALLEY", "cost": 0.5}, - {"line": "398204", "from": "OLD TOWN", "cost": 1.0}, - {"line": "398204", "from": "SORRENTO VALLEY", "cost": 0.5} - ], - "day_pass": 5.0, - "regional_pass": 12.0 - } - self._log.append({"type": "text", "content": "Using default coaster fare based on 2012 base year setup."}) + special_fare_path = _join(_dir(self.source), fares_file_name) + if not os.path.isfile(special_fare_path): + self._log.append({"type": "text", "content": "Special fares file %s not found" % fares_file_name}) + return def get_line(line_id): line = network.transit_line(line_id) @@ -1244,6 +1070,33 @@ def get_line(line_id): raise Exception("%s: line does not exist: %s" % (fares_file_name, line_id)) return line + # Special incremental boarding and in-vehicle fares + # to recreate the coaster zone fares + self._log.append({"type": "header", "content": "Apply special_fares to transit lines"}) + with open(special_fare_path) as fare_file: + self._log.append({"type": "text", "content": "Using fare details (for coaster) from %s" % fares_file_name}) + special_fares = None + yaml_installed = True + try: + import yaml + special_fares = yaml.load(fare_file) + self._log.append({"type": "text", "content": yaml.dump(special_fares).replace("\n", "
")}) + except ImportError: + yaml_installed = False + except: + pass + if special_fares is None: + try: + import json + special_fares = json.load(fare_file) + self._log.append({"type": "text", "content": json.dumps(special_fares, indent=4).replace("\n", "
")}) + except: + pass + if special_fares is None: + msg = "YAML or JSON" if yaml_installed else "JSON (YAML parser not installed)" + raise Exception(fares_file_name + ": file could not be parsed as " + msg) + + for record in special_fares["boarding_cost"]["base"]: line = get_line(record["line"]) line["@fare"] = 0 @@ -1272,104 +1125,57 @@ def get_line(line_id): pass_values.add_attribute(_dt.Attribute("pass_type", _np.array(pass_cost_keys).astype("O"))) pass_values.add_attribute(_dt.Attribute("cost", _np.array(pass_costs).astype("f8"))) gen_utils.DataTableProc("%s_transit_passes" % self.data_table_name, data=pass_values) - self._log.append({"type": "text", "content": "Calculate derived transit attributes complete"}) - return - - def renumber_transit_nodes(self, network, new_node_id): - nodes_to_renumber = [] - # 1. find all node which have valid HNODE IDs, - # and renumber all other nodes to their new IDs - for node in network.nodes(): - if node["@hnode_tr"] > 0: - nodes_to_renumber.append(node) - else: - node.number = new_node_id - new_node_id += 1 - # 2. renumber nodes with HNODE values to move them - # out of the way - hnode_new_id = new_node_id - for node in nodes_to_renumber: - node.number = hnode_new_id - hnode_new_id += 1 - # 3. renumber nodes with HNODE values to their - # final IDs - for node in nodes_to_renumber: - node.number = node["@hnode_tr"] - return new_node_id + self._log.append({"type": "text", "content": "Apply special_fares to transit lines complete"}) + + def renumber_base_nodes(self, network): + tracker = gen_utils.AvailableNodeIDTracker(network) + nodes = [n for n in network.nodes() if n.number > 999999] + nodes = sorted(nodes, key=lambda x: x.number, reverse=True) + if nodes: + self._log.append({"type": "text", "content": "Renumbered %s nodes" % len(nodes)}) + for n in nodes: + old_number = n.number + n.number = tracker.get_id() + self._log.append({"type": "text", "content": " - renumbered %s to %s " % (old_number, n.number)}) def create_turns(self, network): self._log.append({"type": "header", "content": "Import turns and turn restrictions"}) - self._log.append({"type": "text", "content": "Process LINKTYPETURNS.DBF for turn prohibited by type"}) - # Process LINKTYPETURNS.DBF for turn prohibited by type - with _fiona.open(_join(self.source, "LINKTYPETURNS.DBF"), 'r') as f: - link_type_turns = _defaultdict(lambda: {}) - for record in f: - record = record['properties'] - link_type_turns[record["FROM"]][record["TO"]] = { - "LEFT": record["LEFT"], - "RIGHT": record["RIGHT"], - "STRAIGHT": record["STRAIGHT"], - "UTURN": record["UTURN"] - } - for from_link in network.links(): - if from_link.type in link_type_turns: - to_link_turns = link_type_turns[from_link.type] - for to_link in from_link.j_node.outgoing_links(): - if to_link.type in to_link_turns: - record = to_link_turns[to_link.type] - if not from_link.j_node.is_intersection: - network.create_intersection(from_link.j_node) - turn = network.turn(from_link.i_node, from_link.j_node, to_link.j_node) - turn.penalty_func = 1 - if to_link["@tcov_id"] == from_link["left_link"]: - turn.data1 = record["LEFT"] - elif to_link["@tcov_id"] == from_link["through_link"]: - turn.data1 = record["STRAIGHT"] - elif to_link["@tcov_id"] == from_link["right_link"]: - turn.data1 = record["RIGHT"] - else: - turn.data1 = record["UTURN"] - - self._log.append({"type": "text", "content": "Process turns.csv for turn prohibited by ID"}) - turn_data = gen_utils.DataTableProc("turns", _join(self.source, "turns.csv")) + self._log.append({"type": "text", "content": "Process turns for turn prohibited by ID"}) + turn_data = gen_utils.DataTableProc("Turns", self.source) if self.save_data_tables: turn_data.save("%s_turns" % self.data_table_name, self.overwrite) - links = dict((link["@tcov_id"], link) for link in network.links()) - - # Process turns.csv for prohibited turns from_id, to_id, penalty + # Process turns.csv for prohibited turns penalty for i, record in enumerate(turn_data): - from_link_id, to_link_id = int(record["from_id"]), int(record["to_id"]) - from_link, to_link = links[from_link_id], links[to_link_id] - if from_link.j_node == to_link.i_node: - pass - elif from_link.j_node == to_link.j_node: - to_link = to_link.reverse_link - elif from_link.i_node == to_link.i_node: - from_link = from_link.reverse_link - elif from_link.i_node == to_link.j_node: - from_link = from_link.reverse_link - to_link = to_link.reverse_link + from_node_id, to_node_id, at_node_id = record["FromNode"], record["ToNode"], record["MidNode"] + at_node = network.node(at_node_id) + if at_node and not at_node.is_intersection: + try: + network.create_intersection(at_node) + except Exception as error: + text = ("record %s turn from %s, at %s, to %s: cannot create intersection" % + (i, from_node_id, at_node_id, to_node_id)) + self._log.append({"type": "text", "content": text}) + trace_text = _traceback.format_exc().replace("\n", "
") + self._log.append({"type": "text", "content": trace_text}) + self._error.append(text) + continue + turn = network.turn(from_node_id, at_node_id, to_node_id) + if at_node is None: + text = ("record %s turn from %s, at %s, to %s: at node does not exist" % + (i, from_node_id, at_node_id, to_node_id)) + self._log.append({"type": "text", "content": text}) + self._error.append(text) + elif turn is None: + text = ("record %s turn from %s, at %s, to %s: does not form a turn" % + (i, from_node_id, at_node_id, to_node_id)) + self._log.append({"type": "text", "content": text}) + self._error.append(text) else: - msg = "Record %s: links are not adjacent %s - %s." % (i, from_link_id, to_link_id) - self._log.append({"type": "text", "content": msg}) - self._error.append("Turn import: " + msg) - continue - if not from_link or not to_link: - msg = "Record %s: links adjacent but in reverse direction %s - %s." % (i, from_link_id, to_link_id) - self._log.append({"type": "text", "content": msg}) - self._error.append("Turn import: " + msg) - continue - - node = from_link.j_node - if not node.is_intersection: - network.create_intersection(node) - turn = network.turn(from_link.i_node, node, to_link.j_node) - if not record["penalty"]: turn.penalty_func = 0 # prohibit turn - else: - turn.penalty_func = 1 - turn.data1 = float(record["penalty"]) - self._log.append({"type": "text", "content": "Import turns and turn restrictions complete"}) + # NOTE: could support penalty value + # turn.penalty_func = 1 + # turn.data1 = float(record["penalty"]) + self._log.append({"type": "text", "content": "Import turns and turn prohibitions complete"}) def calc_traffic_attributes(self, network): self._log.append({"type": "header", "content": "Calculate derived traffic attributes"}) @@ -1382,13 +1188,11 @@ def calc_traffic_attributes(self, network): # "ITOLL4": "@cost_med_truck" # ITOLL4 - Toll * 1.03 + AOC # "ITOLL5": "@cost_hvy_truck" # ITOLL5 - Toll * 2.33 + AOC fatal_errors = 0 - load_properties = _m.Modeller().tool('sandag.utilities.properties') - props = load_properties(_join(_dir(self.source), "conf", "sandag_abm.properties")) try: - aoc = float(props["aoc.fuel"]) + float(props["aoc.maintenance"]) + aoc = float(self._props["aoc.fuel"]) + float(self._props["aoc.maintenance"]) except ValueError: raise Exception("Error during float conversion for aoc.fuel or aoc.maintenance from sandag_abm.properties file") - scenario_year = int(props["scenarioYear"]) + scenario_year = int(self._props["scenarioYear"]) periods = ["EA", "AM", "MD", "PM", "EV"] time_periods = ["_ea", "_am", "_md", "_pm", "_ev"] src_time_periods = ["_op", "_am", "_op", "_pm", "_op"] @@ -1415,42 +1219,6 @@ def calc_traffic_attributes(self, network): for node in network.nodes(): node["@interchange"] = node.is_interchange - #add zone_id attribute - # map_file_name = FILE_NAMES["node_taz_map"] - # map_file_name = _join(self.source, map_file_name) - # node_taz_map = pd.read_csv(map_file_name) - - # # Loop through all nodes in the network to - # for n in network.nodes(): - # # Check if the node is in the list of nodes we have (ext not there) - # if n.number in node_taz_map.HNODE: - # n["@zone_id"] = int(node_taz_map[node_taz_map.HNODE == n.number]["TAZ"].values[0]) - # else: - # n["@zone_id"] = 0 - - # #read in csv file with parking node numbers and parking counts - # parking_file_name = FILE_NAMES["PARKING"] - # parking_file_path = _join(self.source, parking_file_name) - # if os.path.isfile(parking_file_path): - # with open(parking_file_path) as parking_file: - # self._log.append({"type": "text", "content": "Using parking node details from %s" % parking_file_name}) - # parking_nodes = [] - # for line in parking_file: - # #grab the second column - # node_number = line.split("\t")[0] - # parking_nodes.append(int(node_number)) - # self._log.append({"type": "text", "content": parking_nodes}) - - # # Loop through all nodes in the network - # for n in network.nodes(): - # # Check if the node is in the list of parking nodes - # if n.number in parking_nodes: - # # If it is, add the attribute to the node - # self._log.append({"type": "text", "content": "node %s has parking" % n.number}) - # n["@parking"] = 1 - # else: - # n["@parking"] = 0 - for link in network.links(): if link.type == 1 and mode_d in link.modes: link["@intdist_down"] = interchange_distance(link, "DOWNSTREAM") @@ -1493,69 +1261,22 @@ def calc_traffic_attributes(self, network): } for link in network.links(): # Change SR125 toll speed to 70MPH - if link["@lane_restriction"] == 4 and link.type == 1: + if link["@hov"] == 4 and link.type == 1: link["@speed_posted"] = 70 - link["@cost_operating"] = link.length * aoc - - # Expand off-peak TOD attributes, copy peak period attributes - for time, src_time in zip(time_periods, src_time_periods): - link["@lane" + time] = link["lane" + src_time] - link["@time_link" + time] = link["time_link" + src_time] - + for time in time_periods: # add link delay (30 sec=0.5mins) to HOV connectors to discourage travel - if link.type == 8 and (link["@lane_restriction"] == 2 or link["@lane_restriction"] == 3): + if link.type == 8 and (link["@hov"] == 2 or link["@hov"] == 3): link["@time_link" + time] = link["@time_link" + time] + 0.375 # make speed on HOV lanes (70mph) the same as parallel GP lanes (65mph) # - set speed back to posted speed - increase travel time by (speed_adj/speed_posted) - if link.type == 1 and (link["@lane_restriction"] == 2 or link["@lane_restriction"] == 3): + if link.type == 1 and (link["@hov"] == 2 or link["@hov"] == 3): speed_adj = link["@speed_adjusted"] speed_posted = link["@speed_posted"] if speed_adj>0: link["@time_link" + time] = (speed_adj/(speed_posted*1.0)) * link["@time_link" + time] - link["@time_inter" + time] = link["time_inter" + src_time] - link["@toll" + time] = link["toll" + src_time] - - off_peak_factor_file = FILE_NAMES["OFF_PEAK"] - if os.path.exists(_join(self.source, off_peak_factor_file)): - msg = "Adjusting off-peak tolls based on factors from %s" % off_peak_factor_file - self._log.append({"type": "text", "content": msg}) - tolled_links = list(link for link in network.links() if link["toll_op"] > 0) - # NOTE: CSV Reader sets the field names to UPPERCASE for consistency - with gen_utils.CSVReader(_join(self.source, off_peak_factor_file)) as r: - for row in r: - name = row["FACILITY_NAME"] - ea_factor = float(row["OP_EA_FACTOR"]) - md_factor = float(row["OP_MD_FACTOR"]) - ev_factor = float(row["OP_EV_FACTOR"]) - count = 0 - for link in tolled_links: - if name in link["#name"]: - count += 1 - link["@toll_ea"] = link["@toll_ea"] * ea_factor - link["@toll_md"] = link["@toll_md"] * md_factor - link["@toll_ev"] = link["@toll_ev"] * ev_factor - - msg = "Facility name '%s' matched to %s links." % (name, count) - msg += " Adjusted off-peak period tolls EA: %s, MD: %s, EV: %s" % (ea_factor, md_factor, ev_factor) - self._log.append({"type": "text2", "content": msg}) - - for link in network.links(): - factors = [(3.0/12.0), 1.0, (6.5/12.0), (3.5/3.0), (8.0/12.0)] - for f, time, src_time in zip(factors, time_periods, src_time_periods): - if link["capacity_link" + src_time] != 999999: - link["@capacity_link" + time] = f * link["capacity_link" + src_time] - else: - link["@capacity_link" + time] = 999999 - if link["capacity_inter" + src_time] != 999999: - link["@capacity_inter" + time] = f * link["capacity_inter" + src_time] - else: - link["@capacity_inter" + time] = 999999 - if link["@capacity_hourly" + src_time] != 0: - link["@capacity_hourly" + src_time] = round(link["@capacity_hourly" + src_time]) - # Required file vehicle_class_factor_file = FILE_NAMES["VEHICLE_CLASS"] facility_factors = _defaultdict(lambda: {}) @@ -1622,12 +1343,12 @@ def match_facility_factors(link): factors["count"] += 1 factors = _copy(factors) del factors["count"] - # @lane_restriction = 2 or 3 overrides hov2 and hov3 costs - if link["@lane_restriction"] == 2: + # @hov = 2 or 3 overrides hov2 and hov3 costs + if link["@hov"] == 2: for _, time_factors in factors.iteritems(): time_factors["hov2"] = 0.0 time_factors["hov3"] = 0.0 - elif link["@lane_restriction"] == 3: + elif link["@hov"] == 3: for _, time_factors in factors.iteritems(): time_factors["hov3"] = 0.0 return factors @@ -1648,7 +1369,9 @@ def match_facility_factors(link): msg = "Facility name '%s' matched to %s links." % (name, class_factors["count"]) self._log.append({"type": "text2", "content": msg}) - self._log.append({"type": "text", "content": "Calculation and time period expansion of costs, tolls, capacities and times complete"}) + self._log.append({ + "type": "text", + "content": "Calculation and time period expansion of costs, tolls, capacities and times complete"}) # calculate static reliability for link in network.links(): @@ -1664,7 +1387,7 @@ def match_facility_factors(link): # arterial/ramp/other apply road parameters elif link["type"] <= 9 and link["@lane" + time] > 0: lane_factor = road_rel["lanes"].get(link["@lane" + time], 0.0) - speed_bin = link["@speed_posted"] + speed_bin = int(link["@speed_posted"] / 5) * 5 # truncate to multiple of 5 if speed_bin < 35: speed_bin = "<35" elif speed_bin > 50: @@ -1680,7 +1403,7 @@ def match_facility_factors(link): # Cycle length matrix # Intersecting Link # Approach Link 2 3 4 5 6 7 8 9 - # IFC Description + # FC Description # 2 Prime Arterial 2.5 2 2 2 2 2 2 2 # 3 Major Arterial 2 2 2 2 2 2 2 2 # 4 Collector 2 2 1.5 1.5 1.5 1.5 1.5 1.5 @@ -1800,175 +1523,6 @@ def check_zone_access(self, network, mode): if not access: raise Exception("No access permitted to zone %s" % centroid.id) - def add_transit_to_traffic(self, hwy_network, tr_network, new_node_id): - if not self.merged_scenario_id or not hwy_network or not tr_network: - return - self._log.append({"type": "header", "content": "Merge transit network to traffic network"}) - fatal_errors = 0 - for tr_mode in tr_network.modes(): - hwy_mode = hwy_network.create_mode(tr_mode.type, tr_mode.id) - hwy_mode.description = tr_mode.description - hwy_mode.speed = tr_mode.speed - for tr_veh in tr_network.transit_vehicles(): - hwy_veh = hwy_network.create_transit_vehicle(tr_veh.id, tr_veh.mode.id) - hwy_veh.description = tr_veh.description - hwy_veh.auto_equivalent = tr_veh.auto_equivalent - hwy_veh.seated_capacity = tr_veh.seated_capacity - hwy_veh.total_capacity = tr_veh.total_capacity - - for elem_type in ["NODE", "LINK", "TRANSIT_LINE", "TRANSIT_SEGMENT"]: - for attr in tr_network.attributes(elem_type): - if not attr in hwy_network.attributes(elem_type): - default = "" if attr.startswith("#") else 0 - new_attr = hwy_network.create_attribute(elem_type, attr, default) - - hwy_link_index = dict((l["@tcov_id"], l) for l in hwy_network.links()) - hwy_node_position_index = dict(((n.x, n.y), n) for n in hwy_network.nodes()) - hwy_node_index = dict() - not_matched_links = [] - for tr_link in tr_network.links(): - tcov_id = tr_link["@tcov_id"] - if tcov_id == 0: - i_node = hwy_node_position_index.get((tr_link.i_node.x, tr_link.i_node.y)) - j_node = hwy_node_position_index.get((tr_link.j_node.x, tr_link.j_node.y)) - if i_node and j_node: - hwy_link = hwy_network.link(i_node, j_node) - else: - hwy_link = None - else: - hwy_link = hwy_link_index.get(tcov_id) - if not hwy_link: - not_matched_links.append(tr_link) - else: - if tr_link.i_node not in hwy_node_index: - hwy_node_index[tr_link.i_node] = hwy_link.i_node - for attr in tr_network.attributes("NODE"): - hwy_link.i_node[attr] = tr_link.i_node[attr] - if tr_link.j_node not in hwy_node_index: - hwy_node_index[tr_link.j_node] = hwy_link.j_node - for attr in tr_network.attributes("NODE"): - hwy_link.j_node[attr] = tr_link.j_node[attr] - - hwy_link.modes |= tr_link.modes - - bus_mode = tr_network.mode("b") - - def lookup_node(src_node, new_node_id): - node = hwy_node_index.get(src_node) - if not node: - node = hwy_node_position_index.get((src_node.x, src_node.y)) - if not node: - if hwy_network.node(src_node.number): - node = hwy_network.create_regular_node(new_node_id) - new_node_id += 1 - self._log.append({ - "type": "text", - "content": "Duplicate node ID, renumber transit node %s to %s" % - (src_node.number, new_node_id) - }) - else: - node = hwy_network.create_regular_node(src_node.number) - for attr in tr_network.attributes("NODE"): - node[attr] = src_node[attr] - hwy_node_index[src_node] = node - return node, new_node_id - - for tr_link in not_matched_links: - i_node, new_node_id = lookup_node(tr_link.i_node, new_node_id) - j_node, new_node_id = lookup_node(tr_link.j_node, new_node_id) - # check for duplicate but different links - # All cases to be logged and then an error raised at end - ex_link = hwy_network.link(i_node, j_node) - if ex_link: - self._log.append({ - "type": "text", - "content": "Duplicate links between the same nodes with different IDs in traffic/transit merge. " - "Traffic link ID %s, transit link ID %s." % (ex_link["@tcov_id"], tr_link["@tcov_id"]) - }) - self._error.append("Duplicate links with different IDs between traffic (%s) and transit (%s) networks" % - (ex_link["@tcov_id"], tr_link["@tcov_id"])) - self._split_link(hwy_network, i_node, j_node, new_node_id) - new_node_id += 1 - fatal_errors += 1 - try: - link = hwy_network.create_link(i_node, j_node, tr_link.modes) - except Exception as error: - self._log.append({ - "type": "text", - "content": "Error creating link '%s', I-node '%s', J-node '%s'. Error message %s" % - (tr_link["@tcov_id"], i_node, j_node, error) - }) - self._error.append("Cannot create transit link '%s' in traffic network" % tr_link["@tcov_id"]) - fatal_errors += 1 - continue - hwy_link_index[tr_link["@tcov_id"]] = link - for attr in tr_network.attributes("LINK"): - link[attr] = tr_link[attr] - link.vertices = tr_link.vertices - - # Create transit lines and copy segment data - for tr_line in tr_network.transit_lines(): - itinerary = [] - for seg in tr_line.segments(True): - itinerary.append(hwy_node_index[seg.i_node]) - try: - hwy_line = hwy_network.create_transit_line(tr_line.id, tr_line.vehicle.id, itinerary) - except Exception as error: - msg = "Transit line %s, error message %s" % (tr_line.id, error) - self._log.append({"type": "text", "content": msg}) - self._error.append("Cannot create transit line '%s' in traffic network" % tr_line.id) - fatal_errors += 1 - continue - for attr in hwy_network.attributes("TRANSIT_LINE"): - hwy_line[attr] = tr_line[attr] - for tr_seg, hwy_seg in _izip(tr_line.segments(True), hwy_line.segments(True)): - for attr in hwy_network.attributes("TRANSIT_SEGMENT"): - hwy_seg[attr] = tr_seg[attr] - - # Change ttf from ft2 (fixed speed) to ft1 (congested auto time) - auto_mode = hwy_network.mode("d") - for hwy_link in hwy_network.links(): - if auto_mode in hwy_link.modes: - for seg in hwy_link.segments(): - seg.transit_time_func = 1 - if fatal_errors > 0: - raise Exception("Cannot merge traffic and transit network, %s fatal errors found" % fatal_errors) - - self._log.append({"type": "text", "content": "Merge transit network to traffic network complete"}) - - def _split_link(self, network, i_node, j_node, new_node_id): - # Attribute types to maintain consistency for correspondence with incoming / outgoing link data - periods = ["ea", "am", "md", "pm", "ev"] - approach_attrs = ["@traffic_control", "@turn_thru", "@turn_right", "@turn_left", - "@lane_auxiliary", "@green_to_cycle_init"] - for p_attr in ["@green_to_cycle_", "@time_inter_", "@cycle_"]: - approach_attrs.extend([p_attr + p for p in periods]) - capacity_inter = ["@capacity_inter_" + p for p in periods] - cost_attrs = ["@cost_operating"] - for p_attr in ["@cost_lgt_truck_", "@cost_med_truck_", "@cost_hvy_truck_", "@cost_hov2_", - "@cost_hov3_", "@cost_auto_", "@time_link_", "@trtime_link_", "@toll_"]: - cost_attrs.extend([p_attr + p for p in periods]) - approach_attrs = [a for a in approach_attrs if a in network.attributes("LINK")] - capacity_inter = [a for a in capacity_inter if a in network.attributes("LINK")] - cost_attrs = [a for a in cost_attrs if a in network.attributes("LINK")] - - new_node = network.split_link(i_node, j_node, new_node_id) - - # Correct attributes on the split links - for link in new_node.incoming_links(): - link["#name_to"] = "" - for attr in approach_attrs: - link[attr] = 0 - for attr in capacity_inter: - link[attr] = 999999 - for attr in cost_attrs: - link[attr] = 0.5 * link[attr] - link.volume_delay_func = 10 - for link in new_node.outgoing_links(): - link["#name_from"] = "" - for attr in cost_attrs: - link[attr] = 0.5 * link[attr] - @_m.logbook_trace("Set database functions (VDF, TPF and TTF)") def set_functions(self, scenario): create_function = _m.Modeller().tool( @@ -1982,12 +1536,10 @@ def set_functions(self, scenario): if function: emmebank.delete_function(function) - load_properties = _m.Modeller().tool('sandag.utilities.properties') - props = load_properties(_join(_dir(self.source), "conf", "sandag_abm.properties")) - smartSignalf_CL = props["smartSignal.factor.LC"] - smartSignalf_MA = props["smartSignal.factor.MA"] - smartSignalf_PA = props["smartSignal.factor.PA"] - atdmf = props["atdm.factor"] + smartSignalf_CL = self._props["smartSignal.factor.LC"] + smartSignalf_MA = self._props["smartSignal.factor.MA"] + smartSignalf_PA = self._props["smartSignal.factor.PA"] + atdmf = self._props["atdm.factor"] reliability_tmplt = ( "* (1 + el2 + {0}*(".format(atdmf)+ @@ -2097,7 +1649,7 @@ def check_connectivity(self, scenario): # Note matrix is also created in initialize_matrices create_matrix("ms1", "zero", "zero", scenario=scenario, overwrite=True) with gen_utils.temp_matrices(emmebank, "FULL", 1) as (result_matrix,): - result_matrix.name = "TEMP_SOV_TRAVEL_TIME" + result_matrix.name = "TEMP_AUTO_TRAVEL_TIME" set_extra_function_para( el1="@green_to_cycle_am", el2="@sta_reliability_am", @@ -2110,7 +1662,7 @@ def check_connectivity(self, scenario): "background_traffic": None, "classes": [ { - "mode": "S", # SOV toll mode + "mode": "d", "demand": 'ms"zero"', "generalized_cost": None, "results": { @@ -2150,7 +1702,7 @@ def check_connectivity(self, scenario): scenario.has_traffic_results = False def log_report(self): - report = _m.PageBuilder(title="Import network from TCOVED files report") + report = _m.PageBuilder(title="Import network from TNED files report") try: if self._error: report.add_html("
Errors detected during import: %s
" % len(self._error)) @@ -2160,7 +1712,7 @@ def log_report(self): error_msg.append("") report.add_html("".join(error_msg)) else: - report.add_html("No errors detected during import") + report.add_html("

No errors detected during import :-)") for item in self._log: if item["type"] == "text": diff --git a/src/main/emme/toolbox/master_run.py b/src/main/emme/toolbox/master_run.py index 1e08f798f..f3e8b5b80 100644 --- a/src/main/emme/toolbox/master_run.py +++ b/src/main/emme/toolbox/master_run.py @@ -496,23 +496,19 @@ def __call__(self, main_directory, scenario_id, scenario_title, emmebank_title, self.complete_work(scenarioYear, input_dir, output_dir, mgraFile, "walkMgraEquivMinutes.csv") if not skipBuildNetwork: + source_gdb = _glob.glob(os.path.join(input_dir, "*.gdb")) + if len(source_gdb) > 1: + raise Exception("Multiple *.gdb files found in input directory") + if len(source_gdb) < 1: + raise Exception("No *.gdb file found in input directory") base_scenario = import_network( - source=input_dir, - merged_scenario_id=scenario_id, + source=source_gdb[0], + scenario_id=scenario_id, title=scenario_title, data_table_name=scenarioYear, overwrite=True, emmebank=main_emmebank) - if "modify_network.py" in os.listdir(os.getcwd()): - try: - with _m.logbook_trace("Modify network script"): - import modify_network - reload(modify_network) - modify_network.run(base_scenario) - except ImportError as e: - pass - if not skipInputChecker: input_checker(path=self._path) @@ -521,21 +517,7 @@ def __call__(self, main_directory, scenario_id, scenario_title, emmebank_title, availabilities = self.parse_availability_file(_join(input_dir, availability_file), periods) # initialize per time-period scenarios for number, period in period_ids: - title = "%s - %s assign" % (base_scenario.title, period) - # copy_scenario(base_scenario, number, title, overwrite=True) - _m.logbook_write( - name="Copy scenario %s to %s" % (base_scenario.number, number), - attributes={ - 'from_scenario': base_scenario.number, - 'scenario_id': number, - 'overwrite': True, - 'scenario_title': title - } - ) - if main_emmebank.scenario(number): - main_emmebank.delete_scenario(number) - scenario = main_emmebank.copy_scenario(base_scenario.number, number) - scenario.title = title + scenario = main_emmebank.scenario(number) # Apply availabilities by facility and vehicle class to this time period self.apply_availabilities(period, scenario, availabilities) else: diff --git a/src/main/emme/toolbox/utilities/general.py b/src/main/emme/toolbox/utilities/general.py index 27a8e9aef..4d002d835 100644 --- a/src/main/emme/toolbox/utilities/general.py +++ b/src/main/emme/toolbox/utilities/general.py @@ -77,6 +77,17 @@ def __call__(self, result, expression, selections=None, aggregation=None): return self._network_calc(spec, self._scenario) +class AvailableNodeIDTracker(object): + def __init__(self, network, start=999999): + self._network = network + self._node_id = start + + def get_id(self): + while self._network.node(self._node_id): + self._node_id -= 1 + return self._node_id + + @_context def temp_matrices(emmebank, mat_type, total=1, default_value=0.0): matrices = [] @@ -141,10 +152,10 @@ def __init__(self, table_name, path=None, data=None, convert_numeric=False): self._dt_db = dt_db = project.data_tables() self._convert_numeric = convert_numeric if path: - #try: - source = _dt.DataSource(path) - #except: - # raise Exception("Cannot open file at %s" % path) + try: + source = _dt.DataSource(path) + except _dt.Error as error: + raise Exception("Cannot open file at %s" % path) layer = source.layer(table_name) self._data = layer.get_data() elif data: @@ -179,7 +190,14 @@ def _load_data(self): attr = data.attribute("geometry") for record in attr.values: geo_obj = _ogr.CreateGeometryFromWkt(record.text) - geo_coords.append(geo_obj.GetPoints()) + if _ogr.GeometryTypeToName(geo_obj.GetGeometryType()) == 'Multi Line String': + coords = [] + for line in geo_obj: + coords.extend(line.GetPoints()) + else: + coords = geo_obj.GetPoints() + coords = [point[:2] for point in coords] + geo_coords.append(coords) self._values.append(geo_coords) self._attr_names.append("geo_coordinates") diff --git a/src/main/python/hwyShapeExport.py b/src/main/python/hwyShapeExport.py index c38b9a5e8..910df3dcd 100644 --- a/src/main/python/hwyShapeExport.py +++ b/src/main/python/hwyShapeExport.py @@ -16,6 +16,13 @@ def export_highway_shape(scenario_path: str) -> geopandas.GeoDataFrame: Returns: A GeoPandas GeoDataFrame of the loaded highway network """ + + # temporary so that the sensitivity summary on data lake works + # the sensitivity summary on data lake uses IFC (from TCOV) rather than FC (from TNED) + hwy_tcad = pd.read_csv(os.path.join(scenario_path, "report", "hwyTcad.csv")) + hwy_tcad['IFC'] = hwy_tcad['FC'] + hwy_tcad.to_csv(os.path.join(scenario_path, "report", "hwyTcad.csv"), index=False) + # read in input highway network hwy_tcad = pd.read_csv(os.path.join(scenario_path, "report", "hwyTcad.csv"), usecols=["ID", # highway coverage id @@ -24,12 +31,16 @@ def export_highway_shape(scenario_path: str) -> geopandas.GeoDataFrame: "COJUR", # count jurisdiction code "COSTAT", # count station number "COLOC", # count location code - "IFC", # initial functional class - "IHOV", # link operation type - "ITRUCK", # truck restriction code - "ISPD", # posted speed limit - "IWAY", # one or two way operations - "IMED", # median type + "FC", # initial functional class + "HOV", # link operation type + "EATRUCK", # truck restriction code - Early AM + "AMTRUCK", # truck restriction code - AM Peak + "MDTRUCK", # truck restriction code - Midday + "PMTRUCK", # truck restriction code - PM Peak + "EVTRUCK", # truck restriction code - Evening + "SPD", # posted speed limit + "WAY", # one or two way operations + "MED", # median type "AN", # A node number "FXNM", # cross street name at from end of link "BN", # B node number @@ -207,17 +218,17 @@ def export_highway_shape(scenario_path: str) -> geopandas.GeoDataFrame: left_on="ID", right_on="ID1_" + tod) - # create string description of [IFC] field - conditions = [hwy_tcad["IFC"] == 1, - hwy_tcad["IFC"] == 2, - hwy_tcad["IFC"] == 3, - hwy_tcad["IFC"] == 4, - hwy_tcad["IFC"] == 5, - hwy_tcad["IFC"] == 6, - hwy_tcad["IFC"] == 7, - hwy_tcad["IFC"] == 8, - hwy_tcad["IFC"] == 9, - hwy_tcad["IFC"] == 10] + # create string description of [FC] field + conditions = [hwy_tcad["FC"] == 1, + hwy_tcad["FC"] == 2, + hwy_tcad["FC"] == 3, + hwy_tcad["FC"] == 4, + hwy_tcad["FC"] == 5, + hwy_tcad["FC"] == 6, + hwy_tcad["FC"] == 7, + hwy_tcad["FC"] == 8, + hwy_tcad["FC"] == 9, + hwy_tcad["FC"] == 10] choices = ["Freeway", "Prime Arterial", @@ -230,7 +241,7 @@ def export_highway_shape(scenario_path: str) -> geopandas.GeoDataFrame: "Local Ramp", "Zone Connector"] - hwy_tcad["IFC_Desc"] = np.select(conditions, choices, default="") + hwy_tcad["FC_Desc"] = np.select(conditions, choices, default="") # calculate aggregate flows hwy_tcad["AB_Flow_SOV"] = hwy_tcad[["AB_Flow_SOV_EA", @@ -379,13 +390,17 @@ def export_highway_shape(scenario_path: str) -> geopandas.GeoDataFrame: "COJUR", "COSTAT", "COLOC", - "IFC", - "IFC_Desc", - "IHOV", - "ITRUCK", - "ISPD", - "IWAY", - "IMED", + "FC", + "FC_Desc", + "HOV", + "EATRUCK", + "AMTRUCK", + "MDTRUCK", + "PMTRUCK", + "EVTRUCK", + "SPD", + "WAY", + "MED", "AN", "FXNM", "BN", @@ -476,13 +491,17 @@ def export_highway_shape(scenario_path: str) -> geopandas.GeoDataFrame: "COJUR": "count_jur", "COSTAT": "count_stat", "COLOC": "count_loc", - "IFC": "ifc", - "IFC_Desc": "ifc_desc", - "IHOV": "ihov", - "ITRUCK": "itruck", - "ISPD": "post_speed", - "IWAY": "iway", - "IMED": "imed", + "FC": "fc", + "FC_Desc": "fc_desc", + "HOV": "hov", + "EATRUCK": "truck_ea", + "AMTRUCK": "truck_am", + "MDTRUCK": "truck_md", + "PMTRUCK": "truck_pm", + "EVTRUCK": "truck_ev", + "SPD": "post_speed", + "WAY": "way", + "MED": "med", "AN": "from_node", "FXNM": "from_nm", "BN": "to_node", diff --git a/src/main/resources/runSandagAbm_ActivitySimResident.cmd b/src/main/resources/runSandagAbm_ActivitySimResident.cmd index 4f6e4fc6d..66fc2d298 100644 --- a/src/main/resources/runSandagAbm_ActivitySimResident.cmd +++ b/src/main/resources/runSandagAbm_ActivitySimResident.cmd @@ -50,9 +50,9 @@ MD resident\log CD .. :: Run simulation.py - %PYTHON3% src/asim/simulation.py -s settings_mp.yaml -c src/asim/configs/resident -c src/asim/configs/common -d input -d output/skims -o output/resident || exit /b 2 + :::::::::::::::::::::: CD /d %ANACONDA2_DIR%\Scripts ECHO %cd%