Skip to content

Noise Simulation

Noise Simulation models how sound propagates from one or more source points, taking into account obstacles, vegetation, and environmental conditions.
The outputs are noise exposure maps useful for urban planning and environmental impact assessments.


The module provides two methods:

Full Wave-Based Simulation

Performs detailed noise modeling using full wave-based calculations.

objectnat.simulate_noise(source_points, obstacles, source_noise_db=None, geometric_mean_freq_hz=None, **kwargs)

Simulates noise propagation from a set of source points considering obstacles, trees, and environmental factors.

Parameters:

Name Type Description Default
source_points GeoDataFrame

A GeoDataFrame with one or more point geometries representing noise sources. Optionally, it can include 'source_noise_db' and 'geometric_mean_freq_hz' columns for per-point simulation.

required
obstacles GeoDataFrame

A GeoDataFrame representing obstacles in the environment. If a column with sound absorption coefficients is present, its name should be provided in the absorb_ratio_column argument. Missing values will be filled with the standart_absorb_ratio.

required
source_noise_db (float

Default noise level (dB) to use if not specified per-point. Decibels are logarithmic units used to measure sound intensity. A value of 20 dB represents a barely audible whisper, while 140 dB is comparable to the noise of jet engines.

None
geometric_mean_freq_hz float

Default frequency (Hz) to use if not specified per-point. This parameter influences the sound wave's propagation and scattering in the presence of trees. Lower frequencies travel longer distances than higher frequencies. It's recommended to use values between 63 Hz and 8000 Hz; values outside this range will be clamped to the nearest boundary for the sound absorption coefficient calculation.

None
Optional kwargs
  • absorb_ratio_column (str, optional): The name of the column in the obstacles GeoDataFrame that contains the sound absorption coefficients for each obstacle. Default is None. If not specified, all obstacles will have the standart_absorb_ratio.
  • standart_absorb_ratio (float, optional): The default sound absorption coefficient to use for obstacles without specified values in the absorb_ratio_column. Default is 0.05, which is a typical value for concrete walls.
  • trees (gpd.GeoDataFrame, optional): A GeoDataFrame containing trees or dense vegetation along the sound wave's path. Trees will scatter and absorb sound waves.
  • tree_resolution (int, optional): A resolution parameter for simulating tree interactions with sound waves. Recommended values are between 2 and 16, with higher values providing more accurate simulation results.
  • air_temperature (float, optional): The air temperature in degrees Celsius. The recommended range is from 0 to 30 degrees Celsius, as temperatures outside this range will be clipped. Temperature affects the sound propagation in the air.
  • target_noise_db (float, optional): The target noise level (in dB) for the simulation. Default is 40 dB. Lower values may not be relevant for further analysis, as they are near the threshold of human hearing.
  • db_sim_step (float, optional): The step size in decibels for the noise simulation. Default is 1. For more precise analysis, this can be adjusted. If the difference between source_noise_db and target_noise_db is not divisible by the step size, the function will raise an error.
  • reflection_n (int, optional): The maximum number of reflections (bounces) to simulate for each sound wave. Recommended values are between 1 and 3. Larger values will result in longer simulation times.
  • dead_area_r (float, optional): A debugging parameter that defines the radius of the "dead zone" for reflections. Points within this area will not generate reflections. This is useful to prevent the algorithm from getting stuck in corners or along building walls.
  • use_parallel (bool, optional): Whether to use ProcessPool for task distribution or not. Default is True.

Returns: (gpd.GeoDataFrame): A GeoDataFrame containing the noise simulation results, including noise levels and geometries of the affected areas. Each point's simulation results will be merged into a single GeoDataFrame.

Source code in src\objectnat\methods\noise\noise_simulation.py
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def simulate_noise(
    source_points: gpd.GeoDataFrame,
    obstacles: gpd.GeoDataFrame,
    source_noise_db: float = None,
    geometric_mean_freq_hz: float = None,
    **kwargs,
):
    """
    Simulates noise propagation from a set of source points considering obstacles, trees, and environmental factors.

    Parameters:
        source_points (gpd.GeoDataFrame):
            A GeoDataFrame with one or more point geometries representing noise sources.
            Optionally, it can include 'source_noise_db' and 'geometric_mean_freq_hz' columns for per-point simulation.
        obstacles (gpd.GeoDataFrame):
            A GeoDataFrame representing obstacles in the environment. If a column with sound absorption coefficients
            is present, its name should be provided in the `absorb_ratio_column` argument.
            Missing values will be filled with the `standart_absorb_ratio`.
        source_noise_db  (float, optional):
            Default noise level (dB) to use if not specified per-point. Decibels are logarithmic units used to measure
            sound intensity. A value of 20 dB represents a barely audible whisper, while 140 dB is comparable to the
            noise of jet engines.
        geometric_mean_freq_hz (float, optional):
            Default frequency (Hz) to use if not specified per-point. This parameter influences the sound wave's
            propagation and scattering in the presence of trees. Lower frequencies travel longer distances than higher
            frequencies. It's recommended to use values between 63 Hz and 8000 Hz; values outside this range will be
            clamped to the nearest boundary for the sound absorption coefficient calculation.

    Optional kwargs:
        - absorb_ratio_column (str, optional): The name of the column in the `obstacles` GeoDataFrame that contains the
            sound absorption coefficients for each obstacle. Default is None. If not specified, all obstacles will have
            the `standart_absorb_ratio`.
        - standart_absorb_ratio (float, optional): The default sound absorption coefficient to use for obstacles without
            specified values in the `absorb_ratio_column`. Default is 0.05, which is a typical value for concrete walls.
        - trees (gpd.GeoDataFrame, optional): A GeoDataFrame containing trees or dense vegetation along the sound wave's
            path. Trees will scatter and absorb sound waves.
        - tree_resolution (int, optional): A resolution parameter for simulating tree interactions with sound waves.
            Recommended values are between 2 and 16, with higher values providing more accurate simulation results.
        - air_temperature (float, optional): The air temperature in degrees Celsius. The recommended range is from 0 to
            30 degrees Celsius, as temperatures outside this range will be clipped. Temperature affects the sound
            propagation in the air.
        - target_noise_db (float, optional): The target noise level (in dB) for the simulation. Default is 40 dB.
            Lower values may not be relevant for further analysis, as they are near the threshold of human hearing.
        - db_sim_step (float, optional): The step size in decibels for the noise simulation. Default is 1. For more
            precise analysis, this can be adjusted. If the difference between `source_noise_db` and `target_noise_db`
            is not divisible by the step size, the function will raise an error.
        - reflection_n (int, optional): The maximum number of reflections (bounces) to simulate for each sound wave.
            Recommended values are between 1 and 3. Larger values will result in longer simulation times.
        - dead_area_r (float, optional): A debugging parameter that defines the radius of the "dead zone" for reflections.
            Points within this area will not generate reflections. This is useful to prevent the algorithm from getting
            stuck in corners or along building walls.
        - use_parallel (bool, optional): Whether to use ProcessPool for task distribution or not. Default is True.
    Returns:
        (gpd.GeoDataFrame): A GeoDataFrame containing the noise simulation results, including noise levels and geometries
            of the affected areas. Each point's simulation results will be merged into a single GeoDataFrame.
    """
    # Obstacles args
    absorb_ratio_column = kwargs.get("absorb_ratio_column", None)
    standart_absorb_ratio = kwargs.get("standart_absorb_ratio", 0.05)

    # Trees args
    trees = kwargs.get("trees", None)
    tree_res = kwargs.get("tree_resolution", 4)

    # Simulation conditions
    air_temperature = kwargs.get("air_temperature", 20)
    target_noise_db = kwargs.get("target_noise_db", 40)

    # Simulation params
    db_sim_step = kwargs.get("db_sim_step", 1)
    reflection_n = kwargs.get("reflection_n", 3)
    dead_area_r = kwargs.get("dead_area_r", 5)

    # Use paralleling
    use_parallel = kwargs.get("use_parallel", True)

    # Validate optional columns or default values
    use_column_db = False
    if "source_noise_db" in source_points.columns:
        if (source_points["source_noise_db"] > MAX_DB_VALUE).any():
            raise ValueError(
                f"One or more values in 'source_noise_db' column exceed the physical limit of {MAX_DB_VALUE} dB."
            )
        if source_points["source_noise_db"].isnull().any():
            raise ValueError(f"Column 'source_noise_db' contains missing (NaN) values")
        use_column_db = True

    use_column_freq = False
    if "geometric_mean_freq_hz" in source_points.columns:
        if source_points["geometric_mean_freq_hz"].isnull().any():
            raise ValueError(f"Column 'geometric_mean_freq_hz' contains missing (NaN) values")
        use_column_freq = True

    if not use_column_db:
        if source_noise_db is None:
            raise ValueError(
                "Either `source_noise_db` must be provided or the `source_points` must contain a 'source_noise_db' column."
            )
        if source_noise_db > MAX_DB_VALUE:
            raise ValueError(
                f"source_noise_db ({source_noise_db} dB) exceeds the physical limit of {MAX_DB_VALUE} dB in air."
            )

    if not use_column_freq:
        if geometric_mean_freq_hz is None:
            raise ValueError(
                "Either `geometric_mean_freq_hz` must be provided or the `source_points` must contain a 'geometric_mean_freq_hz' column."
            )
    if not use_column_db and not use_column_freq and len(source_points) > 1:
        logger.warning(
            "`source_noise_db` and `geometric_mean_freq_hz` will be used for all points. Per-point simulation parameters not found."
        )

    original_crs = source_points.crs
    source_points = source_points.copy()

    source_points = source_points.copy()
    if len(obstacles) > 0:
        obstacles = obstacles.copy()
        obstacles.geometry = obstacles.geometry.simplify(tolerance=1)
        local_crs = obstacles.estimate_utm_crs()
        obstacles.to_crs(local_crs, inplace=True)
        source_points.to_crs(local_crs, inplace=True)
    else:
        local_crs = source_points.estimate_utm_crs()
        source_points.to_crs(local_crs, inplace=True)
        source_points.reset_index(drop=True)
        source_points.geometry = source_points.centroid

    # Simplifying trees
    if trees is not None:
        trees = trees.copy()
        trees.to_crs(local_crs, inplace=True)
        trees.geometry = trees.geometry.simplify(tolerance=1)
    else:
        trees = gpd.GeoDataFrame()

    if absorb_ratio_column is None:
        obstacles["absorb_ratio"] = standart_absorb_ratio
    else:
        obstacles["absorb_ratio"] = obstacles[absorb_ratio_column].fillna(standart_absorb_ratio)
    obstacles = obstacles[["absorb_ratio", "geometry"]]

    # creating initial task and simulating for each point
    task_queue = multiprocessing.Queue()
    dead_area_dict = {}
    for ind, row in source_points.iterrows():
        source_point = row.geometry
        local_db = row["source_noise_db"] if use_column_db else source_noise_db
        local_freq = row["geometric_mean_freq_hz"] if use_column_freq else geometric_mean_freq_hz

        # calculating layer dist and db values
        dist_db = [(0, local_db)]
        cur_db = local_db - db_sim_step
        while cur_db > target_noise_db - db_sim_step:
            if cur_db - db_sim_step < target_noise_db:
                cur_db = target_noise_db
            max_dist = dist_to_target_db(local_db, cur_db, local_freq, air_temperature)
            dist_db.append((max_dist, cur_db))
            cur_db -= db_sim_step

        args = (source_point, obstacles, trees, 0, 0, dist_db)
        kwargs = {
            "reflection_n": reflection_n,
            "geometric_mean_freq_hz": local_freq,
            "tree_res": tree_res,
            "min_db": target_noise_db,
            "simulation_ind": ind,
        }
        task_queue.put((_noise_from_point_task, args, kwargs))
        dead_area_dict[ind] = source_point.buffer(dead_area_r, resolution=2)

    noise_gdf = _recursive_simulation_queue(
        task_queue, dead_area_dict=dead_area_dict, dead_area_r=dead_area_r, use_parallel=use_parallel
    )

    noise_gdf = gpd.GeoDataFrame(pd.concat(noise_gdf, ignore_index=True), crs=local_crs)
    polygons = gpd.GeoDataFrame(
        geometry=list(polygonize(noise_gdf.geometry.apply(polygons_to_multilinestring).union_all())), crs=local_crs
    )
    polygons_points = polygons.copy()
    polygons_points.geometry = polygons.representative_point()
    sim_result = polygons_points.sjoin(noise_gdf, predicate="within").reset_index()
    sim_result = sim_result.groupby("index").agg({"noise_level": "max"})
    sim_result["geometry"] = polygons
    sim_result = (
        gpd.GeoDataFrame(sim_result, geometry="geometry", crs=local_crs).dissolve(by="noise_level").reset_index()
    )

    return sim_result.to_crs(original_crs)

noise_simulation_1point


Simplified Noise Frame

Creates a simplified noise exposure map using only geometric visibility and sound decay, without full wave simulation. Ideal for quick results.

objectnat.calculate_simplified_noise_frame(noise_sources, obstacles, air_temperature, **kwargs)

Calculates a simplified environmental noise frame using static noise source geometries without simulating full sound wave propagation or reflections.

This function provides a fast approximation of noise dispersion from a variety of source geometries, including points (e.g., traffic noise measurement points), lines (e.g., roads or railways), and polygons (e.g., industrial zones or buildings). Instead of simulating detailed wave interactions and reflections, it constructs an envelope of potential noise exposure by buffering the source geometry and applying simplified decay formulas based on sound power, frequency and temperature.

Parameters:

Name Type Description Default
noise_sources GeoDataFrame

A GeoDataFrame containing geometries of noise sources (Point, LineString, or Polygon). Each feature must have the following two columns: - 'source_noise_db': Initial sound level at the source, in decibels (dB). - 'geometric_mean_freq_hz': Characteristic sound frequency (Hz) used to model distance-based attenuation. Values in 'source_noise_db' must not exceed the physical maximum of 194 dB. Missing or NaN values in required fields will raise an error.

required
obstacles GeoDataFrame

A GeoDataFrame representing physical obstructions in the environment (e.g., buildings, walls, terrain). These are used to build visibility masks that affect where sound can propagate. Geometry will be simplified for performance using a default tolerance of 1 unit.

required
air_temperature float

The ambient air temperature in degrees Celsius. This value influences the attenuation model of sound in the atmosphere. Temperatures significantly outside the typical 0–30°C range may lead to inaccurate results.

required
Optional kwargs
  • target_noise_db (float, optional): The minimum sound level threshold (in dB) to be modeled. Any value below this threshold is considered insignificant and will be excluded from the resulting noise frame. Default is 40 dB.
  • db_sim_step (float, optional): The simulation step size (in dB) used to discretize sound levels into spatial layers. Default is 5. Smaller values produce more detailed output but increase computation time.
  • linestring_point_radius (float, optional): The spacing radius (in meters) used when converting LineString geometries into distributed point sources for simulation. Default is 30. Reducing this value improves detail along long lines.
  • polygon_point_radius (float, optional): The point spacing (in meters) for distributing sources within Polygon geometries. Default is 15. Points are sampled across the polygon’s surface and perimeter to represent the full sound-emitting area.

Returns:

Type Description
GeoDataFrame

A GeoDataFrame representing simplified noise distribution areas. The output geometries are polygons where each polygon is associated with the maximum sound level (in dB) present in that area, as derived from overlapping source zones. The resulting data is dissolved by noise level and returned in the original coordinate reference system (CRS) of the input sources.

Notes
  • The function does not model reflections or complex diffraction effects. It uses straight-line visibility (line-of-sight) and a layered distance-decay approach for rapid estimation.
  • Obstacles are used for visibility masking only, not as reflectors or absorbers.
  • Output resolution and accuracy depend heavily on the geometry type and point distribution settings.
  • Results are useful for quick noise mapping or for generating initial noise envelopes prior to more detailed simulations.
Source code in src\objectnat\methods\noise\noise_simulation_simplified.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
def calculate_simplified_noise_frame(
    noise_sources: gpd.GeoDataFrame, obstacles: gpd.GeoDataFrame, air_temperature, **kwargs
) -> gpd.GeoDataFrame:
    """
    Calculates a simplified environmental noise frame using static noise source geometries without simulating
    full sound wave propagation or reflections.

    This function provides a fast approximation of noise dispersion from a variety of source geometries, including
    points (e.g., traffic noise measurement points), lines (e.g., roads or railways), and polygons (e.g., industrial
    zones or buildings). Instead of simulating detailed wave interactions and reflections, it constructs an
    envelope of potential noise exposure by buffering the source geometry and applying simplified decay formulas
    based on sound power, frequency and temperature.

    Args:
        noise_sources (gpd.GeoDataFrame): A GeoDataFrame containing geometries of noise sources (Point, LineString,
            or Polygon). Each feature must have the following two columns:
                - 'source_noise_db': Initial sound level at the source, in decibels (dB).
                - 'geometric_mean_freq_hz': Characteristic sound frequency (Hz) used to model distance-based
                  attenuation.
            Values in 'source_noise_db' must not exceed the physical maximum of 194 dB. Missing or NaN values in
            required fields will raise an error.

        obstacles (gpd.GeoDataFrame): A GeoDataFrame representing physical obstructions in the environment
            (e.g., buildings, walls, terrain). These are used to build visibility masks that affect where sound can
            propagate. Geometry will be simplified for performance using a default tolerance of 1 unit.

        air_temperature (float): The ambient air temperature in degrees Celsius. This value influences the
            attenuation model of sound in the atmosphere. Temperatures significantly outside the typical 0–30°C
            range may lead to inaccurate results.

    Optional kwargs:
        - target_noise_db (float, optional): The minimum sound level threshold (in dB) to be modeled. Any value below
            this threshold is considered insignificant and will be excluded from the resulting noise frame.
            Default is 40 dB.
        - db_sim_step (float, optional): The simulation step size (in dB) used to discretize sound levels into
            spatial layers. Default is 5. Smaller values produce more detailed output but increase computation time.
        - linestring_point_radius (float, optional): The spacing radius (in meters) used when converting LineString
            geometries into distributed point sources for simulation. Default is 30. Reducing this value improves
            detail along long lines.
        - polygon_point_radius (float, optional): The point spacing (in meters) for distributing sources within
            Polygon geometries. Default is 15. Points are sampled across the polygon’s surface and perimeter to
            represent the full sound-emitting area.

    Returns:
        (gpd.GeoDataFrame): A GeoDataFrame representing simplified noise distribution areas. The output geometries
            are polygons where each polygon is associated with the maximum sound level (in dB) present in that area,
            as derived from overlapping source zones. The resulting data is dissolved by noise level and returned in
            the original coordinate reference system (CRS) of the input sources.

    Notes:
        - The function does not model reflections or complex diffraction effects. It uses straight-line
          visibility (line-of-sight) and a layered distance-decay approach for rapid estimation.
        - Obstacles are used for visibility masking only, not as reflectors or absorbers.
        - Output resolution and accuracy depend heavily on the geometry type and point distribution settings.
        - Results are useful for quick noise mapping or for generating initial noise envelopes prior to more
          detailed simulations.
    """
    target_noise_db = kwargs.get("target_noise_db", 40)
    db_sim_step = kwargs.get("db_sim_step", 5)
    linestring_point_radius = kwargs.get("linestring_point_radius", 30)
    polygon_point_radius = kwargs.get("polygon_point_radius", 15)

    required_columns = ["source_noise_db", "geometric_mean_freq_hz"]
    for col in required_columns:
        if col not in noise_sources.columns:
            raise ValueError(f"'{col}' column is missing in provided GeoDataFrame")
        if noise_sources[col].isnull().any():
            raise ValueError(f"Column '{col}' contains missing (NaN) values")
    if (noise_sources["source_noise_db"] > MAX_DB_VALUE).any():
        raise ValueError(
            f"One or more values in 'source_noise_db' column exceed the physical limit of {MAX_DB_VALUE} dB."
        )
    original_crs = noise_sources.crs
    if len(obstacles) > 0:
        obstacles = obstacles.copy()
        obstacles.geometry = obstacles.geometry.simplify(tolerance=1)
        local_crs = obstacles.estimate_utm_crs()
        obstacles.to_crs(local_crs, inplace=True)
        noise_sources.to_crs(local_crs, inplace=True)
    else:
        local_crs = noise_sources.estimate_utm_crs()
        noise_sources.to_crs(local_crs, inplace=True)
        noise_sources.reset_index(drop=True)

    noise_sources = noise_sources.explode(ignore_index=True)
    noise_sources["geom_type"] = noise_sources.geom_type

    grouped_sources = noise_sources.groupby(by=["source_noise_db", "geometric_mean_freq_hz", "geom_type"])

    frame_result = []
    total_tasks = 0
    with tqdm(total=total_tasks, desc="Simulating noise") as pbar:
        for (source_db, freq_hz, geom_type), group_gdf in grouped_sources:
            # calculating layer dist and db values
            dist_db = [(0, source_db)]
            cur_db = source_db - db_sim_step
            max_dist = 0
            while cur_db > target_noise_db - db_sim_step:
                if cur_db - db_sim_step < target_noise_db:
                    cur_db = target_noise_db
                max_dist = dist_to_target_db(source_db, cur_db, freq_hz, air_temperature)
                dist_db.append((max_dist, cur_db))
                cur_db -= db_sim_step

            # increasing max_dist for extra view
            max_dist = max_dist * 1.2

            if geom_type == "Point":
                total_tasks += len(group_gdf)
                pbar.total = total_tasks
                pbar.refresh()
                for _, row in group_gdf.iterrows():
                    point_from = row.geometry
                    point_buffer = point_from.buffer(max_dist, resolution=16)
                    local_obstacles = obstacles[obstacles.intersects(point_buffer)]
                    vis_poly = get_visibility_accurate(point_from, obstacles=local_obstacles, view_distance=max_dist)
                    noise_from_feature = _eval_donuts_gdf(point_from, dist_db, local_crs, vis_poly)
                    frame_result.append(noise_from_feature)
                    pbar.update(1)

            elif geom_type == "LineString":
                layer_points = distribute_points_on_linestrings(
                    group_gdf, radius=linestring_point_radius, lloyd_relax_n=1
                )
                total_tasks += len(layer_points)
                pbar.total = total_tasks
                pbar.refresh()
                noise_from_feature = _process_lines_or_polygons(
                    group_gdf, max_dist, obstacles, layer_points, dist_db, local_crs, pbar
                )
                frame_result.append(noise_from_feature)
            elif geom_type == "Polygon":
                group_gdf.geometry = group_gdf.buffer(0.1, resolution=1)
                layer_points = distribute_points_on_polygons(
                    group_gdf, only_exterior=False, radius=polygon_point_radius, lloyd_relax_n=1
                )
                total_tasks += len(layer_points)
                pbar.total = total_tasks
                pbar.refresh()
                noise_from_feature = _process_lines_or_polygons(
                    group_gdf, max_dist, obstacles, layer_points, dist_db, local_crs, pbar
                )
                frame_result.append(noise_from_feature)
            else:
                pass

    noise_gdf = gpd.GeoDataFrame(pd.concat(frame_result, ignore_index=True), crs=local_crs)
    polygons = gpd.GeoDataFrame(
        geometry=list(polygonize(noise_gdf.geometry.apply(polygons_to_multilinestring).union_all())), crs=local_crs
    )
    polygons_points = polygons.copy()
    polygons_points.geometry = polygons.representative_point()
    sim_result = polygons_points.sjoin(noise_gdf, predicate="within").reset_index()
    sim_result = sim_result.groupby("index").agg({"noise_level": "max"})
    sim_result["geometry"] = polygons
    sim_result = (
        gpd.GeoDataFrame(sim_result, geometry="geometry", crs=local_crs).dissolve(by="noise_level").reset_index()
    )

    return sim_result.to_crs(original_crs)

noise_frame


More Details

For comprehensive documentation and advanced configuration options, see the project Wiki:
Noise Simulation on GitHub