roadnoise 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 (38) hide show
  1. roadnoise-0.1.0/CHANGELOG.md +23 -0
  2. roadnoise-0.1.0/CITATION.cff +20 -0
  3. roadnoise-0.1.0/LICENSE +21 -0
  4. roadnoise-0.1.0/MANIFEST.in +9 -0
  5. roadnoise-0.1.0/PKG-INFO +273 -0
  6. roadnoise-0.1.0/README.md +233 -0
  7. roadnoise-0.1.0/examples/quickstart.py +35 -0
  8. roadnoise-0.1.0/examples/receivers.geojson +67 -0
  9. roadnoise-0.1.0/examples/roads.geojson +56 -0
  10. roadnoise-0.1.0/pyproject.toml +121 -0
  11. roadnoise-0.1.0/setup.cfg +4 -0
  12. roadnoise-0.1.0/src/roadnoise/__init__.py +99 -0
  13. roadnoise-0.1.0/src/roadnoise/atmosphere.py +72 -0
  14. roadnoise-0.1.0/src/roadnoise/cli.py +287 -0
  15. roadnoise-0.1.0/src/roadnoise/constants.py +25 -0
  16. roadnoise-0.1.0/src/roadnoise/crtn.py +122 -0
  17. roadnoise-0.1.0/src/roadnoise/diffraction.py +221 -0
  18. roadnoise-0.1.0/src/roadnoise/emission.py +409 -0
  19. roadnoise-0.1.0/src/roadnoise/geo.py +96 -0
  20. roadnoise-0.1.0/src/roadnoise/geometry.py +187 -0
  21. roadnoise-0.1.0/src/roadnoise/ground.py +186 -0
  22. roadnoise-0.1.0/src/roadnoise/indicators.py +163 -0
  23. roadnoise-0.1.0/src/roadnoise/io.py +336 -0
  24. roadnoise-0.1.0/src/roadnoise/model.py +518 -0
  25. roadnoise-0.1.0/src/roadnoise/py.typed +0 -0
  26. roadnoise-0.1.0/src/roadnoise/tables.py +339 -0
  27. roadnoise-0.1.0/src/roadnoise.egg-info/PKG-INFO +273 -0
  28. roadnoise-0.1.0/src/roadnoise.egg-info/SOURCES.txt +36 -0
  29. roadnoise-0.1.0/src/roadnoise.egg-info/dependency_links.txt +1 -0
  30. roadnoise-0.1.0/src/roadnoise.egg-info/entry_points.txt +2 -0
  31. roadnoise-0.1.0/src/roadnoise.egg-info/requires.txt +14 -0
  32. roadnoise-0.1.0/src/roadnoise.egg-info/top_level.txt +1 -0
  33. roadnoise-0.1.0/tests/test_emission.py +315 -0
  34. roadnoise-0.1.0/tests/test_geo.py +59 -0
  35. roadnoise-0.1.0/tests/test_indicators_crtn.py +158 -0
  36. roadnoise-0.1.0/tests/test_io_cli.py +343 -0
  37. roadnoise-0.1.0/tests/test_model.py +301 -0
  38. roadnoise-0.1.0/tests/test_propagation.py +229 -0
