Utilities: mtldp.utils#
mtldp.utils (repository mtldp-meta-utils) contains every function that manipulates the
mtldp.meta data structures without touching raw vendor data: configuration and paths, file
input and output, time and geographic helpers, filters, aggregation, plotting, database export
and the command line tools. It is a namespace package: import the sub-modules directly, for
example from mtldp.utils.data_io import load_traffic_network_from_pickle.
Sub-module |
Purpose |
|---|---|
|
|
|
The command line tools (Command line tools of mtldp-meta-utils). |
|
Load and save networks, trajectories and SPaT; download OpenStreetMap data. |
|
Conversions between timestamps, dates, clock times and time of day. |
|
Distances, headings, coordinate transforms and the movement-index convention. |
|
Cut OSM files to a bounding box; select trajectories. |
|
Aggregate trajectory indices per movement, junction, link, region or arterial. |
|
Shortest paths on the link graph, time-of-day plan segmentation, reference distances. |
|
Network and trajectory objects to |
|
Resample trajectory points on a regular time grid. |
|
Corridor travel time. |
|
Time-space diagrams, metric plots, tables and the region map. |
|
MySQL table definitions and upload helpers. |
|
CLI decorator, logging and chunking helpers. |
data_io#
Traffic network#
dump_traffic_network_to_pickle(network, pickle_filename) and
load_traffic_network_from_pickle(pickle_filename) persist the whole Network object graph.
The network is a deeply recursive structure, so the dump runs in a thread with a 64 MiB stack and
the module raises the recursion limit on import. The loader returns None (and logs the
traceback) when the file cannot be read.
save_network_to_csv(network, folder) writes junctions.csv, links.csv, segments.csv,
movements.csv and arterials.csv using the converters described below.
Network geometry as GeoJSON#
save_network_geometry_to_json(network, output_file=None, arterial_only=False, junction_id_list=None)
writes the geometry of a network for web visualisation (the review tool and the signal portal
consume it). The file is a JSON object with five keys, each a GeoJSON FeatureCollection:
Layer |
Geometry |
Properties (every feature has |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Coordinates follow the GeoJSON order [lon, lat] with six decimals. arterial_only keeps
the junctions touched by an arterial and their elements; junction_id_list keeps the listed
junctions. The file is written with dumps_compact_coords, which indents the structure but
puts every coordinates array on one line to keep the files small. With output_file=None
the dictionary is returned instead of written.
Trajectories#
Raw vendor files are read by two loaders that normalise them to the same columns (trip_id,
timestamp in seconds, latitude, longitude, speed in m/s, heading,
elevation in meters) and set traj_id = trip_id:
load_GM_trajectory_from_CSV(filename_list, format_version)withformat_versionGM_2024_pre(columnsts,vehiclevix,subtaskdatalat,subtaskdatalng,subtaskdataspdinkm, …, plus vehicle length and width) orGM_2025(columnst_epoch,trip_id,lat,lon,speed_kmph,direction_deg,elev). Timestamps are converted from milliseconds and speeds from km/h;load_SL_trajectory_from_parquet(filename_list, format_version)for theSL_2025Parquet delivery (journey_id,capture_time,speed_mph,heading_deg_north,elevation_ft). Rows whosefuzzed_pointflag is notFalseare discarded.
dump_points_to_CSV_by_date(points_df, filename_fn, append=True) groups the points by their
date column and writes one CSV per date with the fixed column order trip_id, traj_id,
timestamp, longitude, latitude, elevation, speed, heading, date,
date_time, tod; existing files are appended to.
Processed trajectories are read by load_trajectories_from_csv(filename, junction_id_list, sort,
drop_duplicate, load_row_num, load_points) (Polars reader, optional junction filter, optional
skipping of the per-point list columns) and turned into objects by
build_trajectory_dict_from_csv(trajectories_filename, junction_id_list, load_points, sort,
deduplicate, load_row_num), which returns a TrajectoryDict.
load_realtime_backup_trajectory and preload_realtime_backup_trajectory read the
compressed JSON-lines backups of the real-time feed, and denoise_trajectories(trajs_df,
max_time, max_control_delay, max_distance) clips outliers.
SPaT#
load_region_spat_from_csv(region, estimate=False) reads the seven CSV files of the region’s
raw_spat directory into a HistoryRegionSPaT; dump_region_spat_to_pickle and
load_region_spat_from_pickle(pickle_path, estimate=False) persist it (estimate=True
addresses the *_estimate.pickle twin).
ProcessedDataLoader#
ProcessedDataLoader(path_manager, timezone) is the loader behind AppRegion and
ProdRegion: load_network(), load_spat(estimate), load_trajs_df(...) and
load_trajs_dict(date_str_list, junction_id_list, load_points, overwrite_buffer, load_row_num,
signalized). The last one parses each day’s CSV once and caches the resulting TrajectoryDict
as trajectories/trajectories/cache/<date>_trajs.pkl (<date>_signalized_trajs.pkl for
the signalized files); later calls read the pickle and re-apply the junction filter. Passing
overwrite_buffer=True or a load_row_num (debug mode, never cached) forces a re-parse.
OSMDownloader#
OSMDownloader(bbox, query_template=None) downloads OpenStreetMap XML through the Overpass API
(overpass-api.de first, overpass.kumi.systems as fallback on HTTP 429 or 504). The default
query selects all highway nodes, ways and relations except footways, paths, service roads,
cycleways and similar non-vehicular features. get_osm_xml(filename_prefix, region_id,
lat_step_size_threshold=0.1, lon_step_size_threshold=0.1, expand_meters=200) performs the tiling
described in Sub-regions and writes <prefix>_bbox_dict.json. Already downloaded tiles
are skipped, so an interrupted download can be resumed.
time#
Four representations of time are used throughout MTLDP:
Name |
Meaning |
|---|---|
|
Unix epoch time in seconds (float). |
|
Local date string |
|
Local clock time string |
|
Time of day as hours since local midnight, e.g. |
The default time zone of the conversion functions is UTC (DEFAULT_TIMEZONE); the region’s
time zone must be passed explicitly (DEFAULT_LOCAL_TIMEZONE is America/Detroit).
Timestamp conversions:
timestamp_to_date_time_and_tod(timestamp, ..., timezone_name)returns(date, date_time, tod);timestamp_to_hhmm,get_seconds_in_day_from_timestamp,get_floor_timestamp(timestamp, floor_minute, timezone_name)andtimestamp_to_time_in_cycle(ts, tod_start, cycle, offset, timezone_name)(position of a timestamp inside a signal cycle). The inverse isget_timestamp_from_date_tod(date, tod, timezone_name).Clock and
todconversions:date_time_to_tod,tod_to_date_time,date_time_to_seconds_in_day,seconds_in_day_to_tod,seconds_in_day_to_date_time,get_seconds_between_tods,get_seconds_between_date_time, andtod_range_to_str(tod_range, sep='-')([7, 9]becomes0700-0900, used in file names).Ranges:
get_date_range_str_list(start_date, end_date=None, date_interval=1, weekdays=True, weekends=True)lists the dates between two bounds (inclusive);weekdays=Falseorweekends=Falsedrops the corresponding days, which is what the--exclude-weekendand--exclude-weekdayoptions of the pipeline commands do.get_date_time_range(start, end, minute_interval)lists clock strings;get_date_name(date_list)yields<first>_<last>.DataFramehelpers:df_add_date_time_and_tod(df, attr='timestamp', timezone_name=...)adds thedate,date_timeandtodcolumns from atimestampcolumn (used bysplit_points);df_add_date,df_add_tod,df_add_date_timeanddf_add_seconds_in_dayadd one column each.
geo_utils#
Distances (
distance):haversine_distance((lat, lon), (lat, lon))in meters,get_traveled_distance(lat_list, lon_list)(cumulative distance along a trace, starting at 0),get_dis_to_point,destination_point(lat, lon, distance, bearing)andsegment_gps_trace(lat_list, lon_list, segment_length=50).Headings (
heading): headings are degrees in(-180, 180].get_heading_directionbins a heading into the cardinal from direction,get_gps_trace_heading_inforeturns the forward and backward headings of a trace and their length-weighted versions,weighted_circular_mean_degree,normalize_degreeandreverse_degree.Transforms (
transform):gps_to_xy_meters(lat, lon, ref_lat, ref_lon)andxy_meters_to_gpsconvert between GPS and a local metric frame anchored at a reference point (Region.convert_GPS_to_xyuses the south-west corner of the bounding box).Geometry (
advanced):shift_geometry(geometry, shift_distance=7, shift_direction='left')offsets a polyline sideways (display geometry),get_shifted_gps_tracemoves a trace along a bearing.Movement convention (
constants):signal_movement_dictdefines the 16 movement indices (1 to 8 lefts and throughs, 9 to 12 rights, 13 to 16 U-turns),get_movement_id(geo_dir, turn_dir),get_turning_direction(upstream_from_dir, downstream_from_dir),get_turning_direction_from_headings(from_heading, to_heading)(straight within ±45°, left, right, U-turn beyond ±135°),movement_conflicting_dict,shared_approach_movement_dict,same_approach_through_movement_dict,get_movement_name_by_movement_indexandcalc_r2(straightness of a polyline). The buffersMAP_BBOX_EXTEND_METER(200 m, map download and OSM cutting) andGPS_POINTS_BBOX_EXTEND_METER(100 m) and the plot orderingDIRECTION_ORDER(S N E W) live here too.
filter#
filter.osm.filter_osm_by_bbox(input_file, output_file, bbox) cuts an OSM file to a bounding
box with complete ways semantics (the behaviour of osmium extract -s complete_ways): a way
is kept as soon as one of its nodes is inside the box, and all nodes it references are written
even when they lie outside. This keeps the road graph connected across the border, which matters
for map matching. The input does not need to be sorted; it is read in three passes with
pyosmium. create_sub_regions, copy_regions and extract_osm_map use it.
filter.trajectory (re-exported as mtldp.utils.filter) selects subsets of a
TrajectoryDict and always returns a new dictionary:
by attribute:
get_tod_trajs_dict(trajs_dict, (start, end)),get_movements_trajs_dict,get_junctions_trajs_dict,get_link_trajs_dict,filter_trajs_dict_based_on_traj_ids,direction_filter(region, trajs_dict, direction_list=['s', 'l']);by size and stops:
length_filter(at least one point),distance_filter(trajs_dict, distance=50),max_stop_filter,exact_stop_filter,min_stop_filter;by quality:
error_filter(trajs_dict, region)drops trajectories whose average matching error exceeds ten meters per lane of the approach,large_frequency_filterdrops trajectories with implausible jumps (over 50 m/s or gaps above 20 s), andfilter_trajs_dict_based_on_experience(trajs_dict, tod_interval)applies a set of practical thresholds and returns(kept, outliers).
The filter_trajs_dict_based_on_* names are aliases of the corresponding get_* functions.
aggregation#
get_aggregated_df(trajs_df, layer='movement', resolution=2, timezone='America/Detroit',
aggregate_date=False) aggregates a table of trajectories into time slices of resolution
minutes at the movement, node (junction), link or all (region) level. Each slice
reports the trajectory count (traj_num), the counts of non-stopping and once-stopping
trajectories, and the mean or sum of control_delay, stop_delay, stop_nums, free_v,
queue_dis, queue_ratio, cycle_failure, spill_over, travel time and travel
distance. aggregate_date=True folds several days into one time-of-day profile.
get_aggregated_arterial_df(df, arterial_list, tod_interval, fill_all_time=True) produces one
row per arterial direction and time slot (tod_interval in hours) with totals of trajectories,
travel time and distance, delay, stops, spill-overs and split failures, plus through_
variants when the table has a through column.
algs#
build_link_networkx_graph(network, allow_backward=True)builds the directed graph whose nodes are links and whose edges are movements (sink movements are skipped, U-turns optionally) and stores it innetwork.networkx_graph.shortest_path_between_links(network, upstream_link_id, downstream_link_id, weight_attrib='length')runs Dijkstra on that graph and returns(link_id_list, weight). It is whatNetworkLinkPathand the arterial builder use to fill gaps between links.tod_opt(features, n_cut, n_min_steps=1)splits equal-length time series inton_cuthomogeneous periods by dynamic programming (time-of-day plan design).calc_ref_dist.reference_distance(...)rebases a cumulative distance so that zero is at a reference point such as a stop bar, andveh_len_calibrationcorrects for vehicle length.
converter#
list_str.list2str(values, con_by='|')andcommon.list_to_str(values, connector=' ')are the two delimiters used in CSV columns: pipe for trajectory list columns, space for network element lists.trajectory.df_to_trajectory_dict(trajs_df, load_points=True)turns a trajectory table into aTrajectoryDict(list columns are split on|,stop_detailsis decoded withparse_stop_details), andtrajs_dict_to_tripsgroups aTrajectoryDictintoTripobjects.traffic_network.convert_junction_to_df,convert_link_to_df,convert_segment_to_df,convert_movement_to_dfandconvert_corridor_to_dfexport one network layer each as aDataFrame(the columns of thenetwork/csvfiles and of the database tables).
interpolation#
interpolate_points(traj, attributes, resolution, degree=1) resamples the selected point
attributes of a trajectory on a regular time grid, marks generated rows with an interpolated
column and fills the remaining columns from the nearest original row on the same side of the
stop bar.
performance#
arterial_travel_time.get_corridor_travel_times(trip_dict, movement_list, distance,
upstream_dis=50) measures the end-to-end travel time of the trips that traverse a complete list
of corridor movements, trims outliers beyond two standard deviations and returns
(mean, count, std).
visualizer#
Plotter(output_dir, fig_size, fig_format, dpi)wraps Matplotlib with the primitives used by all plots:create_plot,save_figure, time-space lines (plot_ts,plot_no_agg_ts), signal bars (plot_signal_bar,plot_signal_lines), histograms, stacked bars and cycle markers.TableDrawer(output_dir)writes tables to Excel sheets or CSV.convertturns trajectories and SPaT into plot coordinates:movement_trajs_dict_to_xy_lists(optionally folding every trajectory into one signal cycle),movement_spat_to_signal_bar(green, yellow, clearance, red and early-green intervals with the colours inSIGNAL_TO_COLOR),trajs_dict_to_stop_cat_color, arrival and delay histograms and the corridor helpersextract_y_location_listandcorridor_trajs_dict_to_movement_lists.trajectorydraws time-space diagrams:plot_no_agg_movement_ts(real clock),plot_movement_tsandplot_movement_ts_with_category(folded into the cycle, optionally coloured by number of stops) andplot_bidirectional_corridor_ts(both directions of an arterial with the signal bars of every junction).metricdraws and tabulates indices:plot_movement_control_delay_scatter,plot_movement_control_delay_hist,plot_movement_arrival_hist,plot_movement_trajs_num_with_category(average daily observations by time of day and stop category),write_junction_metrics_to_tableandget_arterial_metrics_to_table.map.plot_region_bbox(master_config_file_path)draws the sub-region bounding boxes on a Mapbox basemap and writesregion_bounding_box.pngand.htmlinto the region’sfiguresdirectory (thedraw_region_bboxcommand).
database#
MySQLTable(table_name, column_list, unique_keys, unique_key_name) wraps an SQLAlchemy table
definition (InnoDB, utf8mb4). traffic_network defines the junctions, links,
segments, movements, arterials, lanesets and arterial_movement tables and
trajectory_and_index the movement, junction and arterial index tables, the per-trajectory
table and the matched points table. upload.get_mysql_engine(config_path) builds an engine from
a JSON credentials file, upload_network_to_mysql(network, engine, version) uploads the network
layers, and save_df_to_db uploads a large DataFrame in chunks. SQLAlchemy is an optional
dependency: the modules import without it but cannot connect.
common#
typer_runturns a function with typed parameters into a Typer command line entry point.split_trip_df_to_chunks(df, num_chunks)splits a point table into chunks on trip boundaries, so that parallel workers never see half a trip.add_filelog_handler(log_dir, log_name)adds a daily rotating file handler to the root logger (build_traffic_networkwritesnetwork/map.logwith it).show_attributes_and_methods(instance)prints the attributes and methods of an object; the example notebooks use it to explore the data structures.