Changelog
Source:NEWS.md
gps2gtfs 0.6.0
New features
-
g2g_extract_trips_and_stop_times()gainsmin_dwell(seconds, default0): stop visits with a shorter dwell are dropped. Zero dwell means no stationary ping was seen before the vehicle’s last ping in the stop’s buffer: it passed through, stopped between two pings, or was seen stationary only on that last ping.min_dwell = 1removes all three. With pings a few seconds apart that is mostly pass-throughs; with sparser pings it also removes many visits to stops that were served. The default keeps every visit, as before.
gps2gtfs 0.5.1
Bug fixes
-
g2g_extract_trips_and_stop_times()no longer fails on integer-storage timestamps (e.g. epoch seconds read bydata.table::fread()).g2g_clean_gps()now stores timestamps as doublePOSIXct; epoch values are unchanged.
gps2gtfs 0.5.0
Two new exported functions. Nothing else changed: every existing function behaves exactly as in 0.4.0.
g2g_terminals_from_geometries()andg2g_stops_from_geometries()build theterminals_dataandstops_datainputs ofg2g_extract_trips_and_stop_times()from the route’s own linestrings and a plain table of stop coordinates. They are the GTFS-free analogues ofg2g_terminals_from_gtfs()andg2g_stops_from_gtfs(), and return the same shapes, for operators who publish route geometry and a stop list but no static feed.Input shape.
geometriesis a data.frame of vertices in long form with columnsroute_id,direction,vertex_seq(the numeric order of the vertices within a direction),latitudeandlongitude— which is what three lines of any JSON/GeoJSON reader produce, and needs no spatial dependency. Ansfobject ofLINESTRINGorMULTILINESTRINGgeometries withroute_idanddirectioncolumns is also accepted when the (already suggested) ‘sf’ package is installed;MULTILINESTRINGparts are concatenated in part order.Each direction’s terminal is the first vertex of its line, and the
terminal_idis the direction label. Becauseg2g_stops_from_geometries()labels stops with those same values, the stop labels already are terminal IDs and nostop_direction_mapis needed. Exactly two directions are required; a loop or a third branch errors and points atsegmentation = "layover". Two lines whose first vertices are closer than 150 m warn, because that usually means one direction was drawn twice.A stop belongs to a direction when it is within
buffer_m(default 50 m) of that direction’s polyline — measured to the line segments, not to the vertices. A stop sitting halfway along a 300 m segment is on the route; a nearest-vertex rule would measure 150 m and drop it. Distances are computed on a local equirectangular projection in metres, vectorised over stops by segments. A stop withinbuffer_mof both lines (the two sides of one street) is returned once per direction, which is what makes the direction labels do any work. Stops matched by no direction are absent and their count is reported in a message; if that is every stop, the result is a zero-row table with the same four columns and a warning, not an error, so a caller looping over many routes can take that answer for one of them. Rows with a missing coordinate or a missing or blankstop_idare dropped with a warning giving the counts.
gps2gtfs 0.4.0
New feature, opt-in and default-off. Callers who do not set the new arguments get byte-identical behaviour to 0.3.2.
-
g2g_extract_trips()andg2g_extract_trips_and_stop_times()can now cut a trip when the supplied direction changes. Three new arguments, all with backward-compatible defaults:-
cut_on_direction_change(defaultFALSE) — whenTRUE, a change in thedirection_colvalue ends the current trip and starts a new one. WhenFALSE, nothing changes: direction is still only labelled, exactly as in 0.3.2. -
direction_debounce_min_pings(default2L) — minimum consecutive pings a direction run needs before it is allowed to cut. -
direction_debounce_min_seconds(default600) — minimum span, in seconds, a direction run needs before it is allowed to cut.
Both thresholds must be met for a run to qualify (
counts >= Nandspans >= M), so a single contrary ping, or a brief flicker at a stop, no longer splits a trip.cut_on_direction_change = TRUEwithoutdirection_colis an error rather than a silent no-op. The two thresholds are validated:min_pingsmust be one whole number>= 1,min_secondsone non-negative finite number. -
Why it exists. The
the downstream evaluationevaluation reported that the directional fast path labelled segments without splitting them, producing 12-14 hour trips. 0.3.x fixed the package’s response (it now errors on conflicting direction inside one supplied trip identity); this release adds the cutting behaviour itself, which was deferred at the time because a naive cut is not good enough — cutting on every raw direction change inflates trip counts by about 17% and pushes fragmentation from 0.40 to 0.57. The debounce is what makes the cut usable.What the evaluation showed, on one dataset. Against a pre-registered, frozen acceptance gate on held-out the held-out region vehicles (
vehicle A,vehicle B), the defaultN = 2/M = 600 ssetting improved fragmentation, downstream frequency MAPE and median trip-duration error relative to a naive route+direction cut, and met every registered bar. It is worse than that naive baseline on recovery and merge rate, which the gate did not require it to beat. This is one deterministic vehicle split of two service days on one operator: it is evidence that the feature works, not a claim about other feeds. Evaluate on your own data before relying on it.
gps2gtfs 0.3.2
Documentation only. No user-visible behaviour changed, and no function, argument or return value was renamed or added.
g2g_extract_trips()andg2g_extract_trips_and_stop_times()no longer claim to read CSV files. Both descriptions opened “Reads raw GPS and terminal CSV files”, which was never true: each ofgps_data,terminals_dataandstops_dataaccepts a data.frame or a path, exactly as their own@paramentries already said. The two front doors also had one sentence each while smaller helpers had a reasoned paragraph, so both are now written to that depth - which segmentation regime applies when, whatsession_gapbounds, what the stop-matching stage can silently drop, and whyg2g_diagnostics()is the thing to read before trusting the output.g2g_data_stopsandg2g_data_terminalsnow state their provenance. Both are documented as working withg2g_data_gps, which says it comes from Kandy, Sri Lanka; they did not, so the trio read as three unrelated sample tables. Their titles are now parallel as well.Titles are sentence case throughout, per the tidyverse convention the package mostly already followed.
Regenerating the documentation with the current roxygen2 also rewrote
NAMESPACE’simportFrom(data.table, ...)into its multi-argument form and replacedRoxygenNotewithConfig/roxygen2/version. Both are equivalent to what they replaced.
gps2gtfs 0.3.1
- The pure R backend now ignores a passthrough
bus_stopcolumn while matching terminal visits, consistent with the compiled backends.
gps2gtfs 0.3.0
Breaking changes
-
stop_direction_mapis now required whenstops_datalabels its direction groups with anything other than the terminal IDs themselves. It was previously derived by pairing the two labels with the two terminals in order of appearance — a coin flip, since the label order follows however the caller happened to buildstops_data. Losing that flip reverses every direction in the output while still producing plausible stop times, because each ping then matches the opposite-direction stop across the street. Which label belongs to which terminal is not recoverable from the data — both groups span the same corridor, so they sit at the same distance from both terminals — so the case is now an error listing both candidate maps rather than a guess. Callers whose labels are already terminal IDs (everyone usingg2g_stops_from_gtfs()) and users ofdirection_color layover segmentation are unaffected.
Bug fixes
Stop extraction no longer breaks when the GPS data carries a column of its own named
direction.g2g_clean_gps()passes unknown columns through, and such a column collided with the trip-level direction merged into the trajectory: every direction group then matched zero pings and extraction failed with an error naming neither the column nor the cause. It affected every segmentation mode, not only the supplied-identity path. Columns whose names stop extraction reserves (direction,bus_stop,stop_id,grouped_ends, and the already-handledtrip_id) are now dropped from the trajectory with a message.g2g_extract_trips()was never affected.direction_colnow errors instead of silently discarding data when it takes more than one value inside a segment thattrip_coldeclared to be one trip. A trip has exactly one direction, so the two inputs contradict each other; keeping the first value merged an out-and-back into a single multi-hour “trip” that directional routing rejects. The error names both resolutions (add the column totrip_col, or collapse it per trip identity). Direction that is constant within each supplied trip identity — the well-formed GTFS-Realtime case — is unaffected.g2g_terminals_from_gtfs()andg2g_stops_from_gtfs()no longer render numericstop_id/route_idvalues in scientific notation (1e+05), which silently broke every downstream id join.g2g_stops_from_gtfs()warns when trips are dropped for not starting at either derived terminal, instead of silently omitting the stops they serve.g2g_terminals_from_gtfs()warns when the two derived terminals are close enough together to be two platforms of one place rather than the two ends of the route, and its loop-route error now points atsegmentation = "layover".
New features
g2g_diagnostics()gainsmax_trip_duration_mins, the longest extracted trip. Reported, never judged: a mis-segmented run merges a whole shift into one “trip” while the trip count stays plausible, and no other metric shows it.tripsandstop_timesgain an additive, versioned set of C5 inference-label columns for a future baseline-free orientation/turnaround-detection stage:orientation_id,orientation_status,orientation_confidence, and a reserved-nullablepattern_ref(both tables), plus trips-onlystart_anchor_ref/end_anchor_ref. No orientation detector is enabled yet, so every new column is in its empty state (orientation_id/_confidence/pattern_ref/anchorsNA,orientation_status = "none") and the legacydirectioncolumn is retained unchanged. The columns are appended after all pre-existing columns, so existing output is byte-for-byte compatible.orientation_*/pattern_refpropagate fromtripsontostop_timeson the internaltrip_id, the same mechanism asprovided_trip_id. See?g2g_extract_trips_and_stop_times(“Orientation and pattern labels”).
gps2gtfs 0.2.0
New features
- New
segmentation = "layover"mode ing2g_extract_trips()andg2g_extract_trips_and_stop_times()segments raw GPS without terminals or trip ids: trips are cut at dwells longer thanlayover_gap(default 10 min), detected as a silent inter-ping gap or a stationary spell withinlayover_radius(default 50 m). Handles short-turn, loop, and multi-branch routes; emits a single direction group.segmentation = "auto"(default) uses terminals whenterminals_datais supplied, layover otherwise. - New
trip_colsegments by supplied trip identities (fast path) instead of terminal-buffer inference; may name several columns that jointly identify a trip (e.g. the GTFS-Realtime TripDescriptor). - New
direction_coltakes trip direction from the data instead of terminal inference, soterminals_databecomes optional and non-two-terminal routes (short-turns, variants, one-way) work. - New
g2g_terminals_from_gtfs()andg2g_stops_from_gtfs()deriveterminals_data/stops_datafrom a baseline static GTFS feed. - New
g2g_stops_from_positions()estimates stop coordinates fromSTOPPED_ATvehicle positions;g2g_shapes_from_trips()(withreturn_trajectory = TRUE) builds ashapes.txt-shaped table of the geometry driven. -
g2g_clean_gps()gainsdedupeanddrop_missing_vehiclehygiene options, atzargument (also on both pipeline entry points), row-idgeneration, and optionalspeed. -
g2g_extract_trips()andg2g_extract_trips_and_stop_times()now return rows in a documented, backend-invariant order —tripsbytrip_id,stop_timesby(trip_id, arrival_time, stop_id, departure_time)— so summaries built on the output are reproducible regardless of backend or parallelism. -
g2g_extract_trips()andg2g_extract_trips_and_stop_times()now attach an extraction coverage diagnostics table (per-stage ping, segment, and trip counts) to the result, retrievable with the newg2g_diagnostics(), and emit a one-linewarning()when a run drops coverage worth surfacing so unattended pipelines notice silent loss. Silence it withdiagnostics_warn = FALSEoroptions(gps2gtfs.diagnostics_warn = FALSE).
Breaking changes
- Input/output columns follow the GTFS-Realtime convention (
vehicle_id,timestamp); map other schemes viavehicle_col/time_col.g2g_data_gpsuses the new names. - Times in the returned tables are absolute
POSIXct(input timezone), not"HH:MM:SS"strings — format yourself if clock strings are needed. - Returned
trips/stop_timesare documented as inference tables, not valid GTFStrips.txt/stop_times.txt(noroute_id,service_id,stop_sequence, or clock strings). - Trips are segmented within driving sessions (
session_gap, default 4 h) instead of calendar days, so midnight-crossing trips stay intact.
gps2gtfs 0.1.1
-
day_of_weekis now an ordered factor from Monday through Sunday instead of a raw integer; useas.integer(day_of_week)for ISO weekday numbers (1= Monday, …,7= Sunday). -
g2g_extract_trips()andg2g_extract_trips_and_stop_times()now return logicalis_weekdayvalues for weekday/weekend filtering.