ObjectNat 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl
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.
Potentially problematic release.
This version of ObjectNat might be problematic. Click here for more details.
- objectnat/_config.py +0 -4
- objectnat/_version.py +1 -1
- objectnat/methods/coverage_zones/graph_coverage.py +11 -4
- objectnat/methods/coverage_zones/radius_voronoi_coverage.py +4 -2
- objectnat/methods/coverage_zones/stepped_coverage.py +20 -10
- objectnat/methods/isochrones/isochrones.py +31 -11
- objectnat/methods/noise/noise_simulation.py +14 -13
- objectnat/methods/noise/noise_simulation_simplified.py +10 -9
- objectnat/methods/point_clustering/cluster_points_in_polygons.py +3 -3
- objectnat/methods/provision/provision.py +112 -16
- objectnat/methods/provision/provision_model.py +3 -17
- objectnat/methods/utils/graph_utils.py +5 -5
- objectnat/methods/utils/math_utils.py +1 -1
- objectnat/methods/visibility/visibility_analysis.py +9 -17
- objectnat-1.3.0.dist-info/METADATA +201 -0
- objectnat-1.3.0.dist-info/RECORD +33 -0
- {objectnat-1.2.1.dist-info → objectnat-1.3.0.dist-info}/WHEEL +1 -1
- objectnat-1.2.1.dist-info/METADATA +0 -115
- objectnat-1.2.1.dist-info/RECORD +0 -33
- {objectnat-1.2.1.dist-info → objectnat-1.3.0.dist-info}/LICENSE.txt +0 -0
objectnat/_config.py
CHANGED
|
@@ -30,7 +30,6 @@ class Config:
|
|
|
30
30
|
):
|
|
31
31
|
self.enable_tqdm_bar = enable_tqdm_bar
|
|
32
32
|
self.logger = logger
|
|
33
|
-
self.pandarallel_use_file_system = False
|
|
34
33
|
|
|
35
34
|
def change_logger_lvl(self, lvl: Literal["TRACE", "DEBUG", "INFO", "WARN", "ERROR"]):
|
|
36
35
|
self.logger.remove()
|
|
@@ -39,9 +38,6 @@ class Config:
|
|
|
39
38
|
def set_enable_tqdm(self, enable: bool):
|
|
40
39
|
self.enable_tqdm_bar = enable
|
|
41
40
|
|
|
42
|
-
def set_pandarallel_use_file_system(self, enable: bool):
|
|
43
|
-
self.pandarallel_use_file_system = enable
|
|
44
|
-
|
|
45
41
|
|
|
46
42
|
config = Config()
|
|
47
43
|
config.change_logger_lvl("INFO")
|
objectnat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "1.
|
|
1
|
+
VERSION = "1.3.0"
|
|
@@ -27,20 +27,27 @@ def get_graph_coverage(
|
|
|
27
27
|
4. Combining reachability information with Voronoi cells
|
|
28
28
|
5. Clipping results to specified zone boundary
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Args:
|
|
31
31
|
gdf_to (gpd.GeoDataFrame):
|
|
32
32
|
Source points to which coverage is calculated.
|
|
33
|
+
|
|
33
34
|
nx_graph (nx.Graph):
|
|
34
35
|
NetworkX graph representing the transportation network.
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
weight_type:
|
|
38
|
+
Type of edge weight to use for path calculation:
|
|
39
|
+
|
|
40
|
+
- ``"time_min"``: Edge travel time in minutes
|
|
41
|
+
- ``"length_meter"``: Edge length in meters
|
|
42
|
+
|
|
37
43
|
weight_value_cutoff (float):
|
|
38
44
|
Maximum weight value for path calculations (e.g., max travel time/distance).
|
|
45
|
+
|
|
39
46
|
zone (gpd.GeoDataFrame):
|
|
40
47
|
Boundary polygon to clip the resulting coverage zones. If None, concave hull of reachable nodes will be used.
|
|
41
48
|
|
|
42
49
|
Returns:
|
|
43
|
-
|
|
50
|
+
gpd.GeoDataFrame:
|
|
44
51
|
GeoDataFrame with coverage zones polygons, each associated with its source point, returns in the same CRS
|
|
45
52
|
as original gdf_from.
|
|
46
53
|
|
|
@@ -6,16 +6,18 @@ def get_radius_coverage(gdf_from: gpd.GeoDataFrame, radius: float, resolution: i
|
|
|
6
6
|
"""
|
|
7
7
|
Calculate radius-based coverage zones using Voronoi polygons.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Args:
|
|
10
10
|
gdf_from (gpd.GeoDataFrame):
|
|
11
11
|
Source points for which coverage zones are calculated.
|
|
12
|
+
|
|
12
13
|
radius (float):
|
|
13
14
|
Maximum coverage radius in meters.
|
|
15
|
+
|
|
14
16
|
resolution (int):
|
|
15
17
|
Number of segments used to approximate quarter-circle in buffer (default=32).
|
|
16
18
|
|
|
17
19
|
Returns:
|
|
18
|
-
|
|
20
|
+
gpd.GeoDataFrame:
|
|
19
21
|
GeoDataFrame with smoothed coverage zone polygons in the same CRS as original gdf_from.
|
|
20
22
|
|
|
21
23
|
Notes:
|
|
@@ -31,30 +31,40 @@ def get_stepped_graph_coverage(
|
|
|
31
31
|
4. Aggregates zones into stepped coverage layers
|
|
32
32
|
5. Optionally clips results to a boundary zone
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Args:
|
|
35
35
|
gdf_to (gpd.GeoDataFrame):
|
|
36
36
|
Source points from which stepped coverage is calculated.
|
|
37
|
+
|
|
37
38
|
nx_graph (nx.Graph):
|
|
38
39
|
NetworkX graph representing the transportation network.
|
|
39
|
-
|
|
40
|
+
|
|
41
|
+
weight_type:
|
|
40
42
|
Type of edge weight to use for path calculation:
|
|
41
|
-
|
|
42
|
-
- "
|
|
43
|
-
|
|
43
|
+
|
|
44
|
+
- ``"time_min"``: Edge travel time in minutes
|
|
45
|
+
- ``"length_meter"``: Edge length in meters
|
|
46
|
+
|
|
47
|
+
step_type:
|
|
44
48
|
Method for generating stepped zones:
|
|
45
|
-
|
|
46
|
-
- "
|
|
49
|
+
|
|
50
|
+
- ``"voronoi"``: Stepped zones based on Voronoi polygons around graph nodes
|
|
51
|
+
- ``"separate"``: Independent buffer zones per step
|
|
52
|
+
|
|
47
53
|
weight_value_cutoff (float, optional):
|
|
48
54
|
Maximum weight value (e.g., max travel time or distance) to limit the coverage extent.
|
|
55
|
+
|
|
49
56
|
zone (gpd.GeoDataFrame, optional):
|
|
50
57
|
Optional boundary polygon to clip resulting stepped zones. If None, concave hull of reachable area is used.
|
|
58
|
+
|
|
51
59
|
step (float, optional):
|
|
52
60
|
Step interval for coverage zone construction. Defaults to:
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
|
|
62
|
+
- 100 meters for distance-based weight
|
|
63
|
+
- 1 minute for time-based weight
|
|
55
64
|
|
|
56
65
|
Returns:
|
|
57
|
-
|
|
66
|
+
gpd.GeoDataFrame:
|
|
67
|
+
GeoDataFrame with polygons representing stepped coverage zones for each input point,
|
|
58
68
|
annotated by step range.
|
|
59
69
|
|
|
60
70
|
Notes:
|
|
@@ -31,33 +31,44 @@ def get_accessibility_isochrone_stepped(
|
|
|
31
31
|
"""
|
|
32
32
|
Calculate stepped accessibility isochrones for a single point with specified intervals.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
isochrone_type
|
|
34
|
+
Args:
|
|
35
|
+
isochrone_type:
|
|
36
36
|
Visualization method for stepped isochrones:
|
|
37
|
-
|
|
38
|
-
- "
|
|
39
|
-
- "
|
|
37
|
+
|
|
38
|
+
- ``"radius"``: Voronoi-based in circular buffers
|
|
39
|
+
- ``"ways"``: Voronoi-based in road network polygons
|
|
40
|
+
- ``"separate"``: Circular buffers for each step
|
|
41
|
+
|
|
40
42
|
point (gpd.GeoDataFrame):
|
|
41
43
|
Single source point for isochrone calculation (uses first geometry if multiple provided).
|
|
44
|
+
|
|
42
45
|
weight_value (float):
|
|
43
46
|
Maximum travel time (minutes) or distance (meters) threshold.
|
|
44
|
-
|
|
47
|
+
|
|
48
|
+
weight_type:
|
|
45
49
|
Type of weight calculation:
|
|
50
|
+
|
|
46
51
|
- "time_min": Time-based in minutes
|
|
47
52
|
- "length_meter": Distance-based in meters
|
|
53
|
+
|
|
48
54
|
nx_graph (nx.Graph):
|
|
49
55
|
NetworkX graph representing the transportation network.
|
|
56
|
+
|
|
50
57
|
step (float, optional):
|
|
51
58
|
Interval between isochrone steps. Defaults to:
|
|
59
|
+
|
|
52
60
|
- 100 meters for distance-based
|
|
53
61
|
- 1 minute for time-based
|
|
62
|
+
|
|
54
63
|
**kwargs: Additional parameters:
|
|
64
|
+
|
|
55
65
|
- buffer_factor: Size multiplier for buffers (default: 0.7)
|
|
56
66
|
- road_buffer_size: Buffer size for road edges in meters (default: 5)
|
|
57
67
|
|
|
58
68
|
Returns:
|
|
59
|
-
|
|
69
|
+
tuple[gpd.GeoDataFrame, gpd.GeoDataFrame | None, gpd.GeoDataFrame | None]:
|
|
60
70
|
Tuple containing:
|
|
71
|
+
|
|
61
72
|
- stepped_isochrones: GeoDataFrame with stepped polygons and distance/time attributes
|
|
62
73
|
- pt_stops: Public transport stops within isochrones (if available)
|
|
63
74
|
- pt_routes: Public transport routes within isochrones (if available)
|
|
@@ -150,29 +161,38 @@ def get_accessibility_isochrones(
|
|
|
150
161
|
- 'radius': Circular buffer-based isochrones
|
|
151
162
|
- 'ways': Road network-based isochrones
|
|
152
163
|
|
|
153
|
-
|
|
154
|
-
isochrone_type
|
|
164
|
+
Args:
|
|
165
|
+
isochrone_type:
|
|
155
166
|
Type of isochrone to calculate:
|
|
167
|
+
|
|
156
168
|
- "radius": Creates circular buffers around reachable nodes
|
|
157
169
|
- "ways": Creates polygons based on reachable road network
|
|
170
|
+
|
|
158
171
|
points (gpd.GeoDataFrame):
|
|
159
172
|
GeoDataFrame containing source points for isochrone calculation.
|
|
173
|
+
|
|
160
174
|
weight_value (float):
|
|
161
175
|
Maximum travel time (minutes) or distance (meters) threshold.
|
|
162
|
-
|
|
176
|
+
|
|
177
|
+
weight_type:
|
|
163
178
|
Type of weight calculation:
|
|
179
|
+
|
|
164
180
|
- "time_min": Time-based accessibility in minutes
|
|
165
181
|
- "length_meter": Distance-based accessibility in meters
|
|
182
|
+
|
|
166
183
|
nx_graph (nx.Graph):
|
|
167
184
|
NetworkX graph representing the transportation network.
|
|
168
185
|
Must contain CRS and speed attributes for time calculations.
|
|
186
|
+
|
|
169
187
|
**kwargs: Additional parameters:
|
|
188
|
+
|
|
170
189
|
- buffer_factor: Size multiplier for buffers (default: 0.7)
|
|
171
190
|
- road_buffer_size: Buffer size for road edges in meters (default: 5)
|
|
172
191
|
|
|
173
192
|
Returns:
|
|
174
|
-
|
|
193
|
+
tuple[gpd.GeoDataFrame, gpd.GeoDataFrame | None, gpd.GeoDataFrame | None]:
|
|
175
194
|
Tuple containing:
|
|
195
|
+
|
|
176
196
|
- isochrones: GeoDataFrame with calculated isochrone polygons
|
|
177
197
|
- pt_stops: Public transport stops within isochrones (if available)
|
|
178
198
|
- pt_routes: Public transport routes within isochrones (if available)
|
|
@@ -35,7 +35,7 @@ def simulate_noise(
|
|
|
35
35
|
"""
|
|
36
36
|
Simulates noise propagation from a set of source points considering obstacles, trees, and environmental factors.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
Args:
|
|
39
39
|
source_points (gpd.GeoDataFrame):
|
|
40
40
|
A GeoDataFrame with one or more point geometries representing noise sources.
|
|
41
41
|
Optionally, it can include 'source_noise_db' and 'geometric_mean_freq_hz' columns for per-point simulation.
|
|
@@ -53,32 +53,33 @@ def simulate_noise(
|
|
|
53
53
|
frequencies. It's recommended to use values between 63 Hz and 8000 Hz; values outside this range will be
|
|
54
54
|
clamped to the nearest boundary for the sound absorption coefficient calculation.
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
Keyword Args:
|
|
57
|
+
absorb_ratio_column (str, optional): The name of the column in the `obstacles` GeoDataFrame that contains the
|
|
58
58
|
sound absorption coefficients for each obstacle. Default is None. If not specified, all obstacles will have
|
|
59
59
|
the `standart_absorb_ratio`.
|
|
60
|
-
|
|
60
|
+
standart_absorb_ratio (float, optional): The default sound absorption coefficient to use for obstacles without
|
|
61
61
|
specified values in the `absorb_ratio_column`. Default is 0.05, which is a typical value for concrete walls.
|
|
62
|
-
|
|
62
|
+
trees (gpd.GeoDataFrame, optional): A GeoDataFrame containing trees or dense vegetation along the sound wave's
|
|
63
63
|
path. Trees will scatter and absorb sound waves.
|
|
64
|
-
|
|
64
|
+
tree_resolution (int, optional): A resolution parameter for simulating tree interactions with sound waves.
|
|
65
65
|
Recommended values are between 2 and 16, with higher values providing more accurate simulation results.
|
|
66
|
-
|
|
66
|
+
air_temperature (float, optional): The air temperature in degrees Celsius. The recommended range is from 0 to
|
|
67
67
|
30 degrees Celsius, as temperatures outside this range will be clipped. Temperature affects the sound
|
|
68
68
|
propagation in the air.
|
|
69
|
-
|
|
69
|
+
target_noise_db (float, optional): The target noise level (in dB) for the simulation. Default is 40 dB.
|
|
70
70
|
Lower values may not be relevant for further analysis, as they are near the threshold of human hearing.
|
|
71
|
-
|
|
71
|
+
db_sim_step (float, optional): The step size in decibels for the noise simulation. Default is 1. For more
|
|
72
72
|
precise analysis, this can be adjusted. If the difference between `source_noise_db` and `target_noise_db`
|
|
73
73
|
is not divisible by the step size, the function will raise an error.
|
|
74
|
-
|
|
74
|
+
reflection_n (int, optional): The maximum number of reflections (bounces) to simulate for each sound wave.
|
|
75
75
|
Recommended values are between 1 and 3. Larger values will result in longer simulation times.
|
|
76
|
-
|
|
76
|
+
dead_area_r (float, optional): A debugging parameter that defines the radius of the "dead zone" for reflections.
|
|
77
77
|
Points within this area will not generate reflections. This is useful to prevent the algorithm from getting
|
|
78
78
|
stuck in corners or along building walls.
|
|
79
|
-
|
|
79
|
+
use_parallel (bool, optional): Whether to use ProcessPool for task distribution or not. Default is True.
|
|
80
|
+
|
|
80
81
|
Returns:
|
|
81
|
-
|
|
82
|
+
gpd.GeoDataFrame: A GeoDataFrame containing the noise simulation results, including noise levels and geometries
|
|
82
83
|
of the affected areas. Each point's simulation results will be merged into a single GeoDataFrame.
|
|
83
84
|
"""
|
|
84
85
|
# Obstacles args
|
|
@@ -31,9 +31,10 @@ def calculate_simplified_noise_frame(
|
|
|
31
31
|
Args:
|
|
32
32
|
noise_sources (gpd.GeoDataFrame): A GeoDataFrame containing geometries of noise sources (Point, LineString,
|
|
33
33
|
or Polygon). Each feature must have the following two columns:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
|
|
35
|
+
- 'source_noise_db': Initial sound level at the source, in decibels (dB).
|
|
36
|
+
- 'geometric_mean_freq_hz': Characteristic sound frequency (Hz) used to model distance-based attenuation.
|
|
37
|
+
|
|
37
38
|
Values in 'source_noise_db' must not exceed the physical maximum of 194 dB. Missing or NaN values in
|
|
38
39
|
required fields will raise an error.
|
|
39
40
|
|
|
@@ -45,21 +46,21 @@ def calculate_simplified_noise_frame(
|
|
|
45
46
|
attenuation model of sound in the atmosphere. Temperatures significantly outside the typical 0–30°C
|
|
46
47
|
range may lead to inaccurate results.
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
Keyword Args:
|
|
50
|
+
target_noise_db (float, optional): The minimum sound level threshold (in dB) to be modeled. Any value below
|
|
50
51
|
this threshold is considered insignificant and will be excluded from the resulting noise frame.
|
|
51
52
|
Default is 40 dB.
|
|
52
|
-
|
|
53
|
+
db_sim_step (float, optional): The simulation step size (in dB) used to discretize sound levels into
|
|
53
54
|
spatial layers. Default is 5. Smaller values produce more detailed output but increase computation time.
|
|
54
|
-
|
|
55
|
+
linestring_point_radius (float, optional): The spacing radius (in meters) used when converting LineString
|
|
55
56
|
geometries into distributed point sources for simulation. Default is 30. Reducing this value improves
|
|
56
57
|
detail along long lines.
|
|
57
|
-
|
|
58
|
+
polygon_point_radius (float, optional): The point spacing (in meters) for distributing sources within
|
|
58
59
|
Polygon geometries. Default is 15. Points are sampled across the polygon’s surface and perimeter to
|
|
59
60
|
represent the full sound-emitting area.
|
|
60
61
|
|
|
61
62
|
Returns:
|
|
62
|
-
|
|
63
|
+
gpd.GeoDataFrame: A GeoDataFrame representing simplified noise distribution areas. The output geometries
|
|
63
64
|
are polygons where each polygon is associated with the maximum sound level (in dB) present in that area,
|
|
64
65
|
as derived from overlapping source zones. The resulting data is dissolved by noise level and returned in
|
|
65
66
|
the original coordinate reference system (CRS) of the input sources.
|
|
@@ -39,7 +39,7 @@ def get_clusters_polygon(
|
|
|
39
39
|
Generate cluster polygons for given points based on a specified minimum distance and minimum points per cluster.
|
|
40
40
|
Optionally, calculate the relative ratio between types of points within the clusters.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
Args:
|
|
43
43
|
points (gpd.GeoDataFrame):
|
|
44
44
|
GeoDataFrame containing the points to be clustered.
|
|
45
45
|
Must include a 'service_code' column for service ratio calculations.
|
|
@@ -47,13 +47,13 @@ def get_clusters_polygon(
|
|
|
47
47
|
Minimum distance between points to be considered part of the same cluster. Defaults to 100.
|
|
48
48
|
min_point (int, optional):
|
|
49
49
|
Minimum number of points required to form a cluster. Defaults to 5.
|
|
50
|
-
method
|
|
50
|
+
method:
|
|
51
51
|
The clustering method to use. Must be either "DBSCAN" or "HDBSCAN". Defaults to "HDBSCAN".
|
|
52
52
|
service_code_column (str, optional):
|
|
53
53
|
Column, containing service type for relative ratio in clasterized polygons. Defaults to "service_code".
|
|
54
54
|
|
|
55
55
|
Returns:
|
|
56
|
-
|
|
56
|
+
tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
57
57
|
A tuple containing the clustered polygons GeoDataFrame and the original points GeoDataFrame with cluster labels.
|
|
58
58
|
"""
|
|
59
59
|
if method not in ["DBSCAN", "HDBSCAN"]:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
1
|
import geopandas as gpd
|
|
4
2
|
import numpy as np
|
|
5
3
|
import pandas as pd
|
|
@@ -18,26 +16,53 @@ def get_service_provision(
|
|
|
18
16
|
threshold: int,
|
|
19
17
|
buildings_demand_column: str = "demand",
|
|
20
18
|
services_capacity_column: str = "capacity",
|
|
21
|
-
) ->
|
|
22
|
-
"""
|
|
19
|
+
) -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
20
|
+
"""
|
|
21
|
+
Compute service provision between demand locations (buildings) and service facilities.
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
The function implements a **gravity-based allocation model**: service capacity is
|
|
24
|
+
distributed across nearby demand points with weights that **decay with the square
|
|
25
|
+
of distance (or generalized cost)**. Closer buildings receive proportionally
|
|
26
|
+
higher shares of the available capacity.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
29
|
buildings (gpd.GeoDataFrame):
|
|
30
|
-
GeoDataFrame of
|
|
30
|
+
GeoDataFrame of **demand locations** (e.g., residential buildings).
|
|
31
|
+
Must include a numeric column with demand values
|
|
32
|
+
(see ``buildings_demand_column``).
|
|
33
|
+
adjacency_matrix (pd.DataFrame):
|
|
34
|
+
A rectangular DataFrame representing **OD (origin–destination) costs**
|
|
35
|
+
between ``buildings`` (rows) and ``services`` (columns).
|
|
36
|
+
Units must match ``threshold`` (e.g., minutes or meters).
|
|
37
|
+
Missing or infinite values (``NaN`` or ``inf``) are treated as **unreachable**.
|
|
38
|
+
The row index must match ``buildings.index`` and column index must
|
|
39
|
+
match ``services.index``.
|
|
40
|
+
services (gpd.GeoDataFrame):
|
|
41
|
+
GeoDataFrame of **service facilities** (e.g., schools, clinics).
|
|
42
|
+
Must include a numeric column with service capacity
|
|
43
|
+
(see ``services_capacity_column``).
|
|
31
44
|
threshold (int):
|
|
32
|
-
|
|
45
|
+
Maximum allowed cost value for assignment.
|
|
46
|
+
Any OD entry **greater than this threshold** is considered unreachable.
|
|
47
|
+
Units are the same as in ``adjacency_matrix``.
|
|
33
48
|
buildings_demand_column (str):
|
|
34
|
-
|
|
49
|
+
Column name of building demand values. Default is ``"demand"``.
|
|
35
50
|
services_capacity_column (str):
|
|
36
|
-
|
|
51
|
+
Column name of service capacity values. Default is ``"capacity"``.
|
|
37
52
|
|
|
38
53
|
Returns:
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
55
|
+
A tuple of three GeoDataFrames:
|
|
56
|
+
|
|
57
|
+
- **buildings**: input buildings with updated provision metrics.
|
|
58
|
+
- **services**: input services with updated load and capacity metrics.
|
|
59
|
+
- **links**: building–service links within the threshold, containing
|
|
60
|
+
allocated demand shares and distances/costs based on the gravity model.
|
|
61
|
+
|
|
62
|
+
Notes:
|
|
63
|
+
- The model is **gravity-based**, with cost weights decaying by the **square of distance**.
|
|
64
|
+
- Unreachable OD pairs (``NaN`` or ``inf``) are ignored.
|
|
65
|
+
- The function does not perform routing; it expects a precomputed OD matrix.
|
|
41
66
|
"""
|
|
42
67
|
buildings = buildings.copy()
|
|
43
68
|
services = services.copy()
|
|
@@ -56,8 +81,36 @@ def get_service_provision(
|
|
|
56
81
|
|
|
57
82
|
def clip_provision(
|
|
58
83
|
buildings: gpd.GeoDataFrame, services: gpd.GeoDataFrame, links: gpd.GeoDataFrame, selection_zone: gpd.GeoDataFrame
|
|
59
|
-
) ->
|
|
84
|
+
) -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
85
|
+
"""
|
|
86
|
+
Clip service provision results to a specific geographic boundary.
|
|
87
|
+
|
|
88
|
+
Keeps only:
|
|
89
|
+
* buildings that intersect the ``selection_zone``;
|
|
90
|
+
* links that connect to the kept buildings;
|
|
91
|
+
* services referenced by those links.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
buildings:
|
|
95
|
+
GeoDataFrame of buildings **after** :func:`get_service_provision`.
|
|
96
|
+
services:
|
|
97
|
+
GeoDataFrame of services **after** :func:`get_service_provision`.
|
|
98
|
+
links:
|
|
99
|
+
GeoDataFrame of building–service links from
|
|
100
|
+
:func:`get_service_provision`. Must include indices or columns
|
|
101
|
+
to match buildings and services.
|
|
102
|
+
selection_zone:
|
|
103
|
+
GeoDataFrame (polygon or multipolygon) defining the clipping area.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
107
|
+
The filtered subsets of buildings, services, and links
|
|
108
|
+
that fall inside the specified zone.
|
|
60
109
|
|
|
110
|
+
Notes:
|
|
111
|
+
- The function performs **spatial filtering only**.
|
|
112
|
+
It does **not** recompute or redistribute demand/supply.
|
|
113
|
+
"""
|
|
61
114
|
assert selection_zone.crs == buildings.crs == services.crs == links.crs, (
|
|
62
115
|
f"CRS mismatch: buildings_crs:{buildings.crs}, "
|
|
63
116
|
f"links_crs:{links.crs} , "
|
|
@@ -79,6 +132,49 @@ def clip_provision(
|
|
|
79
132
|
def recalculate_links(
|
|
80
133
|
buildings: gpd.GeoDataFrame, services: gpd.GeoDataFrame, links: gpd.GeoDataFrame, new_max_dist: float
|
|
81
134
|
) -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
135
|
+
"""
|
|
136
|
+
Recalculate provision aggregates after tightening the accessibility threshold.
|
|
137
|
+
|
|
138
|
+
Removes all links whose cost (distance or time) exceeds ``new_max_dist``, then
|
|
139
|
+
updates demand and capacity aggregates accordingly. This is done **without
|
|
140
|
+
redistributing** removed demand to alternative services.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
buildings:
|
|
144
|
+
GeoDataFrame of buildings after :func:`get_service_provision`.
|
|
145
|
+
Expected to include provision-related fields such as demand, demand_left,
|
|
146
|
+
supplied demand, and average distance/cost.
|
|
147
|
+
|
|
148
|
+
services:
|
|
149
|
+
GeoDataFrame of services after :func:`get_service_provision`, with
|
|
150
|
+
fields describing remaining capacity and service load.
|
|
151
|
+
|
|
152
|
+
links:
|
|
153
|
+
GeoDataFrame of building–service links containing at least:
|
|
154
|
+
|
|
155
|
+
- ``building_index``
|
|
156
|
+
- ``service_index``
|
|
157
|
+
- ``distance`` (or time cost, in the same units as ``new_max_dist``)
|
|
158
|
+
- ``demand`` (assigned portion)
|
|
159
|
+
|
|
160
|
+
new_max_dist:
|
|
161
|
+
New maximum allowed cost value (same units as OD/threshold).
|
|
162
|
+
Links with cost **greater than** this value are removed.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
166
|
+
- **buildings**: updated aggregate demand metrics and recalculated
|
|
167
|
+
average cost.
|
|
168
|
+
- **services**: updated load and capacity fields after freeing excess capacity.
|
|
169
|
+
- **links**: subset of links that remain within the new threshold.
|
|
170
|
+
|
|
171
|
+
Notes:
|
|
172
|
+
- If no links exceed ``new_max_dist``, the function logs a warning
|
|
173
|
+
and returns the original inputs unchanged.
|
|
174
|
+
- Average cost values are recomputed based on remaining links.
|
|
175
|
+
If a building has no remaining assigned demand, ``avg_dist`` becomes ``NaN``.
|
|
176
|
+
- Removed demand is **not reallocated** to other services.
|
|
177
|
+
"""
|
|
82
178
|
buildings = buildings.copy()
|
|
83
179
|
services = services.copy()
|
|
84
180
|
links = links.copy()
|
|
@@ -4,7 +4,6 @@ from typing import Tuple
|
|
|
4
4
|
import geopandas as gpd
|
|
5
5
|
import numpy as np
|
|
6
6
|
import pandas as pd
|
|
7
|
-
from pandarallel import pandarallel
|
|
8
7
|
from shapely import LineString
|
|
9
8
|
|
|
10
9
|
from objectnat import config
|
|
@@ -48,7 +47,6 @@ class Provision:
|
|
|
48
47
|
).copy()
|
|
49
48
|
self.threshold = threshold
|
|
50
49
|
self.services.to_crs(self.demanded_buildings.crs, inplace=True)
|
|
51
|
-
pandarallel.initialize(progress_bar=False, verbose=0, use_memory_fs=config.pandarallel_use_file_system)
|
|
52
50
|
|
|
53
51
|
@staticmethod
|
|
54
52
|
def ensure_buildings(v: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
|
@@ -81,15 +79,7 @@ class Provision:
|
|
|
81
79
|
|
|
82
80
|
def run(self) -> Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
83
81
|
|
|
84
|
-
def apply_function_based_on_size(df, func, axis, threshold=100):
|
|
85
|
-
if len(df) > threshold:
|
|
86
|
-
return df.parallel_apply(func, axis=axis)
|
|
87
|
-
return df.apply(func, axis=axis)
|
|
88
|
-
|
|
89
82
|
def calculate_flows_y(loc):
|
|
90
|
-
import numpy as np # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
91
|
-
import pandas as pd # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
92
|
-
|
|
93
83
|
c = services_table.loc[loc.name]["capacity_left"]
|
|
94
84
|
p = 1 / loc / loc
|
|
95
85
|
p = p / p.sum()
|
|
@@ -106,9 +96,6 @@ class Provision:
|
|
|
106
96
|
return choice
|
|
107
97
|
|
|
108
98
|
def balance_flows_to_demands(loc):
|
|
109
|
-
import numpy as np # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
110
|
-
import pandas as pd # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
111
|
-
|
|
112
99
|
d = houses_table.loc[loc.name]["demand_left"]
|
|
113
100
|
loc = loc[loc > 0]
|
|
114
101
|
if loc.sum() > 0:
|
|
@@ -159,12 +146,11 @@ class Provision:
|
|
|
159
146
|
f" Best houses: {best_houses}"
|
|
160
147
|
)
|
|
161
148
|
|
|
162
|
-
temp_destination_matrix =
|
|
163
|
-
|
|
149
|
+
temp_destination_matrix = distance_matrix.apply(
|
|
150
|
+
lambda x: calculate_flows_y(x[x <= selection_range]), axis=1
|
|
164
151
|
)
|
|
165
|
-
|
|
166
152
|
temp_destination_matrix = temp_destination_matrix.fillna(0)
|
|
167
|
-
temp_destination_matrix =
|
|
153
|
+
temp_destination_matrix = temp_destination_matrix.apply(balance_flows_to_demands, axis=0)
|
|
168
154
|
temp_destination_matrix = temp_destination_matrix.fillna(0)
|
|
169
155
|
temp_destination_matrix_aligned = temp_destination_matrix.reindex(
|
|
170
156
|
index=destination_matrix.index, columns=destination_matrix.columns, fill_value=0
|
|
@@ -51,7 +51,7 @@ def graph_to_gdf(
|
|
|
51
51
|
"""
|
|
52
52
|
Converts nx graph to gpd.GeoDataFrame as edges.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Args:
|
|
55
55
|
graph (nx.MultiDiGraph):
|
|
56
56
|
The graph to convert.
|
|
57
57
|
edges (bool):
|
|
@@ -98,7 +98,7 @@ def gdf_to_graph(
|
|
|
98
98
|
Intersections are optionally checked and merged. Attributes from the original GeoDataFrame
|
|
99
99
|
can be projected onto the graph edges using spatial matching.
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
Args:
|
|
102
102
|
gdf (gpd.GeoDataFrame): A GeoDataFrame containing at least one LineString geometry.
|
|
103
103
|
project_gdf_attr (bool): If True, attributes from the input GeoDataFrame will be spatially
|
|
104
104
|
projected onto the resulting graph edges. This can be an expensive operation for large datasets.
|
|
@@ -193,7 +193,7 @@ def get_closest_nodes_from_gdf(gdf: gpd.GeoDataFrame, nx_graph: nx.Graph) -> tup
|
|
|
193
193
|
"""
|
|
194
194
|
Finds the closest graph nodes to the geometries in a GeoDataFrame.
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
Args
|
|
197
197
|
----------
|
|
198
198
|
gdf : gpd.GeoDataFrame
|
|
199
199
|
GeoDataFrame with geometries for which the nearest graph nodes will be found.
|
|
@@ -228,7 +228,7 @@ def remove_weakly_connected_nodes(graph: nx.DiGraph) -> nx.DiGraph:
|
|
|
228
228
|
"""
|
|
229
229
|
Removes all nodes that are not part of the largest strongly connected component in the graph.
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
Args
|
|
232
232
|
----------
|
|
233
233
|
graph : nx.DiGraph
|
|
234
234
|
A directed NetworkX graph.
|
|
@@ -275,7 +275,7 @@ def reverse_graph(nx_graph: nx.Graph, weight: str) -> tuple[nx.Graph, nx.DiGraph
|
|
|
275
275
|
For directed graphs, the function returns a new graph with all edge directions reversed,
|
|
276
276
|
preserving the specified edge weight.
|
|
277
277
|
|
|
278
|
-
|
|
278
|
+
Args
|
|
279
279
|
----------
|
|
280
280
|
nx_graph : nx.Graph
|
|
281
281
|
Input NetworkX graph (can be directed or undirected).
|
|
@@ -4,7 +4,6 @@ from multiprocessing import cpu_count
|
|
|
4
4
|
import geopandas as gpd
|
|
5
5
|
import numpy as np
|
|
6
6
|
import pandas as pd
|
|
7
|
-
from pandarallel import pandarallel
|
|
8
7
|
from shapely import LineString, MultiPolygon, Point, Polygon
|
|
9
8
|
from shapely.ops import unary_union
|
|
10
9
|
from tqdm.contrib.concurrent import process_map
|
|
@@ -28,7 +27,7 @@ def get_visibility_accurate(
|
|
|
28
27
|
"""
|
|
29
28
|
Function to get accurate visibility from a given point to buildings within a given distance.
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
Args:
|
|
32
31
|
point_from (Point | gpd.GeoDataFrame):
|
|
33
32
|
The point or GeoDataFrame with 1 point from which the line of sight is drawn.
|
|
34
33
|
If Point is provided it should be in the same crs as obstacles.
|
|
@@ -165,7 +164,7 @@ def get_visibility(
|
|
|
165
164
|
"""
|
|
166
165
|
Function to get a quick estimate of visibility from a given point to buildings within a given distance.
|
|
167
166
|
|
|
168
|
-
|
|
167
|
+
Args:
|
|
169
168
|
point_from (Point | gpd.GeoDataFrame):
|
|
170
169
|
The point or GeoDataFrame with 1 point from which the line of sight is drawn.
|
|
171
170
|
If Point is provided it should be in the same crs as obstacles.
|
|
@@ -239,7 +238,7 @@ def get_visibilities_from_points(
|
|
|
239
238
|
"""
|
|
240
239
|
Calculate visibility polygons from a set of points considering obstacles within a specified view distance.
|
|
241
240
|
|
|
242
|
-
|
|
241
|
+
Args:
|
|
243
242
|
points (gpd.GeoDataFrame):
|
|
244
243
|
GeoDataFrame containing the points from which visibility is calculated.
|
|
245
244
|
obstacles (gpd.GeoDataFrame):
|
|
@@ -297,7 +296,7 @@ def calculate_visibility_catchment_area(
|
|
|
297
296
|
This function is designed to work with at least 1000 points spaced 10-20 meters apart for optimal results.
|
|
298
297
|
Points can be generated using a road graph.
|
|
299
298
|
|
|
300
|
-
|
|
299
|
+
Args:
|
|
301
300
|
points (gpd.GeoDataFrame): GeoDataFrame containing the points from which visibility is calculated.
|
|
302
301
|
obstacles (gpd.GeoDataFrame): GeoDataFrame containing the obstacles that block visibility.
|
|
303
302
|
view_distance (int | float): The maximum distance from each point within which visibility is calculated.
|
|
@@ -313,21 +312,12 @@ def calculate_visibility_catchment_area(
|
|
|
313
312
|
return x
|
|
314
313
|
|
|
315
314
|
def calc_group_factor(x):
|
|
316
|
-
# pylint: disable-next=redefined-outer-name,reimported,import-outside-toplevel
|
|
317
|
-
import numpy as np
|
|
318
|
-
|
|
319
315
|
return np.mean(x.new_ratio) * x.count_n
|
|
320
316
|
|
|
321
317
|
def unary_union_groups(x):
|
|
322
|
-
# pylint: disable-next=redefined-outer-name,reimported,import-outside-toplevel
|
|
323
|
-
from shapely import MultiPolygon
|
|
324
|
-
|
|
325
|
-
# pylint: disable-next=redefined-outer-name,reimported,import-outside-toplevel
|
|
326
|
-
from shapely.ops import unary_union
|
|
327
|
-
|
|
328
318
|
return unary_union(MultiPolygon(list(x["geometry"])).buffer(0))
|
|
329
319
|
|
|
330
|
-
|
|
320
|
+
raise NotImplementedError("This method is temporarily unsupported.")
|
|
331
321
|
|
|
332
322
|
local_crs = obstacles.estimate_utm_crs()
|
|
333
323
|
obstacles = obstacles.to_crs(local_crs)
|
|
@@ -370,7 +360,7 @@ def calculate_visibility_catchment_area(
|
|
|
370
360
|
all_in["count_n"] = all_in["index_right"].apply(len)
|
|
371
361
|
|
|
372
362
|
logger.info("Calculating intersection's parameters")
|
|
373
|
-
all_in["factor"] = all_in.parallel_apply(calc_group_factor, axis=1)
|
|
363
|
+
# all_in["factor"] = all_in.parallel_apply(calc_group_factor, axis=1) # TODO replace pandarallel methods
|
|
374
364
|
threshold = all_in["factor"].quantile(0.3)
|
|
375
365
|
all_in = all_in[all_in["factor"] > threshold]
|
|
376
366
|
|
|
@@ -378,7 +368,9 @@ def calculate_visibility_catchment_area(
|
|
|
378
368
|
min_max_normalization(np.sqrt(all_in["factor"].values), new_min=1, new_max=5)
|
|
379
369
|
).astype(int)
|
|
380
370
|
logger.info("Calculating normalized groups geometry...")
|
|
381
|
-
all_in =
|
|
371
|
+
all_in = (
|
|
372
|
+
all_in.groupby("factor_normalized").parallel_apply(unary_union_groups).reset_index()
|
|
373
|
+
) # TODO replace pandarallel methods
|
|
382
374
|
all_in = gpd.GeoDataFrame(data=all_in.rename(columns={0: "geometry"}), geometry="geometry", crs=32636)
|
|
383
375
|
|
|
384
376
|
all_in = all_in.explode(index_parts=True).reset_index(drop=True)
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ObjectNat
|
|
3
|
+
Version: 1.3.0
|
|
4
|
+
Summary: ObjectNat is an open-source library created for geospatial analysis created by IDU team
|
|
5
|
+
License: BSD-3-Clause
|
|
6
|
+
Author: DDonnyy
|
|
7
|
+
Author-email: 63115678+DDonnyy@users.noreply.github.com
|
|
8
|
+
Requires-Python: >=3.11,<3.13
|
|
9
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Requires-Dist: geopandas (>=1.0.1,<2.0.0)
|
|
14
|
+
Requires-Dist: loguru (>=0.7.3,<0.8.0)
|
|
15
|
+
Requires-Dist: networkx (>=3.4.2,<4.0.0)
|
|
16
|
+
Requires-Dist: numpy (>=2.1.3,<3.0.0)
|
|
17
|
+
Requires-Dist: pandas (>=2.2.0,<3.0.0)
|
|
18
|
+
Requires-Dist: scikit-learn (>=1.4.0,<2.0.0)
|
|
19
|
+
Requires-Dist: tqdm (>=4.66.2,<5.0.0)
|
|
20
|
+
Description-Content-Type: text/x-rst
|
|
21
|
+
|
|
22
|
+
ObjectNat
|
|
23
|
+
=========
|
|
24
|
+
|
|
25
|
+
Object-oriented Network Analysis Tools
|
|
26
|
+
--------------------------------------
|
|
27
|
+
|
|
28
|
+
.. |badge-black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
|
29
|
+
:target: https://github.com/psf/black
|
|
30
|
+
:alt: Code style: black
|
|
31
|
+
|
|
32
|
+
.. |badge-pypi| image:: https://img.shields.io/pypi/v/objectnat.svg
|
|
33
|
+
:target: https://pypi.org/project/objectnat/
|
|
34
|
+
:alt: PyPI version
|
|
35
|
+
|
|
36
|
+
.. |badge-ci| image:: https://github.com/IDUclub/ObjectNat/actions/workflows/ci_pipeline.yml/badge.svg
|
|
37
|
+
:target: https://github.com/IDUclub/ObjectNat/actions/workflows/ci_pipeline.yml
|
|
38
|
+
:alt: CI
|
|
39
|
+
|
|
40
|
+
.. |badge-codecov| image:: https://codecov.io/gh/DDonnyy/ObjectNat/graph/badge.svg?token=K6JFSJ02GU
|
|
41
|
+
:target: https://codecov.io/gh/DDonnyy/ObjectNat
|
|
42
|
+
:alt: Coverage
|
|
43
|
+
|
|
44
|
+
.. |badge-license| image:: https://img.shields.io/badge/license-BSD--3--Clause-blue.svg
|
|
45
|
+
:target: https://opensource.org/licenses/BSD-3-Clause
|
|
46
|
+
:alt: License
|
|
47
|
+
|
|
48
|
+
.. |badge-docs| image:: https://img.shields.io/badge/docs-latest-4aa0d5?logo=readthedocs
|
|
49
|
+
:target: https://iduclub.github.io/ObjectNat/
|
|
50
|
+
:alt: Docs
|
|
51
|
+
|
|
52
|
+
|badge-black| |badge-pypi| |badge-ci| |badge-codecov| |badge-license| |badge-docs|
|
|
53
|
+
|
|
54
|
+
`РИДМИ (Russian) <https://github.com/IDUclub/ObjectNat/blob/main/README_RU.rst>`__
|
|
55
|
+
|
|
56
|
+
.. image:: https://raw.githubusercontent.com/IDUclub/ObjectNat/main/docs/_static/ONlogo.svg
|
|
57
|
+
:align: center
|
|
58
|
+
:width: 400
|
|
59
|
+
:alt: ObjectNat logo
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
**ObjectNat** is an open-source library developed by the **IDU** team
|
|
63
|
+
for spatial and network analysis in urban studies.
|
|
64
|
+
The library provides tools for analyzing **accessibility**, **visibility**,
|
|
65
|
+
**noise propagation**, and **service provision**.
|
|
66
|
+
----
|
|
67
|
+
|
|
68
|
+
Key Features
|
|
69
|
+
------------
|
|
70
|
+
|
|
71
|
+
Each feature includes a **Jupyter Notebook example** and **full documentation**.
|
|
72
|
+
|
|
73
|
+
1. **Isochrones and Transport Accessibility**
|
|
74
|
+
|
|
75
|
+
Isochrones represent areas reachable from an origin point within a specified time along a transport network.
|
|
76
|
+
This feature allows the analysis of transport accessibility using pedestrian, road,
|
|
77
|
+
public transport, or multimodal graphs.
|
|
78
|
+
|
|
79
|
+
The library supports several methods for building isochrones:
|
|
80
|
+
|
|
81
|
+
- **Basic isochrones**: display a single zone reachable within a specified time.
|
|
82
|
+
- **Step isochrones**: divide the accessibility area into time intervals (e.g., 3, 5, 10 minutes).
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
📘 `Example <https://iduclub.github.io/ObjectNat/methods/examples/isochrones.html>`__
|
|
86
|
+
🔗 `Documentation <https://iduclub.github.io/ObjectNat/methods/isochrones.html>`__
|
|
87
|
+
|
|
88
|
+
2. **Graph Coverage Zones from Points**
|
|
89
|
+
|
|
90
|
+
A function for generating **coverage areas** from a set of origin points using a transport network.
|
|
91
|
+
It computes the area reachable from each point by **travel time** or **distance**,
|
|
92
|
+
then builds polygons using **Voronoi diagrams** and clips them by a given boundary if specified.
|
|
93
|
+
|
|
94
|
+
📘 `Example <https://iduclub.github.io/ObjectNat/methods/examples/coverage.html>`__
|
|
95
|
+
🔗 `Documentation <https://iduclub.github.io/ObjectNat/methods/coverage.html>`__
|
|
96
|
+
|
|
97
|
+
3. **Service Provision Analysis**
|
|
98
|
+
|
|
99
|
+
A function to evaluate how well residential buildings and their populations are provided
|
|
100
|
+
with services (e.g., schools, clinics) that have limited **capacity**
|
|
101
|
+
and a defined **accessibility threshold** (in minutes or meters).
|
|
102
|
+
The function models the **balance between supply and demand**,
|
|
103
|
+
assessing how well services meet the needs of nearby buildings within an acceptable time.
|
|
104
|
+
|
|
105
|
+
📘 `Example <https://iduclub.github.io/ObjectNat/methods/examples/provision.html>`__
|
|
106
|
+
🔗 `Documentation <https://iduclub.github.io/ObjectNat/methods/provision.html>`__
|
|
107
|
+
|
|
108
|
+
4. **Visibility Analysis**
|
|
109
|
+
|
|
110
|
+
A function for evaluating visibility from a given point or set of points to nearby buildings within a given radius.
|
|
111
|
+
It is used to assess visual accessibility in urban environments.
|
|
112
|
+
A module is also implemented for computing **visibility coverage zones**
|
|
113
|
+
using a dense observer grid (recommended ~1000 points with a 10–20 m spacing).
|
|
114
|
+
Points can be generated along the transport network and distributed across its edges.
|
|
115
|
+
|
|
116
|
+
📘 `Example <https://iduclub.github.io/ObjectNat/methods/examples/visibility.html>`__
|
|
117
|
+
🔗 `Documentation <https://iduclub.github.io/ObjectNat/methods/visibility.html>`__
|
|
118
|
+
|
|
119
|
+
5. **Noise Simulation & Noise Frame**
|
|
120
|
+
|
|
121
|
+
Simulation of noise propagation from sources, taking into account **obstacles**, **vegetation**,
|
|
122
|
+
and **environmental factors**.
|
|
123
|
+
|
|
124
|
+
📘 `Example <https://iduclub.github.io/ObjectNat/methods/examples/noise.html>`__
|
|
125
|
+
🔗 `Documentation <https://iduclub.github.io/ObjectNat/methods/noise.html>`__
|
|
126
|
+
🧠 `Detailed theory <https://github.com/DDonnyy/ObjectNat/wiki/Noise-simulation>`__
|
|
127
|
+
|
|
128
|
+
6. **Point Clusterization**
|
|
129
|
+
|
|
130
|
+
A function for constructing **cluster polygons** based on a set of points using:
|
|
131
|
+
|
|
132
|
+
- Minimum **distance** between points.
|
|
133
|
+
- Minimum **number of points** in a cluster.
|
|
134
|
+
|
|
135
|
+
The function can also compute the **ratio of service types** in each cluster
|
|
136
|
+
for spatial analysis of service composition.
|
|
137
|
+
|
|
138
|
+
📘 `Example <https://iduclub.github.io/ObjectNat/methods/examples/clustering.html>`__
|
|
139
|
+
🔗 `Documentation <https://iduclub.github.io/ObjectNat/methods/clustering.html>`__
|
|
140
|
+
|
|
141
|
+
----
|
|
142
|
+
|
|
143
|
+
City Graphs via *IduEdu*
|
|
144
|
+
------------------------
|
|
145
|
+
|
|
146
|
+
For optimal performance, **ObjectNat** is recommended to be used with graphs
|
|
147
|
+
created by the `IduEdu <https://github.com/IDUclub/IduEdu>`_ library.
|
|
148
|
+
|
|
149
|
+
**IduEdu** is an open-source Python library designed for building and processing
|
|
150
|
+
complex urban networks based on OpenStreetMap data.
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
**IduEdu** can be installed via ``pip``::
|
|
154
|
+
|
|
155
|
+
pip install IduEdu
|
|
156
|
+
|
|
157
|
+
Example usage::
|
|
158
|
+
|
|
159
|
+
from iduedu import get_4326_boundary, get_intermodal_graph
|
|
160
|
+
|
|
161
|
+
poly = get_4326_boundary(osm_id=1114252)
|
|
162
|
+
G_intermodal = get_intermodal_graph(territory=poly, clip_by_territory=True)
|
|
163
|
+
|
|
164
|
+
----
|
|
165
|
+
|
|
166
|
+
Installation
|
|
167
|
+
------------
|
|
168
|
+
|
|
169
|
+
**ObjectNat** can be installed via ``pip``::
|
|
170
|
+
|
|
171
|
+
pip install ObjectNat
|
|
172
|
+
|
|
173
|
+
----
|
|
174
|
+
|
|
175
|
+
Configuration
|
|
176
|
+
-------------
|
|
177
|
+
|
|
178
|
+
You can adjust logging and progress bar output using the config module::
|
|
179
|
+
|
|
180
|
+
from objectnat import config
|
|
181
|
+
|
|
182
|
+
config.change_logger_lvl("INFO") # mute debug logs
|
|
183
|
+
config.set_enable_tqdm(False) # disable tqdm progress bars
|
|
184
|
+
|
|
185
|
+
----
|
|
186
|
+
|
|
187
|
+
Contacts
|
|
188
|
+
--------
|
|
189
|
+
|
|
190
|
+
- `NCCR <https://actcognitive.org/>`_ — National Center for Cognitive Research
|
|
191
|
+
- `IDU <https://idu.itmo.ru/>`_ — Institute of Design and Urban Studies
|
|
192
|
+
- `Natalya Chichkova <https://t.me/nancy_nat>`_ — Project Manager
|
|
193
|
+
- `Danila Oleynikov (Donny) <https://t.me/ddonny_dd>`_ — Lead Software Engineer
|
|
194
|
+
|
|
195
|
+
----
|
|
196
|
+
|
|
197
|
+
Publications
|
|
198
|
+
------------
|
|
199
|
+
|
|
200
|
+
Coming soon.
|
|
201
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
objectnat/__init__.py,sha256=VZ0TaIuB73EOl5Tin4Cs6-TCEOgBImCNYozwHD3bI_g,248
|
|
2
|
+
objectnat/_api.py,sha256=tor-BVRvbVeqGUrFlNc3pVJ_N3gpyDgMpxQfHgV5Y1U,725
|
|
3
|
+
objectnat/_config.py,sha256=OVOw0yORiirljN866xs45OLcRFhOV-xznZU6kI58ScI,1188
|
|
4
|
+
objectnat/_version.py,sha256=5O6-qxBThm-gXXX0GfrBRhqSXFn9X_TTRdp9PZJHO6o,19
|
|
5
|
+
objectnat/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
objectnat/methods/coverage_zones/__init__.py,sha256=iebp-RbU60GRjIosd0Yvypz8-XNCme7f1Aah2nqynig,164
|
|
7
|
+
objectnat/methods/coverage_zones/graph_coverage.py,sha256=0ct66u6YblxcQmVtqpa-yX2xpb83Jagxxd6HkgCBVP4,4264
|
|
8
|
+
objectnat/methods/coverage_zones/radius_voronoi_coverage.py,sha256=okU6rcCwyNOXbHORYxPyh_j_J2LnQUEiD1ROiTYSndA,1627
|
|
9
|
+
objectnat/methods/coverage_zones/stepped_coverage.py,sha256=QVzRP_IKqjuWBWGYTnd4FFKzWtL_xxMXvO-_GpDlL2Y,5619
|
|
10
|
+
objectnat/methods/isochrones/__init__.py,sha256=inpyVZ4Mfz53k8HxW1zz3XFAqOE1xOQfpnfsS2f-a58,91
|
|
11
|
+
objectnat/methods/isochrones/isochrone_utils.py,sha256=84OLlj0tbnJAg7H_k0Il3wRHzdFhlMNU4G3w8z1_x6c,7037
|
|
12
|
+
objectnat/methods/isochrones/isochrones.py,sha256=qMBH8GMbfWcTgw-PMcX4FEIyLc2EB_p7Uox6_Qq0a8g,11465
|
|
13
|
+
objectnat/methods/noise/__init__.py,sha256=fXlEuZ1z9Pw7cP4YRcbLaQnEgJIRkFdr2dTe2ypMhgg,189
|
|
14
|
+
objectnat/methods/noise/noise_init_data.py,sha256=jMFOqi5T7FYqcKjYg8AOIr_VNYbRZQks9wGULYNaMlw,513
|
|
15
|
+
objectnat/methods/noise/noise_reduce.py,sha256=KuVYLF5-hQGDqki6KU4BtgEufQ8tkOZvBWjFFjgw4m4,7040
|
|
16
|
+
objectnat/methods/noise/noise_simulation.py,sha256=dO94kumoK--OVAqQ-rbWTC8M_2r-j5SIC_dDM_yrCrA,22360
|
|
17
|
+
objectnat/methods/noise/noise_simulation_simplified.py,sha256=J2vt2Qj5wYHdo2AkHJhlgd8SAsOEFRA0SMOhZMJJ14s,11058
|
|
18
|
+
objectnat/methods/point_clustering/__init__.py,sha256=SLRcjLZ_NyQvKBu5SHZTt7snCWpa0n7HlJqxlACJMyM,62
|
|
19
|
+
objectnat/methods/point_clustering/cluster_points_in_polygons.py,sha256=Bu5RONhMCWivCCl8dZjh9DlhgEV-BGy0T1DLY8sldV4,5144
|
|
20
|
+
objectnat/methods/provision/__init__.py,sha256=a8h_Md3rBnmXK0o-MuaYiTIMRFMFu0bRZJ7ZP0P2hDQ,81
|
|
21
|
+
objectnat/methods/provision/provision.py,sha256=IL0PWNpm6fdzdyGlzRbDl-i4lU-OVLhBt0uGuH8qfmw,9730
|
|
22
|
+
objectnat/methods/provision/provision_exceptions.py,sha256=ofZOEv0jIZRNBgfrwqphkIHUWaGDo2WOa-hf7EuCM1g,1746
|
|
23
|
+
objectnat/methods/provision/provision_model.py,sha256=e8hfolPq8LK3SLI1nXffXqjtHgG0VWvwV7VFMzPbYzI,14024
|
|
24
|
+
objectnat/methods/utils/__init__.py,sha256=1jhuPYwWwlNALtiO6n5IF-5z85nLKX4aC-ZaI85LEMM,53
|
|
25
|
+
objectnat/methods/utils/geom_utils.py,sha256=lCuMayV1iu8WJVVtwbswxfvl4B4dwxAqRcFfF1jd98M,6558
|
|
26
|
+
objectnat/methods/utils/graph_utils.py,sha256=5GI7WwwkK7tA72gpoMMMgoqugrIeomJK-OC5bjCT1dk,12637
|
|
27
|
+
objectnat/methods/utils/math_utils.py,sha256=AoOx0hjIRfir-Fw2s2t6PubfybHuVoLKLbv-SJE0BGs,865
|
|
28
|
+
objectnat/methods/visibility/__init__.py,sha256=vgrN4OTnap4fYfqm5GgNi787TZ85GbS9LbGfTIyTg-I,167
|
|
29
|
+
objectnat/methods/visibility/visibility_analysis.py,sha256=Y76mRbYHBy8O1iUaOTBIA_Wy6OHJL8cKfXAqRWWe7_8,20504
|
|
30
|
+
objectnat-1.3.0.dist-info/LICENSE.txt,sha256=gI3AMqmBc4AdVW3_l1YV4pBBhle1fvAqiK62uA543uI,1526
|
|
31
|
+
objectnat-1.3.0.dist-info/METADATA,sha256=S0F3otCDjST9WJzOvLXYwzfFCvi-vhVCSg0UYjyEiY4,7370
|
|
32
|
+
objectnat-1.3.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
33
|
+
objectnat-1.3.0.dist-info/RECORD,,
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: ObjectNat
|
|
3
|
-
Version: 1.2.1
|
|
4
|
-
Summary: ObjectNat is an open-source library created for geospatial analysis created by IDU team
|
|
5
|
-
License: BSD-3-Clause
|
|
6
|
-
Author: DDonnyy
|
|
7
|
-
Author-email: 63115678+DDonnyy@users.noreply.github.com
|
|
8
|
-
Requires-Python: >=3.10,<3.13
|
|
9
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Requires-Dist: geopandas (>=1.0.1,<2.0.0)
|
|
15
|
-
Requires-Dist: loguru (>=0.7.3,<0.8.0)
|
|
16
|
-
Requires-Dist: networkx (>=3.4.2,<4.0.0)
|
|
17
|
-
Requires-Dist: numpy (>=2.1.3,<3.0.0)
|
|
18
|
-
Requires-Dist: pandarallel (>=1.6.5,<2.0.0)
|
|
19
|
-
Requires-Dist: pandas (>=2.2.0,<3.0.0)
|
|
20
|
-
Requires-Dist: scikit-learn (>=1.4.0,<2.0.0)
|
|
21
|
-
Requires-Dist: tqdm (>=4.66.2,<5.0.0)
|
|
22
|
-
Description-Content-Type: text/markdown
|
|
23
|
-
|
|
24
|
-
# ObjectNat
|
|
25
|
-
|
|
26
|
-
[](https://github.com/psf/black)
|
|
27
|
-
[](https://pypi.org/project/objectnat/)
|
|
28
|
-
[](https://github.com/DDonnyy/ObjecNat/actions/workflows/ci_pipeline.yml)
|
|
29
|
-
[](https://codecov.io/gh/DDonnyy/ObjectNat)
|
|
30
|
-
[](https://opensource.org/licenses/MIT)
|
|
31
|
-
|
|
32
|
-
- [РИДМИ (Russian)](README_ru.md)
|
|
33
|
-
<p align="center">
|
|
34
|
-
<img src="https://github.com/user-attachments/assets/ed0f226e-1728-4659-9e21-b4d499e703cd" alt="logo" width="400">
|
|
35
|
-
</p>
|
|
36
|
-
|
|
37
|
-
#### **ObjectNat** is an open-source library created for geospatial analysis created by **IDU team**
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Features and How to Use
|
|
42
|
-
|
|
43
|
-
Each feature is accompanied by a Jupyter notebook example and full documentation.
|
|
44
|
-
|
|
45
|
-
1. **[Isochrones and Transport Accessibility](./examples/isochrone_generator.ipynb)**
|
|
46
|
-
Analyze areas reachable within a given time along a transport network.
|
|
47
|
-
📄 [See documentation](https://ddonnyy.github.io/ObjectNat/latest/usage/isochrones.html)
|
|
48
|
-
|
|
49
|
-
2. **[Coverage Zones](./examples/coverage_zones.ipynb)**
|
|
50
|
-
Build zones of reachability for each point using routing or simple radius.
|
|
51
|
-
📄 [See documentation](https://ddonnyy.github.io/ObjectNat/latest/usage/coverage.html)
|
|
52
|
-
|
|
53
|
-
3. **[Service Provision Analysis](./examples/calculate_provision.ipynb)**
|
|
54
|
-
Evaluate service availability and model demand-supply balance.
|
|
55
|
-
📄 [See documentation](https://ddonnyy.github.io/ObjectNat/latest/usage/provision.html)
|
|
56
|
-
|
|
57
|
-
4. **[Visibility Analysis](./examples/visibility_analysis.ipynb)**
|
|
58
|
-
Estimate visibility to nearby buildings from selected points.
|
|
59
|
-
📄 [See documentation](https://ddonnyy.github.io/ObjectNat/latest/usage/visibility.html)
|
|
60
|
-
|
|
61
|
-
5. **[Noise Simulation](./examples/noise_simulation.ipynb)**
|
|
62
|
-
Simulate noise propagation considering obstacles and environment.
|
|
63
|
-
📄 [See documentation](https://ddonnyy.github.io/ObjectNat/latest/usage/noise.html)
|
|
64
|
-
🔗 [Detailed theory in the Wiki](https://github.com/DDonnyy/ObjectNat/wiki/Noise-simulation)
|
|
65
|
-
|
|
66
|
-
6. **[Point Clusterization](./examples/point_clusterization.ipynb)**
|
|
67
|
-
Group nearby points into clusters and analyze service composition.
|
|
68
|
-
📄 [See documentation](https://ddonnyy.github.io/ObjectNat/latest/usage/clustering.html)
|
|
69
|
-
|
|
70
|
-
---
|
|
71
|
-
|
|
72
|
-
## City graphs
|
|
73
|
-
|
|
74
|
-
To ensure optimal performance of ObjectNat's geospatial analysis functions, it's recommended to utilize urban graphs sourced from the [IduEdu](https://github.com/DDonnyy/IduEdu) library.
|
|
75
|
-
**IduEdu** is an open-source Python library designed for the creation and manipulation of complex city networks derived from OpenStreetMap data.
|
|
76
|
-
|
|
77
|
-
**IduEdu** can be installed with ``pip``:
|
|
78
|
-
```
|
|
79
|
-
pip install IduEdu
|
|
80
|
-
```
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## Installation
|
|
84
|
-
|
|
85
|
-
**ObjectNat** can be installed with ``pip``:
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
pip install ObjectNat
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
### Configuration changes
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
from objectnat import config
|
|
97
|
-
|
|
98
|
-
config.change_logger_lvl('INFO') # To mute all debug msgs
|
|
99
|
-
config.set_enable_tqdm(False) # To mute all tqdm's progress bars
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
## Contacts
|
|
105
|
-
|
|
106
|
-
- [NCCR](https://actcognitive.org/) - National Center for Cognitive Research
|
|
107
|
-
- [IDU](https://idu.itmo.ru/) - Institute of Design and Urban Studies
|
|
108
|
-
- [Natalya Chichkova](https://t.me/nancy_nat) - project manager
|
|
109
|
-
- [Danila Oleynikov (Donny)](https://t.me/ddonny_dd) - lead software engineer
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## Publications
|
|
114
|
-
|
|
115
|
-
_Coming soon._
|
objectnat-1.2.1.dist-info/RECORD
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
objectnat/__init__.py,sha256=VZ0TaIuB73EOl5Tin4Cs6-TCEOgBImCNYozwHD3bI_g,248
|
|
2
|
-
objectnat/_api.py,sha256=tor-BVRvbVeqGUrFlNc3pVJ_N3gpyDgMpxQfHgV5Y1U,725
|
|
3
|
-
objectnat/_config.py,sha256=bt9wohg8Hjwxril5PzotO5fjaIcown9vHq6kvXa3tzo,1353
|
|
4
|
-
objectnat/_version.py,sha256=1ZlkgkmphZGnGYOHAq5WqbnpVdTi39MMaQJd1wOEceI,19
|
|
5
|
-
objectnat/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
objectnat/methods/coverage_zones/__init__.py,sha256=iebp-RbU60GRjIosd0Yvypz8-XNCme7f1Aah2nqynig,164
|
|
7
|
-
objectnat/methods/coverage_zones/graph_coverage.py,sha256=e4seTrhOTqEZ8DhE9xiH_fe-TwPNZXH3ujEq87CuEA0,4190
|
|
8
|
-
objectnat/methods/coverage_zones/radius_voronoi_coverage.py,sha256=A-O6WhgpJTizIHGiJ9JuP882iHbNejh0EW10H493kDM,1631
|
|
9
|
-
objectnat/methods/coverage_zones/stepped_coverage.py,sha256=e-zNE0JP4Q1Crb-JEbIZc0dYFzZXle2LjTxJPGS-7XE,5659
|
|
10
|
-
objectnat/methods/isochrones/__init__.py,sha256=inpyVZ4Mfz53k8HxW1zz3XFAqOE1xOQfpnfsS2f-a58,91
|
|
11
|
-
objectnat/methods/isochrones/isochrone_utils.py,sha256=84OLlj0tbnJAg7H_k0Il3wRHzdFhlMNU4G3w8z1_x6c,7037
|
|
12
|
-
objectnat/methods/isochrones/isochrones.py,sha256=VgpwubX7VEFStXyXM0B58cpFoCn3AboVekIU8GtXWls,11573
|
|
13
|
-
objectnat/methods/noise/__init__.py,sha256=fXlEuZ1z9Pw7cP4YRcbLaQnEgJIRkFdr2dTe2ypMhgg,189
|
|
14
|
-
objectnat/methods/noise/noise_init_data.py,sha256=jMFOqi5T7FYqcKjYg8AOIr_VNYbRZQks9wGULYNaMlw,513
|
|
15
|
-
objectnat/methods/noise/noise_reduce.py,sha256=KuVYLF5-hQGDqki6KU4BtgEufQ8tkOZvBWjFFjgw4m4,7040
|
|
16
|
-
objectnat/methods/noise/noise_simulation.py,sha256=-2pTzV-nShJfhgPg4AejKlMSvlTlnQDBCngLPZhFX8w,22389
|
|
17
|
-
objectnat/methods/noise/noise_simulation_simplified.py,sha256=igj4hI3rPDdKYgabq0aH0Evhy2jg-uTstLSqFjx2VSw,11094
|
|
18
|
-
objectnat/methods/point_clustering/__init__.py,sha256=SLRcjLZ_NyQvKBu5SHZTt7snCWpa0n7HlJqxlACJMyM,62
|
|
19
|
-
objectnat/methods/point_clustering/cluster_points_in_polygons.py,sha256=reY5ekJrAJUdWwVMvvZBp5T34H4K2V4mt3rQuyoUYPQ,5193
|
|
20
|
-
objectnat/methods/provision/__init__.py,sha256=a8h_Md3rBnmXK0o-MuaYiTIMRFMFu0bRZJ7ZP0P2hDQ,81
|
|
21
|
-
objectnat/methods/provision/provision.py,sha256=4NpZiUBllflt1woIOXAf1zh7MAB5Q6DegN67fXpJHu0,4947
|
|
22
|
-
objectnat/methods/provision/provision_exceptions.py,sha256=ofZOEv0jIZRNBgfrwqphkIHUWaGDo2WOa-hf7EuCM1g,1746
|
|
23
|
-
objectnat/methods/provision/provision_model.py,sha256=texAsht4DeBHveJpp6FEvsA4nE13kbf6iYXZD-xK7mw,14866
|
|
24
|
-
objectnat/methods/utils/__init__.py,sha256=1jhuPYwWwlNALtiO6n5IF-5z85nLKX4aC-ZaI85LEMM,53
|
|
25
|
-
objectnat/methods/utils/geom_utils.py,sha256=lCuMayV1iu8WJVVtwbswxfvl4B4dwxAqRcFfF1jd98M,6558
|
|
26
|
-
objectnat/methods/utils/graph_utils.py,sha256=aOtFZbuPAiGhkglMJBi9xcsb8XBbVFW7535DrhjWx1w,12667
|
|
27
|
-
objectnat/methods/utils/math_utils.py,sha256=Gcoi7MOmoWlu0ao5465GLtnP2eW4ZXKsGU9NyvUDR6E,871
|
|
28
|
-
objectnat/methods/visibility/__init__.py,sha256=vgrN4OTnap4fYfqm5GgNi787TZ85GbS9LbGfTIyTg-I,167
|
|
29
|
-
objectnat/methods/visibility/visibility_analysis.py,sha256=nXcDQ9zewpM4FFSXuYophUNsh6wiHSIHjM88vV6Iv_U,20843
|
|
30
|
-
objectnat-1.2.1.dist-info/LICENSE.txt,sha256=gI3AMqmBc4AdVW3_l1YV4pBBhle1fvAqiK62uA543uI,1526
|
|
31
|
-
objectnat-1.2.1.dist-info/METADATA,sha256=8s9EHELAA7LodH5DMtuKTF1JN3NyyBbWINlzqzKDCtw,4510
|
|
32
|
-
objectnat-1.2.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
33
|
-
objectnat-1.2.1.dist-info/RECORD,,
|
|
File without changes
|