@@ -0,0 +1,23 @@
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
+ - CNOSSOS-EU road traffic source model (Directive 2002/49/EC Annex II as amended by
10
+ Directive (EU) 2021/1226): Table F-1 (2021) coefficients for categories 1, 2, 3, 4a
11
+ and 4b, Table F-4 (2021) road surfaces, Table F-2 studded tyres and Table F-3
12
+ acceleration corrections (2015), temperature and gradient corrections, line-source
13
+ emission per metre, user CSV tables for coefficients (including category 5) and
14
+ road surfaces.
15
+ - ISO 9613-1 atmospheric absorption; CNOSSOS-EU and ISO 9613-2 flat-ground ground
16
+ effect; CNOSSOS-EU, ISO 9613-2 and Maekawa single-edge diffraction.
17
+ - Road segmentation, receiver grids, facade receivers, barriers and buildings as
18
+ diffracting edges, long-term levels and L_day / L_evening / L_night / L_den.
19
+ - END exposure statistics per 5 dB band.
20
+ - UK CRTN (1988) quick method.
21
+ - GeoJSON input, ESRI ASCII grid, GeoTIFF (optional rasterio) and GeoJSON output,
22
+ geopandas helpers, and the `roadnoise` command line (`emission`, `grid`,
23
+ `receivers`, `crtn`, `surfaces`).
@@ -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: "yusufeminoglu@gmail.com"
7
+ orcid: "https://orcid.org/0009-0005-6000-2934"
8
+ title: "roadnoise"
9
+ version: "0.1.0"
10
+ date-released: "2026-11-09"
11
+ license: "MIT"
12
+ repository-code: "https://github.com/YusufEminoglu/roadnoise_sdk"
13
+ url: "https://pypi.org/project/roadnoise/"
14
+ abstract: "Transparent, screening-level road traffic noise in Python: CNOSSOS-EU road source model (Directive (EU) 2015/996 as amended by Delegated Directive (EU) 2021/1226), ISO 9613 and CNOSSOS-EU flat-ground propagation, END indicators (Lden, Lnight) and exposure statistics, plus the UK CRTN quick method."
15
+ keywords:
16
+ - noise
17
+ - road traffic noise
18
+ - CNOSSOS-EU
19
+ - Environmental Noise Directive
20
+ - noise mapping
@@ -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/roadnoise/py.typed
7
+ recursive-include src/roadnoise *.py
8
+ recursive-include tests *.py
9
+ recursive-include examples *.py *.geojson
@@ -0,0 +1,273 @@
1
+ Metadata-Version: 2.4
2
+ Name: roadnoise
3
+ Version: 0.1.0
4
+ Summary: Transparent screening-level road traffic noise in Python: CNOSSOS-EU source model, ISO 9613 propagation, END indicators (Lden, Lnight) and exposure statistics.
5
+ Author-email: Yusuf Eminoğlu <yusufeminoglu@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/YusufEminoglu/roadnoise_sdk
8
+ Project-URL: Repository, https://github.com/YusufEminoglu/roadnoise_sdk
9
+ Project-URL: Issues, https://github.com/YusufEminoglu/roadnoise_sdk/issues
10
+ Project-URL: Changelog, https://github.com/YusufEminoglu/roadnoise_sdk/blob/main/CHANGELOG.md
11
+ Keywords: noise,road-traffic-noise,cnossos-eu,environmental-noise-directive,noise-mapping,lden,iso-9613,acoustics,crtn,gis,urban-planning
12
+ Classifier: Development Status :: 3 - Alpha
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: Topic :: Scientific/Engineering :: GIS
21
+ Classifier: Topic :: Scientific/Engineering :: Physics
22
+ Classifier: Intended Audience :: Science/Research
23
+ Classifier: Intended Audience :: Developers
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: numpy>=1.20.0
29
+ Provides-Extra: geo
30
+ Requires-Dist: geopandas>=0.12.0; extra == "geo"
31
+ Requires-Dist: shapely>=2.0.0; extra == "geo"
32
+ Provides-Extra: raster
33
+ Requires-Dist: rasterio>=1.3.0; extra == "raster"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
37
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
38
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ # roadnoise
42
+
43
+ Transparent, **screening-level** road traffic noise in Python for environmental-noise
44
+ consultants, planners and researchers working in the context of the EU Environmental
45
+ Noise Directive (END, Directive 2002/49/EC).
46
+
47
+ * **CNOSSOS-EU road source model** – Directive 2002/49/EC Annex II as introduced by
48
+ Commission Directive (EU) 2015/996 and amended by Commission Delegated Directive
49
+ (EU) 2021/1226: vehicle categories 1, 2, 3, 4a, 4b (5 reserved), rolling and
50
+ propulsion noise per octave band 63 Hz – 8 kHz, speed laws, road-surface,
51
+ studded-tyre, temperature, gradient and acceleration corrections, line-source
52
+ emission per metre.
53
+ * **Propagation (screening)** – geometric divergence, ISO 9613-1 atmospheric
54
+ absorption at any temperature / humidity, flat-ground ground effect (CNOSSOS-EU
55
+ homogeneous + favourable, or ISO 9613-2 general method), single-edge diffraction
56
+ (CNOSSOS-EU Δdif with ground terms, ISO 9613-2 D<sub>z</sub>, or Maekawa), optional
57
+ +3 dB facade reflection.
58
+ * **Receivers** – segmentation of road lines into point sources, regular grids,
59
+ facade receivers.
60
+ * **Indicators** – L<sub>day</sub>, L<sub>evening</sub>, L<sub>night</sub>,
61
+ L<sub>den</sub> (5/10 dB penalties, 12/4/8 h or Member-State variants) and END
62
+ exposure statistics (people per 5 dB band, optional rounding to hundreds).
63
+ * **CRTN** – the UK *Calculation of Road Traffic Noise* (1988) as an independent
64
+ quick check.
65
+ * **I/O** – GeoJSON in, ESRI ASCII grid / GeoJSON out; GeoTIFF with the `raster`
66
+ extra; geopandas helpers with the `geo` extra. Only `numpy` is required.
67
+
68
+ > **Scope.** `roadnoise` is a screening tool. It is **not** a certified or
69
+ > conformance-tested CNOSSOS-EU implementation and must not be used on its own for
70
+ > strategic noise maps reported under the END. See
71
+ > [Differences to full CNOSSOS-EU](#differences-to-full-cnossos-eu).
72
+
73
+ ## Installation
74
+
75
+ ```bash
76
+ pip install roadnoise # numpy only
77
+ pip install "roadnoise[raster]" # + rasterio for GeoTIFF output
78
+ pip install "roadnoise[geo]" # + geopandas/shapely helpers
79
+ ```
80
+
81
+ Python 3.9 – 3.13.
82
+
83
+ ## Quick start
84
+
85
+ ```python
86
+ import roadnoise as rn
87
+
88
+ # Emission per metre of 1104 cars/h + 96 heavy vehicles/h at 50 km/h
89
+ lw = rn.line_sound_power({"1": 1104, "3": 96}, 50) # dB re 1 pW/m, 8 bands
90
+ print(rn.a_weighted(lw)) # ~84.1 dB(A)
91
+
92
+ # A road from AADT, a barrier, a building, facade receivers
93
+ road = rn.Road.from_aadt([(0, 0), (400, 0)], aadt=24000, speed=50, heavy=0.08)
94
+ barrier = rn.Barrier([(50, 12), (350, 12)], height=3.0)
95
+ building = rn.Building([(150, 40), (250, 40), (250, 60), (150, 60)], height=12.0)
96
+ pts, _ = rn.facade_receivers(building.footprint, spacing=5.0)
97
+
98
+ res = rn.compute_levels([road], pts, receiver_height=4.0,
99
+ barriers=[barrier], buildings=[building])
100
+ res.lden, res.lnight # dB(A) per receiver
101
+
102
+ grid = rn.compute_grid([road], cell=10.0, margin=100.0, barriers=[barrier])
103
+ grid.levels["lden"] # (nrows, ncols), north-up
104
+
105
+ table = rn.exposure_statistics(res.lden, population=3.0, round_to=100)
106
+ print(table.to_text("People per Lden band"))
107
+ ```
108
+
109
+ See `examples/quickstart.py` for a complete script.
110
+
111
+ ## Command line
112
+
113
+ ```bash
114
+ roadnoise emission --flow 1200 --speed 50 --heavy 0.08
115
+ roadnoise emission --flow 1200 --speed 90 --heavy 0.15 --surface "2-layer ZOAB" --json
116
+ roadnoise grid roads.geojson --cell 10 -o lden.asc # ESRI ASCII grid
117
+ roadnoise grid roads.geojson --cell 10 -o lden.tif # needs roadnoise[raster]
118
+ roadnoise grid roads.geojson --cell 10 -o map.asc --indicator all --barriers walls.geojson
119
+ roadnoise receivers roads.geojson dwellings.geojson -o out.geojson \
120
+ --population-field population --round-to 100
121
+ roadnoise crtn --flow 20000 --speed 75 --heavy-pct 10 --distance 20
122
+ roadnoise surfaces
123
+ ```
124
+
125
+ Common propagation options: `--method cnossos|iso9613`, `--ground G`,
126
+ `--temperature`, `--humidity`, `--max-distance`, `--segment`, `--facade`,
127
+ `--buildings`, `--barriers`, `--assume-projected`.
128
+
129
+ ### GeoJSON input
130
+
131
+ Coordinates must be in a projected CRS in metres. Files without a legacy `crs`
132
+ member whose coordinates look like longitude/latitude are rejected (override with
133
+ `--assume-projected`). Road properties (case-insensitive):
134
+
135
+ | property | meaning |
136
+ |---|---|
137
+ | `AADT` | annual average daily traffic (veh/day); or give `q_day`, `q_evening`, `q_night` (veh/h) |
138
+ | `heavy` / `heavy_pct` | heavy-vehicle fraction (0–1) / percentage |
139
+ | `speed`, `speed_heavy` | km/h (default 50; heavy defaults to `speed`) |
140
+ | `medium_share` | share of heavy vehicles that are category 2 (default 0 → all category 3) |
141
+ | `two_wheeler` | motorcycle fraction (category 4b) |
142
+ | `surface` | Table F-4 surface name (`roadnoise surfaces`) |
143
+ | `gradient` | % (applied half uphill, half downhill) |
144
+ | `temperature`, `studded_ratio`, `studded_months` | emission corrections |
145
+
146
+ Barriers: LineStrings with `height` (m). Buildings: Polygons with `height` (m).
147
+ Receivers: Points with optional `height` (m) or a Z coordinate.
148
+
149
+ ## What is implemented, and how it was checked
150
+
151
+ ### Coefficient tables (all shipped, none are placeholders)
152
+
153
+ | table | source | verification |
154
+ |---|---|---|
155
+ | F-1 A<sub>R</sub>, B<sub>R</sub>, A<sub>P</sub>, B<sub>P</sub> (cat. 1, 2, 3, 4a, 4b) | Directive (EU) 2021/1226, Annex point (19)(a) | 160 values machine-compared with the EUR-Lex text of the directive |
156
+ | F-4 road surfaces α<sub>i,m</sub>, β<sub>m</sub>, validity speeds (15 surfaces) | Directive (EU) 2021/1226, Annex point (19)(b) | 540 values machine-compared with the EUR-Lex text |
157
+ | F-2 studded tyres a<sub>i</sub>, b<sub>i</sub> | Directive (EU) 2015/996, Appendix F (not replaced in 2021) | transcribed from the Official Journal PDF and checked by hand |
158
+ | F-3 acceleration C<sub>R</sub>, C<sub>P</sub> | Directive (EU) 2015/996, Appendix F (not replaced in 2021) | transcribed from the Official Journal PDF and checked by hand |
159
+ | K<sub>m</sub> temperature coefficients | Annex II §2.2.3, formula (2.2.10) | from the text |
160
+ | A-weighting AWC<sub>f,i</sub> | Directive (EU) 2021/1226, amendment to §2.5.5 | from the text |
161
+
162
+ Note that the 2021 Table F-1 differs from the 2015 one (e.g. category 1
163
+ A<sub>R</sub> at 1 kHz is 100.1 dB, not 97.3 dB) – older tools may still use the
164
+ 2015 numbers. Category 5 has no coefficients in either directive: it raises an error
165
+ unless you supply coefficients with `load_coefficients_csv()` (format:
166
+ `category,coefficient,63,…,8000` with rows `AR, BR, AP, BP`; values are range-checked).
167
+ National road surfaces can be loaded with `load_surface_csv()`
168
+ (`name,category,63,…,8000,beta[,v_min,v_max]`).
169
+
170
+ ### Emission formulas (Annex II §2.2)
171
+
172
+ (2.2.1) line source, (2.2.2)–(2.2.3) total power and the 20 km/h floor, (2.2.4)–(2.2.5)
173
+ rolling noise, (2.2.6)–(2.2.9) studded tyres, (2.2.10) temperature, (2.2.11)–(2.2.12)
174
+ propulsion, (2.2.13)–(2.2.16) gradient, (2.2.17)–(2.2.18) acceleration near junctions,
175
+ (2.2.19)–(2.2.20) road surface (with a warning outside the validity speed range).
176
+
177
+ ### Propagation
178
+
179
+ * A<sub>div</sub> = 20 lg d + 11 (2.5.12); A<sub>atm</sub> = α d / 1000 (2.5.13).
180
+ * α from the ISO 9613-1 pure-tone formulae at exact band mid-frequencies. The
181
+ implementation reproduces **ISO 9613-2 Table 2** (e.g. 20 °C / 70 %:
182
+ 0.1, 0.3, 1.1, 2.8, 5.0, 9.0, 22.9, 76.6 dB/km) at all six tabulated
183
+ temperature/humidity pairs to the printed precision (unit-tested).
184
+ * CNOSSOS-EU ground effect (2.5.14)–(2.5.20) for homogeneous and favourable
185
+ conditions with G<sub>s</sub> = 0 for the road platform; long-term level (2.5.9)
186
+ with an occurrence p per period.
187
+ * CNOSSOS-EU diffraction (2.5.21) with C″ = 1 (single edge), Δ<sub>ground</sub>(S,O)
188
+ and Δ<sub>ground</sub>(O,R) as amended in 2021, curved-ray path differences in
189
+ favourable conditions (2.5.24)–(2.5.27), and the 2021 diffraction criterion
190
+ (δ > −λ/20 and the Rayleigh criterion δ > λ/4 − δ*).
191
+ * ISO 9613-2: general ground method (Table 3), alternative A-weighted method
192
+ (eq. 10), D<sub>z</sub> with K<sub>met</sub> (eq. 14, 18) capped at 20 dB,
193
+ A<sub>bar</sub> = D<sub>z</sub> − A<sub>gr</sub> ≥ 0, optional C<sub>met</sub>.
194
+ * Maekawa barrier attenuation in the Kurze–Anderson closed form.
195
+
196
+ The 2015/2021 Official Journal prints the ground-effect and diffraction equations as
197
+ images. They were implemented from the published formula structure (term names,
198
+ bounds, special cases quoted in the text) together with the standard CNOSSOS-EU
199
+ formulation, and are unit-tested against independent hand calculations of those
200
+ equations – but they could not be compared character-by-character with the OJ text.
201
+
202
+ ### CRTN (UK, 1988)
203
+
204
+ Basic noise level (18 h and 1 h), speed and % heavy correction, gradient (+0.3 dB/%),
205
+ road surface, distance, ground cover, angle of view and +2.5 dB facade. **Not
206
+ implemented:** the barrier chart, the gradient speed reduction, the low-flow
207
+ correction and conversions from L<sub>A10</sub> to L<sub>den</sub>. CRTN predicts
208
+ L<sub>A10,18h</sub>, not an END indicator; use it as a cross-check only. The Nordic
209
+ prediction methods are **not** included.
210
+
211
+ ### Validation in the test suite
212
+
213
+ ISO 9613-2 Table 2 absorption values; hand calculations of every emission correction
214
+ and of the ground and diffraction equations; energetic summation (60 ⊕ 60 =
215
+ 63.01 dB, ten equal sources +10 dB); L<sub>den</sub> (equal levels give L + 6.39 dB,
216
+ Member-State period variants); flow doubling +3.01 dB; a long, finely segmented road
217
+ over hard ground matches the analytic line-source integral L = L<sub>W</sub>′ −
218
+ 10 lg(4d) + 3 within 0.05 dB; symmetry and monotonic decay with distance; higher
219
+ barriers shield more; CRTN reference point (75 km/h, 0 % heavy gives a 0 dB speed
220
+ correction; 20 000 veh/18 h gives a basic noise level of 72.1 dB(A)). Coverage is
221
+ above 95 %.
222
+
223
+ ## Differences to full CNOSSOS-EU
224
+
225
+ `roadnoise` deliberately simplifies the propagation part. Results can differ
226
+ significantly from a conformant CNOSSOS-EU implementation, in particular in
227
+ built-up areas:
228
+
229
+ * **Flat ground only.** No digital terrain model, no mean ground plane per path, no
230
+ equivalent heights, no embankments or cuttings; ground factor G is uniform.
231
+ * **No path finding.** One straight vertical propagation plane per source–receiver
232
+ pair; no "rubber-band" convex hull and **no multiple diffraction**: only the single
233
+ most effective edge (largest path difference) is used, so thick buildings and
234
+ double barriers are under-estimated.
235
+ * **No lateral (vertical-edge) diffraction** and **no reflections** from facades or
236
+ barriers (image sources). The optional +3 dB facade term is a flat correction.
237
+ * Barriers and building walls are thin edges at their top height; absorption of
238
+ barrier surfaces is ignored.
239
+ * The favourable-condition occurrences p (defaults day 0.5, evening 0.75, night 1.0)
240
+ are screening assumptions, not directional meteorological statistics.
241
+ * The AADT → hourly-flow split (default 70 % day / 20 % evening / 10 % night) and the
242
+ heavy-vehicle split (default all category 3) are **placeholder assumptions**;
243
+ provide `q_day`/`q_evening`/`q_night`, `medium_share` and counts where available.
244
+ * No statistical urban approach, no population/dwelling assignment rules of Annex II
245
+ §2.8 beyond the facade receiver helper and the per-band exposure table.
246
+ * Sound power is computed once per road and per END period; there is no hourly
247
+ traffic model.
248
+
249
+ ## Citation
250
+
251
+ If you use `roadnoise`, please cite it via `CITATION.cff`. Key references:
252
+
253
+ * Directive 2002/49/EC of the European Parliament and of the Council relating to the
254
+ assessment and management of environmental noise (OJ L 189, 18.7.2002).
255
+ * Commission Directive (EU) 2015/996 establishing common noise assessment methods
256
+ (OJ L 168, 1.7.2015).
257
+ * Commission Delegated Directive (EU) 2021/1226 amending, for the purposes of
258
+ adapting to scientific and technical progress, Annex II to Directive 2002/49/EC as
259
+ regards common noise assessment methods (OJ L 269, 28.7.2021).
260
+ * Kephalopoulos S., Paviotti M., Anfosso-Lédée F. (2012). *Common noise assessment
261
+ methods in Europe (CNOSSOS-EU)*. JRC Reference Report EUR 25379 EN.
262
+ * ISO 9613-1:1993 *Acoustics – Attenuation of sound during propagation outdoors –
263
+ Part 1: Calculation of the absorption of sound by the atmosphere*.
264
+ * ISO 9613-2:1996 *Part 2: General method of calculation*.
265
+ * Maekawa Z. (1968). Noise reduction by screens. *Applied Acoustics* 1(3), 157–173;
266
+ Kurze U. J., Anderson G. S. (1971). Sound attenuation by barriers. *Applied
267
+ Acoustics* 4(1), 35–53.
268
+ * Department of Transport and Welsh Office (1988). *Calculation of Road Traffic
269
+ Noise*. HMSO, London.
270
+
271
+ ## License
272
+
273
+ MIT © Yusuf Eminoğlu
@@ -0,0 +1,233 @@
1
+ # roadnoise
2
+
3
+ Transparent, **screening-level** road traffic noise in Python for environmental-noise
4
+ consultants, planners and researchers working in the context of the EU Environmental
5
+ Noise Directive (END, Directive 2002/49/EC).
6
+
7
+ * **CNOSSOS-EU road source model** – Directive 2002/49/EC Annex II as introduced by
8
+ Commission Directive (EU) 2015/996 and amended by Commission Delegated Directive
9
+ (EU) 2021/1226: vehicle categories 1, 2, 3, 4a, 4b (5 reserved), rolling and
10
+ propulsion noise per octave band 63 Hz – 8 kHz, speed laws, road-surface,
11
+ studded-tyre, temperature, gradient and acceleration corrections, line-source
12
+ emission per metre.
13
+ * **Propagation (screening)** – geometric divergence, ISO 9613-1 atmospheric
14
+ absorption at any temperature / humidity, flat-ground ground effect (CNOSSOS-EU
15
+ homogeneous + favourable, or ISO 9613-2 general method), single-edge diffraction
16
+ (CNOSSOS-EU Δdif with ground terms, ISO 9613-2 D<sub>z</sub>, or Maekawa), optional
17
+ +3 dB facade reflection.
18
+ * **Receivers** – segmentation of road lines into point sources, regular grids,
19
+ facade receivers.
20
+ * **Indicators** – L<sub>day</sub>, L<sub>evening</sub>, L<sub>night</sub>,
21
+ L<sub>den</sub> (5/10 dB penalties, 12/4/8 h or Member-State variants) and END
22
+ exposure statistics (people per 5 dB band, optional rounding to hundreds).
23
+ * **CRTN** – the UK *Calculation of Road Traffic Noise* (1988) as an independent
24
+ quick check.
25
+ * **I/O** – GeoJSON in, ESRI ASCII grid / GeoJSON out; GeoTIFF with the `raster`
26
+ extra; geopandas helpers with the `geo` extra. Only `numpy` is required.
27
+
28
+ > **Scope.** `roadnoise` is a screening tool. It is **not** a certified or
29
+ > conformance-tested CNOSSOS-EU implementation and must not be used on its own for
30
+ > strategic noise maps reported under the END. See
31
+ > [Differences to full CNOSSOS-EU](#differences-to-full-cnossos-eu).
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install roadnoise # numpy only
37
+ pip install "roadnoise[raster]" # + rasterio for GeoTIFF output
38
+ pip install "roadnoise[geo]" # + geopandas/shapely helpers
39
+ ```
40
+
41
+ Python 3.9 – 3.13.
42
+
43
+ ## Quick start
44
+
45
+ ```python
46
+ import roadnoise as rn
47
+
48
+ # Emission per metre of 1104 cars/h + 96 heavy vehicles/h at 50 km/h
49
+ lw = rn.line_sound_power({"1": 1104, "3": 96}, 50) # dB re 1 pW/m, 8 bands
50
+ print(rn.a_weighted(lw)) # ~84.1 dB(A)
51
+
52
+ # A road from AADT, a barrier, a building, facade receivers
53
+ road = rn.Road.from_aadt([(0, 0), (400, 0)], aadt=24000, speed=50, heavy=0.08)
54
+ barrier = rn.Barrier([(50, 12), (350, 12)], height=3.0)
55
+ building = rn.Building([(150, 40), (250, 40), (250, 60), (150, 60)], height=12.0)
56
+ pts, _ = rn.facade_receivers(building.footprint, spacing=5.0)
57
+
58
+ res = rn.compute_levels([road], pts, receiver_height=4.0,
59
+ barriers=[barrier], buildings=[building])
60
+ res.lden, res.lnight # dB(A) per receiver
61
+
62
+ grid = rn.compute_grid([road], cell=10.0, margin=100.0, barriers=[barrier])
63
+ grid.levels["lden"] # (nrows, ncols), north-up
64
+
65
+ table = rn.exposure_statistics(res.lden, population=3.0, round_to=100)
66
+ print(table.to_text("People per Lden band"))
67
+ ```
68
+
69
+ See `examples/quickstart.py` for a complete script.
70
+
71
+ ## Command line
72
+
73
+ ```bash
74
+ roadnoise emission --flow 1200 --speed 50 --heavy 0.08
75
+ roadnoise emission --flow 1200 --speed 90 --heavy 0.15 --surface "2-layer ZOAB" --json
76
+ roadnoise grid roads.geojson --cell 10 -o lden.asc # ESRI ASCII grid
77
+ roadnoise grid roads.geojson --cell 10 -o lden.tif # needs roadnoise[raster]
78
+ roadnoise grid roads.geojson --cell 10 -o map.asc --indicator all --barriers walls.geojson
79
+ roadnoise receivers roads.geojson dwellings.geojson -o out.geojson \
80
+ --population-field population --round-to 100
81
+ roadnoise crtn --flow 20000 --speed 75 --heavy-pct 10 --distance 20
82
+ roadnoise surfaces
83
+ ```
84
+
85
+ Common propagation options: `--method cnossos|iso9613`, `--ground G`,
86
+ `--temperature`, `--humidity`, `--max-distance`, `--segment`, `--facade`,
87
+ `--buildings`, `--barriers`, `--assume-projected`.
88
+
89
+ ### GeoJSON input
90
+
91
+ Coordinates must be in a projected CRS in metres. Files without a legacy `crs`
92
+ member whose coordinates look like longitude/latitude are rejected (override with
93
+ `--assume-projected`). Road properties (case-insensitive):
94
+
95
+ | property | meaning |
96
+ |---|---|
97
+ | `AADT` | annual average daily traffic (veh/day); or give `q_day`, `q_evening`, `q_night` (veh/h) |
98
+ | `heavy` / `heavy_pct` | heavy-vehicle fraction (0–1) / percentage |
99
+ | `speed`, `speed_heavy` | km/h (default 50; heavy defaults to `speed`) |
100
+ | `medium_share` | share of heavy vehicles that are category 2 (default 0 → all category 3) |
101
+ | `two_wheeler` | motorcycle fraction (category 4b) |
102
+ | `surface` | Table F-4 surface name (`roadnoise surfaces`) |
103
+ | `gradient` | % (applied half uphill, half downhill) |
104
+ | `temperature`, `studded_ratio`, `studded_months` | emission corrections |
105
+
106
+ Barriers: LineStrings with `height` (m). Buildings: Polygons with `height` (m).
107
+ Receivers: Points with optional `height` (m) or a Z coordinate.
108
+
109
+ ## What is implemented, and how it was checked
110
+
111
+ ### Coefficient tables (all shipped, none are placeholders)
112
+
113
+ | table | source | verification |
114
+ |---|---|---|
115
+ | F-1 A<sub>R</sub>, B<sub>R</sub>, A<sub>P</sub>, B<sub>P</sub> (cat. 1, 2, 3, 4a, 4b) | Directive (EU) 2021/1226, Annex point (19)(a) | 160 values machine-compared with the EUR-Lex text of the directive |
116
+ | F-4 road surfaces α<sub>i,m</sub>, β<sub>m</sub>, validity speeds (15 surfaces) | Directive (EU) 2021/1226, Annex point (19)(b) | 540 values machine-compared with the EUR-Lex text |
117
+ | F-2 studded tyres a<sub>i</sub>, b<sub>i</sub> | Directive (EU) 2015/996, Appendix F (not replaced in 2021) | transcribed from the Official Journal PDF and checked by hand |
118
+ | F-3 acceleration C<sub>R</sub>, C<sub>P</sub> | Directive (EU) 2015/996, Appendix F (not replaced in 2021) | transcribed from the Official Journal PDF and checked by hand |
119
+ | K<sub>m</sub> temperature coefficients | Annex II §2.2.3, formula (2.2.10) | from the text |
120
+ | A-weighting AWC<sub>f,i</sub> | Directive (EU) 2021/1226, amendment to §2.5.5 | from the text |
121
+
122
+ Note that the 2021 Table F-1 differs from the 2015 one (e.g. category 1
123
+ A<sub>R</sub> at 1 kHz is 100.1 dB, not 97.3 dB) – older tools may still use the
124
+ 2015 numbers. Category 5 has no coefficients in either directive: it raises an error
125
+ unless you supply coefficients with `load_coefficients_csv()` (format:
126
+ `category,coefficient,63,…,8000` with rows `AR, BR, AP, BP`; values are range-checked).
127
+ National road surfaces can be loaded with `load_surface_csv()`
128
+ (`name,category,63,…,8000,beta[,v_min,v_max]`).
129
+
130
+ ### Emission formulas (Annex II §2.2)
131
+
132
+ (2.2.1) line source, (2.2.2)–(2.2.3) total power and the 20 km/h floor, (2.2.4)–(2.2.5)
133
+ rolling noise, (2.2.6)–(2.2.9) studded tyres, (2.2.10) temperature, (2.2.11)–(2.2.12)
134
+ propulsion, (2.2.13)–(2.2.16) gradient, (2.2.17)–(2.2.18) acceleration near junctions,
135
+ (2.2.19)–(2.2.20) road surface (with a warning outside the validity speed range).
136
+
137
+ ### Propagation
138
+
139
+ * A<sub>div</sub> = 20 lg d + 11 (2.5.12); A<sub>atm</sub> = α d / 1000 (2.5.13).
140
+ * α from the ISO 9613-1 pure-tone formulae at exact band mid-frequencies. The
141
+ implementation reproduces **ISO 9613-2 Table 2** (e.g. 20 °C / 70 %:
142
+ 0.1, 0.3, 1.1, 2.8, 5.0, 9.0, 22.9, 76.6 dB/km) at all six tabulated
143
+ temperature/humidity pairs to the printed precision (unit-tested).
144
+ * CNOSSOS-EU ground effect (2.5.14)–(2.5.20) for homogeneous and favourable
145
+ conditions with G<sub>s</sub> = 0 for the road platform; long-term level (2.5.9)
146
+ with an occurrence p per period.
147
+ * CNOSSOS-EU diffraction (2.5.21) with C″ = 1 (single edge), Δ<sub>ground</sub>(S,O)
148
+ and Δ<sub>ground</sub>(O,R) as amended in 2021, curved-ray path differences in
149
+ favourable conditions (2.5.24)–(2.5.27), and the 2021 diffraction criterion
150
+ (δ > −λ/20 and the Rayleigh criterion δ > λ/4 − δ*).
151
+ * ISO 9613-2: general ground method (Table 3), alternative A-weighted method
152
+ (eq. 10), D<sub>z</sub> with K<sub>met</sub> (eq. 14, 18) capped at 20 dB,
153
+ A<sub>bar</sub> = D<sub>z</sub> − A<sub>gr</sub> ≥ 0, optional C<sub>met</sub>.
154
+ * Maekawa barrier attenuation in the Kurze–Anderson closed form.
155
+
156
+ The 2015/2021 Official Journal prints the ground-effect and diffraction equations as
157
+ images. They were implemented from the published formula structure (term names,
158
+ bounds, special cases quoted in the text) together with the standard CNOSSOS-EU
159
+ formulation, and are unit-tested against independent hand calculations of those
160
+ equations – but they could not be compared character-by-character with the OJ text.
161
+
162
+ ### CRTN (UK, 1988)
163
+
164
+ Basic noise level (18 h and 1 h), speed and % heavy correction, gradient (+0.3 dB/%),
165
+ road surface, distance, ground cover, angle of view and +2.5 dB facade. **Not
166
+ implemented:** the barrier chart, the gradient speed reduction, the low-flow
167
+ correction and conversions from L<sub>A10</sub> to L<sub>den</sub>. CRTN predicts
168
+ L<sub>A10,18h</sub>, not an END indicator; use it as a cross-check only. The Nordic
169
+ prediction methods are **not** included.
170
+
171
+ ### Validation in the test suite
172
+
173
+ ISO 9613-2 Table 2 absorption values; hand calculations of every emission correction
174
+ and of the ground and diffraction equations; energetic summation (60 ⊕ 60 =
175
+ 63.01 dB, ten equal sources +10 dB); L<sub>den</sub> (equal levels give L + 6.39 dB,
176
+ Member-State period variants); flow doubling +3.01 dB; a long, finely segmented road
177
+ over hard ground matches the analytic line-source integral L = L<sub>W</sub>′ −
178
+ 10 lg(4d) + 3 within 0.05 dB; symmetry and monotonic decay with distance; higher
179
+ barriers shield more; CRTN reference point (75 km/h, 0 % heavy gives a 0 dB speed
180
+ correction; 20 000 veh/18 h gives a basic noise level of 72.1 dB(A)). Coverage is
181
+ above 95 %.
182
+
183
+ ## Differences to full CNOSSOS-EU
184
+
185
+ `roadnoise` deliberately simplifies the propagation part. Results can differ
186
+ significantly from a conformant CNOSSOS-EU implementation, in particular in
187
+ built-up areas:
188
+
189
+ * **Flat ground only.** No digital terrain model, no mean ground plane per path, no
190
+ equivalent heights, no embankments or cuttings; ground factor G is uniform.
191
+ * **No path finding.** One straight vertical propagation plane per source–receiver
192
+ pair; no "rubber-band" convex hull and **no multiple diffraction**: only the single
193
+ most effective edge (largest path difference) is used, so thick buildings and
194
+ double barriers are under-estimated.
195
+ * **No lateral (vertical-edge) diffraction** and **no reflections** from facades or
196
+ barriers (image sources). The optional +3 dB facade term is a flat correction.
197
+ * Barriers and building walls are thin edges at their top height; absorption of
198
+ barrier surfaces is ignored.
199
+ * The favourable-condition occurrences p (defaults day 0.5, evening 0.75, night 1.0)
200
+ are screening assumptions, not directional meteorological statistics.
201
+ * The AADT → hourly-flow split (default 70 % day / 20 % evening / 10 % night) and the
202
+ heavy-vehicle split (default all category 3) are **placeholder assumptions**;
203
+ provide `q_day`/`q_evening`/`q_night`, `medium_share` and counts where available.
204
+ * No statistical urban approach, no population/dwelling assignment rules of Annex II
205
+ §2.8 beyond the facade receiver helper and the per-band exposure table.
206
+ * Sound power is computed once per road and per END period; there is no hourly
207
+ traffic model.
208
+
209
+ ## Citation
210
+
211
+ If you use `roadnoise`, please cite it via `CITATION.cff`. Key references:
212
+
213
+ * Directive 2002/49/EC of the European Parliament and of the Council relating to the
214
+ assessment and management of environmental noise (OJ L 189, 18.7.2002).
215
+ * Commission Directive (EU) 2015/996 establishing common noise assessment methods
216
+ (OJ L 168, 1.7.2015).
217
+ * Commission Delegated Directive (EU) 2021/1226 amending, for the purposes of
218
+ adapting to scientific and technical progress, Annex II to Directive 2002/49/EC as
219
+ regards common noise assessment methods (OJ L 269, 28.7.2021).
220
+ * Kephalopoulos S., Paviotti M., Anfosso-Lédée F. (2012). *Common noise assessment
221
+ methods in Europe (CNOSSOS-EU)*. JRC Reference Report EUR 25379 EN.
222
+ * ISO 9613-1:1993 *Acoustics – Attenuation of sound during propagation outdoors –
223
+ Part 1: Calculation of the absorption of sound by the atmosphere*.
224
+ * ISO 9613-2:1996 *Part 2: General method of calculation*.
225
+ * Maekawa Z. (1968). Noise reduction by screens. *Applied Acoustics* 1(3), 157–173;
226
+ Kurze U. J., Anderson G. S. (1971). Sound attenuation by barriers. *Applied
227
+ Acoustics* 4(1), 35–53.
228
+ * Department of Transport and Welsh Office (1988). *Calculation of Road Traffic
229
+ Noise*. HMSO, London.
230
+
231
+ ## License
232
+
233
+ MIT © Yusuf Eminoğlu
@@ -0,0 +1,35 @@
1
+ """Minimal end-to-end example: emission, receivers, a noise map and exposure.
2
+
3
+ Run from the repository root: python examples/quickstart.py
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import numpy as np
9
+
10
+ import roadnoise as rn
11
+
12
+ # 1. Emission of a traffic flow (CNOSSOS-EU, Table F-1 of Directive (EU) 2021/1226).
13
+ lw = rn.line_sound_power({"1": 1104, "3": 96}, 50)
14
+ print("L_W' per band:", np.round(lw, 1), f"-> {float(rn.a_weighted(lw)):.1f} dB(A) re 1 pW/m")
15
+
16
+ # 2. Two roads from AADT (projected coordinates in metres).
17
+ main = rn.Road.from_aadt([(0, 0), (400, 0)], aadt=24000, speed=50, heavy=0.08)
18
+ side = rn.Road.from_aadt([(200, -150), (200, -10)], aadt=4000, speed=30, heavy=0.02)
19
+ barrier = rn.Barrier([(50, 12), (350, 12)], height=3.0)
20
+ building = rn.Building([(150, 40), (250, 40), (250, 60), (150, 60)], height=12.0)
21
+
22
+ # 3. Facade receivers of the building, 4 m high, 0.1 m in front of each wall.
23
+ pts, _ = rn.facade_receivers(building.footprint, spacing=5.0)
24
+ res = rn.compute_levels([main, side], pts, 4.0, barriers=[barrier], buildings=[building])
25
+ print(f"Facade L_den: {np.nanmin(res.lden):.1f} - {np.nanmax(res.lden):.1f} dB(A)")
26
+
27
+ # 4. Exposure: 60 residents in the building, assigned to the most exposed facade.
28
+ table = rn.exposure_statistics([res.lden.max()], [60])
29
+ print(table.to_text("Residents per L_den band"))
30
+
31
+ # 5. A 10 m noise map.
32
+ grid = rn.compute_grid(
33
+ [main, side], cell=10.0, margin=60.0, barriers=[barrier], buildings=[building]
34
+ )
35
+ print("grid shape", grid.levels["lden"].shape)