aadtkit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. aadtkit-0.1.0/CHANGELOG.md +22 -0
  2. aadtkit-0.1.0/CITATION.cff +20 -0
  3. aadtkit-0.1.0/LICENSE +21 -0
  4. aadtkit-0.1.0/MANIFEST.in +9 -0
  5. aadtkit-0.1.0/PKG-INFO +225 -0
  6. aadtkit-0.1.0/README.md +190 -0
  7. aadtkit-0.1.0/examples/quickstart.py +65 -0
  8. aadtkit-0.1.0/pyproject.toml +105 -0
  9. aadtkit-0.1.0/setup.cfg +4 -0
  10. aadtkit-0.1.0/src/aadtkit/__init__.py +134 -0
  11. aadtkit-0.1.0/src/aadtkit/__main__.py +7 -0
  12. aadtkit-0.1.0/src/aadtkit/_version.py +1 -0
  13. aadtkit-0.1.0/src/aadtkit/aadt.py +282 -0
  14. aadtkit-0.1.0/src/aadtkit/classification.py +105 -0
  15. aadtkit-0.1.0/src/aadtkit/cli.py +307 -0
  16. aadtkit-0.1.0/src/aadtkit/errors.py +17 -0
  17. aadtkit-0.1.0/src/aadtkit/expand.py +148 -0
  18. aadtkit-0.1.0/src/aadtkit/factors.py +372 -0
  19. aadtkit-0.1.0/src/aadtkit/io.py +312 -0
  20. aadtkit-0.1.0/src/aadtkit/peak.py +154 -0
  21. aadtkit-0.1.0/src/aadtkit/py.typed +0 -0
  22. aadtkit-0.1.0/src/aadtkit/quality.py +231 -0
  23. aadtkit-0.1.0/src/aadtkit.egg-info/PKG-INFO +225 -0
  24. aadtkit-0.1.0/src/aadtkit.egg-info/SOURCES.txt +33 -0
  25. aadtkit-0.1.0/src/aadtkit.egg-info/dependency_links.txt +1 -0
  26. aadtkit-0.1.0/src/aadtkit.egg-info/entry_points.txt +2 -0
  27. aadtkit-0.1.0/src/aadtkit.egg-info/requires.txt +6 -0
  28. aadtkit-0.1.0/src/aadtkit.egg-info/top_level.txt +1 -0
  29. aadtkit-0.1.0/tests/conftest.py +41 -0
  30. aadtkit-0.1.0/tests/test_aadt.py +163 -0
  31. aadtkit-0.1.0/tests/test_expand.py +93 -0
  32. aadtkit-0.1.0/tests/test_factors.py +133 -0
  33. aadtkit-0.1.0/tests/test_io_cli.py +209 -0
  34. aadtkit-0.1.0/tests/test_peak_classes.py +103 -0
  35. aadtkit-0.1.0/tests/test_quality.py +71 -0
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.1.0] - 2026-11-09
8
+ ### Added
9
+ - Continuous-count AADT: AASHTO method from monthly average days of week (MADW),
10
+ the FHWA hour-based calendar-weighted procedure (TMG 2022, 3.1.4.7) and the simple mean;
11
+ complete-day aggregation, monthly ADT, calendar day-of-week occurrences.
12
+ - Monthly, day-of-week and month x day-of-week expansion factors; factor groups with
13
+ per-factor standard deviation, CV and 95 % confidence half-width; k-means clustering of
14
+ stations on factor patterns.
15
+ - Short-count expansion to AADT with axle-correction and growth factors, on calendar days or
16
+ consecutive 24-hour blocks; axle-correction factors from classification counts.
17
+ - K-factor (n-th highest hour), D-factor, design hour volume and peak-hour factor.
18
+ - FHWA 13-class vehicle categories, class and group shares, truck share.
19
+ - Data-quality checks: missing hours, negative values, zero runs, constant runs, ceiling
20
+ and robust (median/MAD) outliers.
21
+ - CSV I/O for hourly counts and factor tables; `aadtkit` command line with `aadt`,
22
+ `factors`, `cluster`, `expand`, `peak`, `qc` and `classes`.
@@ -0,0 +1,20 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ authors:
4
+ - family-names: "Eminoğlu"
5
+ given-names: "Yusuf"
6
+ email: "geospacephilo@gmail.com"
7
+ orcid: "https://orcid.org/0009-0005-6000-2934"
8
+ title: "aadtkit"
9
+ version: "0.1.0"
10
+ date-released: "2026-11-09"
11
+ license: "MIT"
12
+ repository-code: "https://gitlab.com/geophilo1/aadtkit_sdk"
13
+ url: "https://pypi.org/project/aadtkit/"
14
+ abstract: "Traffic count processing and AADT estimation following the FHWA Traffic Monitoring Guide: AASHTO and hour-based AADT for continuous count stations, monthly and day-of-week expansion factors, factor groups, short-count expansion with axle-correction factors, K-, D- and peak-hour factors, FHWA 13-class vehicle shares and data-quality checks."
15
+ keywords:
16
+ - traffic
17
+ - AADT
18
+ - traffic monitoring
19
+ - expansion factors
20
+ - transportation planning
aadtkit-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yusuf Eminoğlu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include CITATION.cff
5
+ include pyproject.toml
6
+ include src/aadtkit/py.typed
7
+ recursive-include src/aadtkit *.py
8
+ recursive-include tests *.py
9
+ recursive-include examples *.py
aadtkit-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.4
2
+ Name: aadtkit
3
+ Version: 0.1.0
4
+ Summary: Traffic count processing and AADT estimation following the FHWA Traffic Monitoring Guide: AASHTO and hour-based AADT, expansion factors, factor groups, short-count expansion, K/D/peak-hour factors, FHWA 13-class shares and data-quality checks.
5
+ Author-email: Yusuf Eminoğlu <geospacephilo@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://gitlab.com/geophilo1/aadtkit_sdk
8
+ Project-URL: Repository, https://gitlab.com/geophilo1/aadtkit_sdk
9
+ Project-URL: Issues, https://gitlab.com/geophilo1/aadtkit_sdk/-/work_items
10
+ Project-URL: Changelog, https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/CHANGELOG.md
11
+ Keywords: traffic,aadt,traffic-counts,traffic-monitoring,fhwa,expansion-factors,k-factor,transportation-planning,vehicle-classification,data-quality
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Environment :: Console
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Classifier: Topic :: Scientific/Engineering :: GIS
23
+ Classifier: Intended Audience :: Science/Research
24
+ Classifier: Intended Audience :: Developers
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.9
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
31
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
32
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
33
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # aadtkit
37
+
38
+ [![PyPI version](https://img.shields.io/pypi/v/aadtkit.svg)](https://pypi.org/project/aadtkit/)
39
+ [![Python versions](https://img.shields.io/pypi/pyversions/aadtkit.svg)](https://pypi.org/project/aadtkit/)
40
+ [![pipeline](https://gitlab.com/geophilo1/aadtkit_sdk/badges/main/pipeline.svg)](https://gitlab.com/geophilo1/aadtkit_sdk/-/pipelines)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/LICENSE)
42
+
43
+ **Traffic count processing and AADT estimation, following the FHWA *Traffic Monitoring Guide*.**
44
+
45
+ `aadtkit` turns hourly traffic counts into the numbers a traffic monitoring
46
+ program reports and a planner designs with:
47
+
48
+ - **AADT at continuous count stations**: the AASHTO method (averaging monthly
49
+ average days of week, MADW) and the hour-based, calendar-weighted
50
+ procedure FHWA has recommended since 2016 (TMG 2022);
51
+ - **expansion factors**: monthly, day-of-week and month x day-of-week;
52
+ - **factor groups**: group averages with dispersion and 95 % precision, and
53
+ k-means clustering of stations on their factor patterns;
54
+ - **short-count expansion** to AADT with axle-correction and growth factors;
55
+ - **design-hour characteristics**: K-factor (30th highest hour), D-factor,
56
+ design hour volume and peak-hour factor;
57
+ - **vehicle classification**: the FHWA 13 classes, class and group shares;
58
+ - **data-quality checks**: missing hours, zero runs, stuck counters, ceilings
59
+ and robust outliers;
60
+ - **CSV I/O** and an `aadtkit` command line.
61
+
62
+ It runs on the Python standard library alone.
63
+
64
+ ```bash
65
+ pip install aadtkit
66
+ ```
67
+
68
+ Requires Python 3.9 or newer.
69
+
70
+ ---
71
+
72
+ ## Quickstart
73
+
74
+ ```python
75
+ import aadtkit as ak
76
+
77
+ records = ak.read_hourly_csv("ccs_2024.csv") # station,datetime,volume[,direction][,vehicle_class]
78
+ series = ak.to_series(records, station="CCS-1") # {hour-start datetime: volume}
79
+
80
+ report = ak.check_hourly(series) # flag, never modify
81
+ clean = ak.drop_flagged(series, report)
82
+
83
+ res = ak.aadt_hourly_weighted(clean) # TMG 2022 hour-based procedure
84
+ res_aashto = ak.aadt_aashto(ak.daily_totals(clean)) # AASHTO / MADW
85
+ print(res.aadt, res.madt[7], res.madw[(7, 4)]) # AADT, July MADT, July Fridays
86
+
87
+ factors = ak.factors_from_result(res, "CCS-1") # AADT/MADT, AADT/ADT_dow, AADT/MADW
88
+
89
+ short = ak.to_series(ak.read_hourly_csv("short_count.csv"))
90
+ est = ak.short_count_aadt(short, factors, axle_factor=0.92, growth=ak.growth_factor(0.015, 1))
91
+ print(est.aadt, est.raw_adt)
92
+
93
+ k = ak.k_factor(clean, res.aadt) # 30th highest hour / AADT
94
+ d = ak.d_factor(ak.by_direction(records, "CCS-1")) # peak-direction share at that hour
95
+ ```
96
+
97
+ A complete runnable example on synthetic data is in
98
+ [`examples/quickstart.py`](https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/examples/quickstart.py).
99
+
100
+ ### Command line
101
+
102
+ ```bash
103
+ aadtkit qc ccs.csv # issues as CSV, summary on stderr
104
+ aadtkit aadt ccs.csv --method hourly # or aashto / simple
105
+ aadtkit factors ccs.csv --group RURAL -o factors.csv
106
+ aadtkit cluster factors.csv -k 3 # factor groups by k-means
107
+ aadtkit expand short.csv --factors factors.csv --group RURAL --axle-factor 0.92
108
+ aadtkit peak ccs.csv --nth 30 # K- and D-factor
109
+ aadtkit classes class_counts.csv # FHWA 13-class shares
110
+ ```
111
+
112
+ Add `--hour-ending` when timestamps (or hours 1-24) mark the end of the hour.
113
+
114
+ ### CSV layout
115
+
116
+ ```text
117
+ station,datetime,volume,direction,vehicle_class
118
+ CCS-1,2024-01-01 00:00,312,NB,2
119
+ ```
120
+
121
+ `datetime` may be replaced by `date` + `hour`; `direction` and
122
+ `vehicle_class` are optional. Empty or `NA` volumes are missing hours.
123
+ Factor tables use `name,type,month,dow,factor` with `type` in `monthly`,
124
+ `dow`, `month_dow`. Days of week are numbered 0 = Monday ... 6 = Sunday.
125
+
126
+ ## API overview
127
+
128
+ | Area | Functions / classes |
129
+ | --- | --- |
130
+ | AADT | `daily_totals`, `aadt_aashto`, `aadt_hourly_weighted`, `aadt_simple`, `madt`, `dow_occurrences`, `AADTResult` |
131
+ | Factors | `factors_from_result`, `FactorSet.factor`, `group_factors`, `FactorGroup`, `FactorStats`, `cluster_stations`, `t_critical_95` |
132
+ | Axle correction | `axle_correction_factor`, `axle_correction_from_classes`, `DEFAULT_AXLES_PER_CLASS` |
133
+ | Short counts | `short_count_aadt`, `expand_volume`, `growth_factor`, `split_24h_blocks`, `ShortCountEstimate` |
134
+ | Design hour | `highest_hours`, `nth_highest_hour`, `k_factor`, `d_factor`, `design_hour_volume`, `peak_hour_factor` |
135
+ | Classification | `FHWAClass`, `CLASS_DESCRIPTIONS`, `class_shares`, `group_shares`, `truck_share` |
136
+ | Quality | `check_hourly`, `QualityReport`, `QualityIssue`, `drop_flagged` |
137
+ | I/O | `read_hourly_csv`, `write_hourly_csv`, `to_series`, `by_station`, `by_direction`, `class_totals`, `duplicate_keys`, `read_factors_csv`, `write_factors_csv` |
138
+
139
+ ## Methods and sources
140
+
141
+ **AASHTO method** (TMG 2013, Section 3.2.1, Step 7; adopted by FHWA and the
142
+ AASHTO *Guidelines for Traffic Data Programs*):
143
+
144
+ ```text
145
+ AADT = 1/7 * sum_i [ 1/12 * sum_j ( 1/n_ij * sum_k VOL_ijk ) ]
146
+ ```
147
+
148
+ `VOL_ijk` is the daily volume of the k-th occurrence of day of week *i* in
149
+ month *j*; the inner mean is the monthly average day of week (MADW). All 84
150
+ month x day-of-week cells need at least one complete day; `allow_missing_cells`
151
+ relaxes this explicitly.
152
+
153
+ **Hour-based procedure** (TMG 2022, Section 3.1.4.7, Step 3):
154
+
155
+ ```text
156
+ MADT_m = sum_j w_jm * sum_h (1/n_hjm * sum_i VOL_ihjm) / sum_j w_jm
157
+ AADT = sum_m d_m * MADT_m / sum_m d_m
158
+ ```
159
+
160
+ `w_jm` is the number of times day of week *j* occurs in month *m* (4 or 5) and
161
+ `d_m` the number of days in the month. Hour-level averaging lets partial days
162
+ contribute; with a complete year it equals the plain mean of all 365/366 days.
163
+
164
+ **Factors** (TMG 2022, 3.1.4.7, Steps 4-5): monthly factor `AADT / MADT_m`,
165
+ day-of-week factor `AADT / ADT_j`, month x day-of-week factor
166
+ `AADT / MADW_mj`; group factors are the mean over the group's stations, with
167
+ precision `t(0.975, n-1) * s / sqrt(n)` (TMG 2022, 3.1.4.5 and 3.2.6.2).
168
+
169
+ **Short counts**: `AADT = VOL_24 * M * D * A * G`, applied seasonality first
170
+ (TMG 2022, 3.2.8, worked example `518 x 0.95 x 1.24 = 610`, reproduced in the
171
+ tests). The axle-correction factor is vehicles per axle pair from a
172
+ classification count, `ACF = vehicles / (axles / 2)` (TMG glossary: vehicles
173
+ divided by axles; the divisor 2 reflects counters that register one count per
174
+ two axle hits and is a parameter).
175
+
176
+ **Design hour**: `K = V30 / AADT` with `V30` the 30th highest hourly volume
177
+ of the year (TMG 2022, 3.1.2 and 3.1.4.3; AASHTO *Green Book*);
178
+ `D` = peak-direction share of that hour; `PHF = V / (4 * V15)` (*Highway
179
+ Capacity Manual*).
180
+
181
+ **Classification**: FHWA 13-category scheme; default groups follow HPMS
182
+ (single-unit truck and bus = classes 4-7, combination truck = 8-13).
183
+
184
+ **Quality**: robust z-score `0.6745 |x - median| / MAD` within each
185
+ day-of-week x hour group (Iglewicz and Hoaglin, 1993).
186
+
187
+ References:
188
+
189
+ - FHWA (2022). *Traffic Monitoring Guide*. U.S. Department of Transportation,
190
+ Federal Highway Administration. <https://www.fhwa.dot.gov/policyinformation/tmguide/>
191
+ - FHWA (2013). *Traffic Monitoring Guide*, Chapter 3.
192
+ - AASHTO (2009). *AASHTO Guidelines for Traffic Data Programs*, 2nd ed.
193
+ - Transportation Research Board. *Highway Capacity Manual*.
194
+ - NCHRP Research Report 1081 (2024). *Guide on Methods for Assigning Counts to
195
+ Adjustment Factor Groups*. National Academies Press.
196
+
197
+ ## Limitations
198
+
199
+ - Quality thresholds (zero run 6 h, constant run 4 h, z > 5) are engineering
200
+ defaults, not FHWA values; set them to your agency's validation rules.
201
+ - `DEFAULT_AXLES_PER_CLASS` uses nominal axle numbers; classes 3, 4, 7, 8, 10,
202
+ 11 and 13 cover ranges. Use measured axles per class where available.
203
+ - k-means on factor vectors is one of several grouping approaches; groups
204
+ should still be reviewed against road function and geography, and short
205
+ count sites must be assigned to groups by the analyst.
206
+ - Incomplete days are never imputed. Daylight-saving shifts are not handled;
207
+ supply local standard time or UTC.
208
+ - Only 95 % confidence intervals are tabulated (t values for df <= 30, 40, 60,
209
+ 120, interpolated otherwise).
210
+
211
+ ## Academic citation
212
+
213
+ ```bibtex
214
+ @software{eminoglu_aadtkit,
215
+ author = {Eminoğlu, Yusuf},
216
+ title = {aadtkit},
217
+ year = {2026},
218
+ version = {0.1.0},
219
+ url = {https://gitlab.com/geophilo1/aadtkit_sdk}
220
+ }
221
+ ```
222
+
223
+ ## License
224
+
225
+ MIT, see [LICENSE](https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/LICENSE). Contact: geospacephilo@gmail.com
@@ -0,0 +1,190 @@
1
+ # aadtkit
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/aadtkit.svg)](https://pypi.org/project/aadtkit/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/aadtkit.svg)](https://pypi.org/project/aadtkit/)
5
+ [![pipeline](https://gitlab.com/geophilo1/aadtkit_sdk/badges/main/pipeline.svg)](https://gitlab.com/geophilo1/aadtkit_sdk/-/pipelines)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/LICENSE)
7
+
8
+ **Traffic count processing and AADT estimation, following the FHWA *Traffic Monitoring Guide*.**
9
+
10
+ `aadtkit` turns hourly traffic counts into the numbers a traffic monitoring
11
+ program reports and a planner designs with:
12
+
13
+ - **AADT at continuous count stations**: the AASHTO method (averaging monthly
14
+ average days of week, MADW) and the hour-based, calendar-weighted
15
+ procedure FHWA has recommended since 2016 (TMG 2022);
16
+ - **expansion factors**: monthly, day-of-week and month x day-of-week;
17
+ - **factor groups**: group averages with dispersion and 95 % precision, and
18
+ k-means clustering of stations on their factor patterns;
19
+ - **short-count expansion** to AADT with axle-correction and growth factors;
20
+ - **design-hour characteristics**: K-factor (30th highest hour), D-factor,
21
+ design hour volume and peak-hour factor;
22
+ - **vehicle classification**: the FHWA 13 classes, class and group shares;
23
+ - **data-quality checks**: missing hours, zero runs, stuck counters, ceilings
24
+ and robust outliers;
25
+ - **CSV I/O** and an `aadtkit` command line.
26
+
27
+ It runs on the Python standard library alone.
28
+
29
+ ```bash
30
+ pip install aadtkit
31
+ ```
32
+
33
+ Requires Python 3.9 or newer.
34
+
35
+ ---
36
+
37
+ ## Quickstart
38
+
39
+ ```python
40
+ import aadtkit as ak
41
+
42
+ records = ak.read_hourly_csv("ccs_2024.csv") # station,datetime,volume[,direction][,vehicle_class]
43
+ series = ak.to_series(records, station="CCS-1") # {hour-start datetime: volume}
44
+
45
+ report = ak.check_hourly(series) # flag, never modify
46
+ clean = ak.drop_flagged(series, report)
47
+
48
+ res = ak.aadt_hourly_weighted(clean) # TMG 2022 hour-based procedure
49
+ res_aashto = ak.aadt_aashto(ak.daily_totals(clean)) # AASHTO / MADW
50
+ print(res.aadt, res.madt[7], res.madw[(7, 4)]) # AADT, July MADT, July Fridays
51
+
52
+ factors = ak.factors_from_result(res, "CCS-1") # AADT/MADT, AADT/ADT_dow, AADT/MADW
53
+
54
+ short = ak.to_series(ak.read_hourly_csv("short_count.csv"))
55
+ est = ak.short_count_aadt(short, factors, axle_factor=0.92, growth=ak.growth_factor(0.015, 1))
56
+ print(est.aadt, est.raw_adt)
57
+
58
+ k = ak.k_factor(clean, res.aadt) # 30th highest hour / AADT
59
+ d = ak.d_factor(ak.by_direction(records, "CCS-1")) # peak-direction share at that hour
60
+ ```
61
+
62
+ A complete runnable example on synthetic data is in
63
+ [`examples/quickstart.py`](https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/examples/quickstart.py).
64
+
65
+ ### Command line
66
+
67
+ ```bash
68
+ aadtkit qc ccs.csv # issues as CSV, summary on stderr
69
+ aadtkit aadt ccs.csv --method hourly # or aashto / simple
70
+ aadtkit factors ccs.csv --group RURAL -o factors.csv
71
+ aadtkit cluster factors.csv -k 3 # factor groups by k-means
72
+ aadtkit expand short.csv --factors factors.csv --group RURAL --axle-factor 0.92
73
+ aadtkit peak ccs.csv --nth 30 # K- and D-factor
74
+ aadtkit classes class_counts.csv # FHWA 13-class shares
75
+ ```
76
+
77
+ Add `--hour-ending` when timestamps (or hours 1-24) mark the end of the hour.
78
+
79
+ ### CSV layout
80
+
81
+ ```text
82
+ station,datetime,volume,direction,vehicle_class
83
+ CCS-1,2024-01-01 00:00,312,NB,2
84
+ ```
85
+
86
+ `datetime` may be replaced by `date` + `hour`; `direction` and
87
+ `vehicle_class` are optional. Empty or `NA` volumes are missing hours.
88
+ Factor tables use `name,type,month,dow,factor` with `type` in `monthly`,
89
+ `dow`, `month_dow`. Days of week are numbered 0 = Monday ... 6 = Sunday.
90
+
91
+ ## API overview
92
+
93
+ | Area | Functions / classes |
94
+ | --- | --- |
95
+ | AADT | `daily_totals`, `aadt_aashto`, `aadt_hourly_weighted`, `aadt_simple`, `madt`, `dow_occurrences`, `AADTResult` |
96
+ | Factors | `factors_from_result`, `FactorSet.factor`, `group_factors`, `FactorGroup`, `FactorStats`, `cluster_stations`, `t_critical_95` |
97
+ | Axle correction | `axle_correction_factor`, `axle_correction_from_classes`, `DEFAULT_AXLES_PER_CLASS` |
98
+ | Short counts | `short_count_aadt`, `expand_volume`, `growth_factor`, `split_24h_blocks`, `ShortCountEstimate` |
99
+ | Design hour | `highest_hours`, `nth_highest_hour`, `k_factor`, `d_factor`, `design_hour_volume`, `peak_hour_factor` |
100
+ | Classification | `FHWAClass`, `CLASS_DESCRIPTIONS`, `class_shares`, `group_shares`, `truck_share` |
101
+ | Quality | `check_hourly`, `QualityReport`, `QualityIssue`, `drop_flagged` |
102
+ | I/O | `read_hourly_csv`, `write_hourly_csv`, `to_series`, `by_station`, `by_direction`, `class_totals`, `duplicate_keys`, `read_factors_csv`, `write_factors_csv` |
103
+
104
+ ## Methods and sources
105
+
106
+ **AASHTO method** (TMG 2013, Section 3.2.1, Step 7; adopted by FHWA and the
107
+ AASHTO *Guidelines for Traffic Data Programs*):
108
+
109
+ ```text
110
+ AADT = 1/7 * sum_i [ 1/12 * sum_j ( 1/n_ij * sum_k VOL_ijk ) ]
111
+ ```
112
+
113
+ `VOL_ijk` is the daily volume of the k-th occurrence of day of week *i* in
114
+ month *j*; the inner mean is the monthly average day of week (MADW). All 84
115
+ month x day-of-week cells need at least one complete day; `allow_missing_cells`
116
+ relaxes this explicitly.
117
+
118
+ **Hour-based procedure** (TMG 2022, Section 3.1.4.7, Step 3):
119
+
120
+ ```text
121
+ MADT_m = sum_j w_jm * sum_h (1/n_hjm * sum_i VOL_ihjm) / sum_j w_jm
122
+ AADT = sum_m d_m * MADT_m / sum_m d_m
123
+ ```
124
+
125
+ `w_jm` is the number of times day of week *j* occurs in month *m* (4 or 5) and
126
+ `d_m` the number of days in the month. Hour-level averaging lets partial days
127
+ contribute; with a complete year it equals the plain mean of all 365/366 days.
128
+
129
+ **Factors** (TMG 2022, 3.1.4.7, Steps 4-5): monthly factor `AADT / MADT_m`,
130
+ day-of-week factor `AADT / ADT_j`, month x day-of-week factor
131
+ `AADT / MADW_mj`; group factors are the mean over the group's stations, with
132
+ precision `t(0.975, n-1) * s / sqrt(n)` (TMG 2022, 3.1.4.5 and 3.2.6.2).
133
+
134
+ **Short counts**: `AADT = VOL_24 * M * D * A * G`, applied seasonality first
135
+ (TMG 2022, 3.2.8, worked example `518 x 0.95 x 1.24 = 610`, reproduced in the
136
+ tests). The axle-correction factor is vehicles per axle pair from a
137
+ classification count, `ACF = vehicles / (axles / 2)` (TMG glossary: vehicles
138
+ divided by axles; the divisor 2 reflects counters that register one count per
139
+ two axle hits and is a parameter).
140
+
141
+ **Design hour**: `K = V30 / AADT` with `V30` the 30th highest hourly volume
142
+ of the year (TMG 2022, 3.1.2 and 3.1.4.3; AASHTO *Green Book*);
143
+ `D` = peak-direction share of that hour; `PHF = V / (4 * V15)` (*Highway
144
+ Capacity Manual*).
145
+
146
+ **Classification**: FHWA 13-category scheme; default groups follow HPMS
147
+ (single-unit truck and bus = classes 4-7, combination truck = 8-13).
148
+
149
+ **Quality**: robust z-score `0.6745 |x - median| / MAD` within each
150
+ day-of-week x hour group (Iglewicz and Hoaglin, 1993).
151
+
152
+ References:
153
+
154
+ - FHWA (2022). *Traffic Monitoring Guide*. U.S. Department of Transportation,
155
+ Federal Highway Administration. <https://www.fhwa.dot.gov/policyinformation/tmguide/>
156
+ - FHWA (2013). *Traffic Monitoring Guide*, Chapter 3.
157
+ - AASHTO (2009). *AASHTO Guidelines for Traffic Data Programs*, 2nd ed.
158
+ - Transportation Research Board. *Highway Capacity Manual*.
159
+ - NCHRP Research Report 1081 (2024). *Guide on Methods for Assigning Counts to
160
+ Adjustment Factor Groups*. National Academies Press.
161
+
162
+ ## Limitations
163
+
164
+ - Quality thresholds (zero run 6 h, constant run 4 h, z > 5) are engineering
165
+ defaults, not FHWA values; set them to your agency's validation rules.
166
+ - `DEFAULT_AXLES_PER_CLASS` uses nominal axle numbers; classes 3, 4, 7, 8, 10,
167
+ 11 and 13 cover ranges. Use measured axles per class where available.
168
+ - k-means on factor vectors is one of several grouping approaches; groups
169
+ should still be reviewed against road function and geography, and short
170
+ count sites must be assigned to groups by the analyst.
171
+ - Incomplete days are never imputed. Daylight-saving shifts are not handled;
172
+ supply local standard time or UTC.
173
+ - Only 95 % confidence intervals are tabulated (t values for df <= 30, 40, 60,
174
+ 120, interpolated otherwise).
175
+
176
+ ## Academic citation
177
+
178
+ ```bibtex
179
+ @software{eminoglu_aadtkit,
180
+ author = {Eminoğlu, Yusuf},
181
+ title = {aadtkit},
182
+ year = {2026},
183
+ version = {0.1.0},
184
+ url = {https://gitlab.com/geophilo1/aadtkit_sdk}
185
+ }
186
+ ```
187
+
188
+ ## License
189
+
190
+ MIT, see [LICENSE](https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/LICENSE). Contact: geospacephilo@gmail.com
@@ -0,0 +1,65 @@
1
+ """aadtkit quickstart on synthetic data (no files needed).
2
+
3
+ Run with: python examples/quickstart.py
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import datetime as dt
9
+ import math
10
+ import random
11
+ import tempfile
12
+ from pathlib import Path
13
+
14
+ import aadtkit as ak
15
+
16
+ # --- 1. A synthetic continuous count station (one year, two directions) ------
17
+ rng = random.Random(42)
18
+ season = {m: 1 + 0.2 * math.sin((m - 4) / 12 * 2 * math.pi) for m in range(1, 13)}
19
+ weekday = [1.0, 1.0, 1.02, 1.05, 1.15, 0.9, 0.8]
20
+ profile = [1, 1, 1, 1, 2, 4, 7, 9, 8, 5, 4, 4, 5, 5, 5, 6, 8, 9, 7, 5, 4, 3, 2, 2]
21
+ profile = [p / sum(profile) for p in profile]
22
+
23
+ records = []
24
+ day = dt.date(2024, 1, 1)
25
+ while day.year == 2024:
26
+ total = 12000 * season[day.month] * weekday[day.weekday()]
27
+ for h in range(24):
28
+ ts = dt.datetime.combine(day, dt.time(h))
29
+ vol = total * profile[h] * rng.uniform(0.95, 1.05)
30
+ nb_share = 0.6 if h < 12 else 0.4
31
+ records.append(ak.HourlyRecord("CCS-1", ts, round(vol * nb_share), "NB"))
32
+ records.append(ak.HourlyRecord("CCS-1", ts, round(vol * (1 - nb_share)), "SB"))
33
+ day += dt.timedelta(days=1)
34
+
35
+ with tempfile.TemporaryDirectory() as tmp:
36
+ path = Path(tmp) / "ccs.csv"
37
+ ak.write_hourly_csv(path, records)
38
+ records = ak.read_hourly_csv(path)
39
+
40
+ series = ak.to_series(records, station="CCS-1")
41
+
42
+ # --- 2. Quality checks -------------------------------------------------------
43
+ report = ak.check_hourly(series)
44
+ print(f"completeness {report.completeness:.1%}, issues: {len(report.issues)}")
45
+
46
+ # --- 3. AADT -----------------------------------------------------------------
47
+ hourly = ak.aadt_hourly_weighted(series)
48
+ aashto = ak.aadt_aashto(ak.daily_totals(series))
49
+ print(f"AADT (TMG 2022 hour-based) = {hourly.aadt:,.0f}")
50
+ print(f"AADT (AASHTO, MADW) = {aashto.aadt:,.0f}")
51
+
52
+ # --- 4. Factors and short-count expansion ------------------------------------
53
+ factors = ak.factors_from_result(hourly, "CCS-1")
54
+ print("monthly factors:", {m: round(f, 3) for m, f in factors.monthly.items()})
55
+
56
+ start = dt.datetime(2024, 4, 16, 9) # a 48-hour count starting Tuesday 09:00
57
+ short = {start + dt.timedelta(hours=i): series[start + dt.timedelta(hours=i)] for i in range(48)}
58
+ est = ak.short_count_aadt(short, factors, basis="blocks")
59
+ print(f"48-h count: raw ADT {est.raw_adt:,.0f} -> AADT estimate {est.aadt:,.0f}")
60
+
61
+ # --- 5. Design hour ----------------------------------------------------------
62
+ k = ak.k_factor(series, hourly.aadt)
63
+ d = ak.d_factor(ak.by_direction(records, "CCS-1"))
64
+ print(f"K30 = {k:.3f}, D = {d.d:.2f} ({d.peak_direction}), DHV = {hourly.aadt * k:,.0f}")
65
+ print("PHF of 15-min counts:", round(ak.peak_hour_factor([210, 245, 260, 230, 190]).phf, 3))
@@ -0,0 +1,105 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "aadtkit"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name = "Yusuf Eminoğlu", email = "geospacephilo@gmail.com" }
10
+ ]
11
+ description = "Traffic count processing and AADT estimation following the FHWA Traffic Monitoring Guide: AASHTO and hour-based AADT, expansion factors, factor groups, short-count expansion, K/D/peak-hour factors, FHWA 13-class shares and data-quality checks."
12
+ keywords = [
13
+ "traffic",
14
+ "aadt",
15
+ "traffic-counts",
16
+ "traffic-monitoring",
17
+ "fhwa",
18
+ "expansion-factors",
19
+ "k-factor",
20
+ "transportation-planning",
21
+ "vehicle-classification",
22
+ "data-quality",
23
+ ]
24
+ readme = "README.md"
25
+ license = "MIT"
26
+ license-files = ["LICENSE"]
27
+ requires-python = ">=3.9"
28
+ classifiers = [
29
+ "Development Status :: 4 - Beta",
30
+ "Programming Language :: Python :: 3",
31
+ "Programming Language :: Python :: 3.9",
32
+ "Programming Language :: Python :: 3.10",
33
+ "Programming Language :: Python :: 3.11",
34
+ "Programming Language :: Python :: 3.12",
35
+ "Programming Language :: Python :: 3.13",
36
+ "Operating System :: OS Independent",
37
+ "Environment :: Console",
38
+ "Topic :: Scientific/Engineering",
39
+ "Topic :: Scientific/Engineering :: GIS",
40
+ "Intended Audience :: Science/Research",
41
+ "Intended Audience :: Developers",
42
+ "Typing :: Typed",
43
+ ]
44
+ dependencies = []
45
+
46
+ [project.optional-dependencies]
47
+ dev = [
48
+ "pytest>=7.0.0",
49
+ "pytest-cov>=4.0.0",
50
+ "ruff>=0.1.0",
51
+ "mypy>=1.0.0",
52
+ ]
53
+
54
+ [project.scripts]
55
+ aadtkit = "aadtkit.cli:main"
56
+
57
+ [project.urls]
58
+ Homepage = "https://gitlab.com/geophilo1/aadtkit_sdk"
59
+ Repository = "https://gitlab.com/geophilo1/aadtkit_sdk"
60
+ Issues = "https://gitlab.com/geophilo1/aadtkit_sdk/-/work_items"
61
+ Changelog = "https://gitlab.com/geophilo1/aadtkit_sdk/-/blob/main/CHANGELOG.md"
62
+
63
+ [tool.setuptools]
64
+ package-dir = {"" = "src"}
65
+
66
+ [tool.setuptools.packages.find]
67
+ where = ["src"]
68
+
69
+ [tool.setuptools.package-data]
70
+ aadtkit = ["py.typed"]
71
+
72
+ [tool.ruff]
73
+ line-length = 100
74
+ target-version = "py39"
75
+ exclude = [
76
+ "scratch",
77
+ ".venv",
78
+ "build",
79
+ "dist",
80
+ ]
81
+
82
+ [tool.ruff.lint]
83
+ select = ["E", "F", "W", "I", "B", "C4"]
84
+ ignore = ["E501"]
85
+
86
+ [tool.mypy]
87
+ files = ["src"]
88
+ warn_unused_ignores = true
89
+
90
+ [tool.pytest.ini_options]
91
+ testpaths = ["tests"]
92
+ # Lets `pytest` run straight from a clone, without an editable install first.
93
+ pythonpath = ["src"]
94
+ filterwarnings = [
95
+ "ignore::DeprecationWarning",
96
+ "ignore::PendingDeprecationWarning",
97
+ ]
98
+
99
+ [tool.coverage.run]
100
+ source = ["aadtkit"]
101
+ branch = true
102
+
103
+ [tool.coverage.report]
104
+ show_missing = true
105
+ fail_under = 85
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+