ObjectNat 0.2.1__py3-none-any.whl → 0.2.2__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/_version.py +1 -1
- objectnat/methods/cluster_points_in_polygons.py +15 -9
- objectnat/methods/isochrones.py +7 -4
- objectnat/methods/living_buildings_osm.py +26 -28
- objectnat/methods/provision/city_provision.py +15 -19
- objectnat/methods/provision/provision.py +13 -0
- {objectnat-0.2.1.dist-info → objectnat-0.2.2.dist-info}/METADATA +2 -2
- {objectnat-0.2.1.dist-info → objectnat-0.2.2.dist-info}/RECORD +10 -10
- {objectnat-0.2.1.dist-info → objectnat-0.2.2.dist-info}/LICENSE.txt +0 -0
- {objectnat-0.2.1.dist-info → objectnat-0.2.2.dist-info}/WHEEL +0 -0
objectnat/_version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "0.2.
|
|
1
|
+
VERSION = "0.2.2"
|
|
@@ -21,10 +21,10 @@ def _get_cluster(services_select, min_dist, min_point, method):
|
|
|
21
21
|
return services_select
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def _get_service_ratio(loc):
|
|
24
|
+
def _get_service_ratio(loc, service_code_column):
|
|
25
25
|
all_services = loc.shape[0]
|
|
26
|
-
loc[
|
|
27
|
-
services_count = loc.groupby(
|
|
26
|
+
loc[service_code_column] = loc[service_code_column].astype(str)
|
|
27
|
+
services_count = loc.groupby(service_code_column).size()
|
|
28
28
|
return (services_count / all_services).round(2)
|
|
29
29
|
|
|
30
30
|
|
|
@@ -33,6 +33,7 @@ def get_clusters_polygon(
|
|
|
33
33
|
min_dist: float | int = 100,
|
|
34
34
|
min_point: int = 5,
|
|
35
35
|
method: Literal["DBSCAN", "HDBSCAN"] = "HDBSCAN",
|
|
36
|
+
service_code_column: str = "service_code",
|
|
36
37
|
) -> tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
37
38
|
"""
|
|
38
39
|
Generate cluster polygons for given points based on a specified minimum distance and minimum points per cluster.
|
|
@@ -49,7 +50,8 @@ def get_clusters_polygon(
|
|
|
49
50
|
Minimum number of points required to form a cluster. Defaults to 5.
|
|
50
51
|
method : Literal["DBSCAN", "HDBSCAN"], optional
|
|
51
52
|
The clustering method to use. Must be either "DBSCAN" or "HDBSCAN". Defaults to "HDBSCAN".
|
|
52
|
-
|
|
53
|
+
service_code_column : str, optional
|
|
54
|
+
Column, containing service type for relative ratio in clasterized polygons. Defaults to "service_code".
|
|
53
55
|
Returns
|
|
54
56
|
-------
|
|
55
57
|
tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]
|
|
@@ -72,17 +74,19 @@ def get_clusters_polygon(
|
|
|
72
74
|
|
|
73
75
|
services_select = _get_cluster(points, min_dist, min_point, method)
|
|
74
76
|
|
|
75
|
-
if
|
|
77
|
+
if service_code_column not in points.columns:
|
|
76
78
|
logger.warning(
|
|
77
|
-
"No
|
|
79
|
+
f"No {service_code_column} column in provided GeoDataFrame, cluster polygons will be without relative ratio"
|
|
78
80
|
)
|
|
79
|
-
points[
|
|
81
|
+
points[service_code_column] = service_code_column
|
|
80
82
|
|
|
81
83
|
services_normal = services_select[services_select["cluster"] != -1]
|
|
82
84
|
services_outlier = services_select[services_select["cluster"] == -1]
|
|
83
85
|
|
|
84
86
|
if len(services_normal) > 0:
|
|
85
|
-
cluster_service = services_normal.groupby("cluster", group_keys=True).apply(
|
|
87
|
+
cluster_service = services_normal.groupby("cluster", group_keys=True).apply(
|
|
88
|
+
_get_service_ratio, service_code_column=service_code_column
|
|
89
|
+
)
|
|
86
90
|
if isinstance(cluster_service, pd.Series):
|
|
87
91
|
cluster_service = cluster_service.unstack(level=1, fill_value=0)
|
|
88
92
|
|
|
@@ -98,7 +102,9 @@ def get_clusters_polygon(
|
|
|
98
102
|
new_clusters = list(range(clusters_outlier, clusters_outlier + len(services_outlier)))
|
|
99
103
|
services_outlier.loc[:, "cluster"] = new_clusters
|
|
100
104
|
|
|
101
|
-
cluster_service = services_outlier.groupby("cluster", group_keys=True).apply(
|
|
105
|
+
cluster_service = services_outlier.groupby("cluster", group_keys=True).apply(
|
|
106
|
+
_get_service_ratio, service_code_column=service_code_column
|
|
107
|
+
)
|
|
102
108
|
if isinstance(cluster_service, pd.Series):
|
|
103
109
|
cluster_service = cluster_service.unstack(level=1, fill_value=0)
|
|
104
110
|
|
objectnat/methods/isochrones.py
CHANGED
|
@@ -45,16 +45,19 @@ def get_accessibility_isochrones(
|
|
|
45
45
|
-------
|
|
46
46
|
tuple[gpd.GeoDataFrame, gpd.GeoDataFrame | None, gpd.GeoDataFrame | None]
|
|
47
47
|
A tuple containing:
|
|
48
|
-
- isochrones :
|
|
49
|
-
|
|
50
|
-
- public transport
|
|
48
|
+
- isochrones :
|
|
49
|
+
GeoDataFrame with the calculated isochrone geometries.
|
|
50
|
+
- public transport stops (if applicable) :
|
|
51
|
+
GeoDataFrame with public transport stops within the isochrone, or None if not applicable.
|
|
52
|
+
- public transport routes (if applicable) :
|
|
53
|
+
GeoDataFrame with public transport routes within the isochrone, or None if not applicable.
|
|
51
54
|
|
|
52
55
|
Examples
|
|
53
56
|
--------
|
|
54
57
|
>>> from iduedu import get_intermodal_graph
|
|
55
58
|
>>> graph = get_intermodal_graph(polygon=my_territory_polygon)
|
|
56
59
|
>>> points = gpd.GeoDataFrame(geometry=[Point(30.33, 59.95)], crs=4326).to_crs(graph.graph['crs'])
|
|
57
|
-
>>> isochrones, pt_stops, pt_routes = get_accessibility_isochrones(points,
|
|
60
|
+
>>> isochrones, pt_stops, pt_routes = get_accessibility_isochrones(points,weight_value=15, weight_type="time_min", graph_nx=my_graph)
|
|
58
61
|
|
|
59
62
|
"""
|
|
60
63
|
|
|
@@ -31,7 +31,7 @@ def eval_is_living(row: gpd.GeoSeries):
|
|
|
31
31
|
>>> buildings = download_buildings(osm_territory_id=421007)
|
|
32
32
|
>>> buildings['is_living'] = buildings.apply(eval_is_living, axis=1)
|
|
33
33
|
"""
|
|
34
|
-
|
|
34
|
+
return row["building"] in (
|
|
35
35
|
"apartments",
|
|
36
36
|
"house",
|
|
37
37
|
"residential",
|
|
@@ -41,10 +41,7 @@ def eval_is_living(row: gpd.GeoSeries):
|
|
|
41
41
|
"bungalow",
|
|
42
42
|
"cabin",
|
|
43
43
|
"farm",
|
|
44
|
-
)
|
|
45
|
-
return True
|
|
46
|
-
else:
|
|
47
|
-
return False
|
|
44
|
+
)
|
|
48
45
|
|
|
49
46
|
|
|
50
47
|
def eval_population(source: gpd.GeoDataFrame, population_column: str, area_per_person: float = 33):
|
|
@@ -111,7 +108,8 @@ def download_buildings(
|
|
|
111
108
|
area_per_person: float = 33,
|
|
112
109
|
) -> gpd.GeoDataFrame | None:
|
|
113
110
|
"""
|
|
114
|
-
Download building geometries and evaluate 'is_living' and 'population'
|
|
111
|
+
Download building geometries and evaluate 'is_living' and 'population'
|
|
112
|
+
attributes for a specified territory from OpenStreetMap.
|
|
115
113
|
|
|
116
114
|
Parameters
|
|
117
115
|
----------
|
|
@@ -146,27 +144,27 @@ def download_buildings(
|
|
|
146
144
|
(buildings["geometry"].geom_type == "Polygon") | (buildings["geometry"].geom_type == "MultiPolygon")
|
|
147
145
|
]
|
|
148
146
|
if buildings.empty:
|
|
149
|
-
logger.warning(
|
|
147
|
+
logger.warning("There are no buildings in the specified territory. Output GeoDataFrame is empty.")
|
|
150
148
|
return buildings
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
]
|
|
149
|
+
|
|
150
|
+
buildings[is_living_column] = buildings.apply(eval_is_living, axis=1)
|
|
151
|
+
buildings = eval_population(buildings, population_column, area_per_person)
|
|
152
|
+
buildings.reset_index(drop=True, inplace=True)
|
|
153
|
+
logger.debug("Done!")
|
|
154
|
+
return buildings[
|
|
155
|
+
[
|
|
156
|
+
"building",
|
|
157
|
+
"addr:street",
|
|
158
|
+
"addr:housenumber",
|
|
159
|
+
"amenity",
|
|
160
|
+
"area",
|
|
161
|
+
"name",
|
|
162
|
+
"building:levels",
|
|
163
|
+
"leisure",
|
|
164
|
+
"design:year",
|
|
165
|
+
is_living_column,
|
|
166
|
+
"building:levels_is_real",
|
|
167
|
+
population_column,
|
|
168
|
+
"geometry",
|
|
172
169
|
]
|
|
170
|
+
]
|
|
@@ -5,15 +5,13 @@ import geopandas as gpd
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import pandas as pd
|
|
7
7
|
from shapely import LineString
|
|
8
|
-
|
|
8
|
+
from pandarallel import pandarallel
|
|
9
9
|
from objectnat import config
|
|
10
10
|
|
|
11
11
|
from .provision_exceptions import CapacityKeyError, DemandKeyError
|
|
12
12
|
|
|
13
13
|
logger = config.logger
|
|
14
14
|
|
|
15
|
-
from pandarallel import pandarallel
|
|
16
|
-
|
|
17
15
|
|
|
18
16
|
class CityProvision:
|
|
19
17
|
"""
|
|
@@ -45,7 +43,7 @@ class CityProvision:
|
|
|
45
43
|
):
|
|
46
44
|
self.services = self.ensure_services(services)
|
|
47
45
|
self.demanded_buildings = self.ensure_buildings(demanded_buildings)
|
|
48
|
-
self.adjacency_matrix = self.
|
|
46
|
+
self.adjacency_matrix = self.delete_useless_matrix_rows_columns(adjacency_matrix, demanded_buildings, services)
|
|
49
47
|
self.threshold = threshold
|
|
50
48
|
self.check_crs(self.demanded_buildings, self.services)
|
|
51
49
|
pandarallel.initialize(progress_bar=False, verbose=0)
|
|
@@ -54,7 +52,6 @@ class CityProvision:
|
|
|
54
52
|
def ensure_buildings(v: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
|
55
53
|
if "demand" not in v.columns:
|
|
56
54
|
raise DemandKeyError
|
|
57
|
-
v = v.copy()
|
|
58
55
|
v["demand_left"] = v["demand"]
|
|
59
56
|
return v
|
|
60
57
|
|
|
@@ -62,7 +59,6 @@ class CityProvision:
|
|
|
62
59
|
def ensure_services(v: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
|
63
60
|
if "capacity" not in v.columns:
|
|
64
61
|
raise CapacityKeyError
|
|
65
|
-
v = v.copy()
|
|
66
62
|
v["capacity_left"] = v["capacity"]
|
|
67
63
|
return v
|
|
68
64
|
|
|
@@ -73,7 +69,7 @@ class CityProvision:
|
|
|
73
69
|
), f"\nThe CRS in the provided geodataframes are different.\nBuildings CRS:{demanded_buildings.crs}\nServices CRS:{services.crs} \n"
|
|
74
70
|
|
|
75
71
|
@staticmethod
|
|
76
|
-
def
|
|
72
|
+
def delete_useless_matrix_rows_columns(adjacency_matrix, demanded_buildings, services):
|
|
77
73
|
adjacency_matrix.index = adjacency_matrix.index.astype(int)
|
|
78
74
|
|
|
79
75
|
builds_indexes = set(demanded_buildings.index.astype(int).tolist())
|
|
@@ -142,12 +138,11 @@ class CityProvision:
|
|
|
142
138
|
def apply_function_based_on_size(df, func, axis, threshold=500):
|
|
143
139
|
if len(df) > threshold:
|
|
144
140
|
return df.parallel_apply(func, axis=axis)
|
|
145
|
-
|
|
146
|
-
return df.apply(func, axis=axis)
|
|
141
|
+
return df.apply(func, axis=axis)
|
|
147
142
|
|
|
148
143
|
def _calculate_flows_y(loc):
|
|
149
|
-
import numpy as np
|
|
150
|
-
import pandas as pd
|
|
144
|
+
import numpy as np # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
145
|
+
import pandas as pd # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
151
146
|
|
|
152
147
|
c = services_table.loc[loc.name]["capacity_left"]
|
|
153
148
|
p = 1 / loc / loc
|
|
@@ -165,8 +160,8 @@ class CityProvision:
|
|
|
165
160
|
return choice
|
|
166
161
|
|
|
167
162
|
def _balance_flows_to_demands(loc):
|
|
168
|
-
import numpy as np
|
|
169
|
-
import pandas as pd
|
|
163
|
+
import numpy as np # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
164
|
+
import pandas as pd # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
170
165
|
|
|
171
166
|
d = houses_table.loc[loc.name]["demand_left"]
|
|
172
167
|
loc = loc[loc > 0]
|
|
@@ -228,6 +223,11 @@ def _calc_links(
|
|
|
228
223
|
buildings: gpd.GeoDataFrame,
|
|
229
224
|
distance_matrix: pd.DataFrame,
|
|
230
225
|
):
|
|
226
|
+
buildings_ = buildings.copy()
|
|
227
|
+
services_ = services.copy()
|
|
228
|
+
buildings_.geometry = buildings_.representative_point()
|
|
229
|
+
services_.geometry = services_.representative_point()
|
|
230
|
+
|
|
231
231
|
def subfunc(loc):
|
|
232
232
|
try:
|
|
233
233
|
return [
|
|
@@ -244,15 +244,11 @@ def _calc_links(
|
|
|
244
244
|
def subfunc_geom(loc):
|
|
245
245
|
return LineString(
|
|
246
246
|
(
|
|
247
|
-
buildings_
|
|
248
|
-
services_
|
|
247
|
+
buildings_.geometry[loc["building_index"]],
|
|
248
|
+
services_.geometry[loc["service_index"]],
|
|
249
249
|
)
|
|
250
250
|
)
|
|
251
251
|
|
|
252
|
-
buildings_ = buildings.copy()
|
|
253
|
-
services_ = services.copy()
|
|
254
|
-
buildings_.geometry = buildings_.representative_point()
|
|
255
|
-
services_.geometry = services_.representative_point()
|
|
256
252
|
flat_matrix = destination_matrix.transpose().apply(lambda x: subfunc(x[x > 0]), result_type="reduce")
|
|
257
253
|
|
|
258
254
|
distribution_links = gpd.GeoDataFrame(data=[item for sublist in list(flat_matrix) for item in sublist])
|
|
@@ -12,6 +12,8 @@ def get_service_provision(
|
|
|
12
12
|
adjacency_matrix: pd.DataFrame,
|
|
13
13
|
services: gpd.GeoDataFrame,
|
|
14
14
|
threshold: int,
|
|
15
|
+
buildings_demand_column: str = "demand",
|
|
16
|
+
services_capacity_column: str = "capacity",
|
|
15
17
|
) -> Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
16
18
|
"""Calculate load from buildings with demands on the given services using the distances matrix between them.
|
|
17
19
|
|
|
@@ -20,10 +22,18 @@ def get_service_provision(
|
|
|
20
22
|
adjacency_matrix (pd.DataFrame): DataFrame representing the adjacency matrix
|
|
21
23
|
buildings (gpd.GeoDataFrame): GeoDataFrame of demanded buildings
|
|
22
24
|
threshold (int): Threshold value
|
|
25
|
+
buildings_demand_column (str): column name of buildings demands
|
|
26
|
+
services_capacity_column (str): column name of services capacity
|
|
23
27
|
Returns:
|
|
24
28
|
Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]: Tuple of GeoDataFrames representing provision
|
|
25
29
|
buildings, provision services, and provision links
|
|
26
30
|
"""
|
|
31
|
+
buildings = buildings.copy()
|
|
32
|
+
services = services.copy()
|
|
33
|
+
adjacency_matrix = adjacency_matrix.copy()
|
|
34
|
+
buildings["demand"] = buildings[buildings_demand_column]
|
|
35
|
+
services["capacity"] = services[services_capacity_column]
|
|
36
|
+
|
|
27
37
|
provision_buildings, provision_services, provision_links = CityProvision(
|
|
28
38
|
services=services,
|
|
29
39
|
demanded_buildings=buildings,
|
|
@@ -40,6 +50,9 @@ def clip_provision(
|
|
|
40
50
|
assert (
|
|
41
51
|
selection_zone.crs == buildings.crs == services.crs == links.crs
|
|
42
52
|
), f"CRS mismatch: buildings_crs:{buildings.crs}, links_crs:{links.crs} , services_crs:{services.crs}, selection_zone_crs:{selection_zone.crs}"
|
|
53
|
+
buildings = buildings.copy()
|
|
54
|
+
links = links.copy()
|
|
55
|
+
services = services.copy()
|
|
43
56
|
|
|
44
57
|
s = buildings.intersects(selection_zone.unary_union)
|
|
45
58
|
buildings = buildings.loc[s[s].index]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ObjectNat
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: ObjectNat is an open-source library created for geospatial analysis created by IDU team
|
|
5
5
|
License: BSD-3-Clause
|
|
6
6
|
Author: DDonnyy
|
|
@@ -44,7 +44,7 @@ Description-Content-Type: text/markdown
|
|
|
44
44
|
|
|
45
45
|
## Features and how to use
|
|
46
46
|
|
|
47
|
-
1. **[City graph from OSM (IduEdu)](
|
|
47
|
+
1. **[City graph from OSM (IduEdu)](./examples/get_any_graph.ipynb)** - Functions to assemble a road, pedestrian,
|
|
48
48
|
and public transport graph from OpenStreetMap (OSM) and creating Intermodal graph.
|
|
49
49
|
|
|
50
50
|
<img src="https://github.com/user-attachments/assets/8dc98da9-8462-415e-8cc8-bdfca788e206" alt="IntermodalGraph" height="250">
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
objectnat/__init__.py,sha256=OnDvrLPLEeYIE_9qOVYgMc-PkRzIqShtGxirguEXiRU,260
|
|
2
2
|
objectnat/_api.py,sha256=oiEO2P-tv6AMDdNoT8d0BWMmgeUJa4bhzGDTU2BWTXI,704
|
|
3
3
|
objectnat/_config.py,sha256=t4nv83Tj4fwYjdzwUh0bA8b_12DqL-GlEVfKaG_hccg,2107
|
|
4
|
-
objectnat/_version.py,sha256=
|
|
4
|
+
objectnat/_version.py,sha256=qleN2zQctO8stGYcfqFUyavqMP9pxdKBbwgZWyKxPWQ,18
|
|
5
5
|
objectnat/methods/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
6
|
objectnat/methods/balanced_buildings.py,sha256=hLT2QgmGWpROtnL8SJQIujeP6q9ou15yIdHpv66CfMs,2892
|
|
7
|
-
objectnat/methods/cluster_points_in_polygons.py,sha256=
|
|
7
|
+
objectnat/methods/cluster_points_in_polygons.py,sha256=ANoPHB89Ih6SYUTs0VoYqW7zi9GVIytSOGySoQ3vby4,5073
|
|
8
8
|
objectnat/methods/coverage_zones.py,sha256=yMeK1DjneMAxxKv9busEKdAsP25xiJMcPCixlJCDI4s,2835
|
|
9
|
-
objectnat/methods/isochrones.py,sha256=
|
|
10
|
-
objectnat/methods/living_buildings_osm.py,sha256=
|
|
9
|
+
objectnat/methods/isochrones.py,sha256=CBJprxcyPIYC4RJizqJ1MJL-Zkea4iyr7wHTOOQ7DC8,6146
|
|
10
|
+
objectnat/methods/living_buildings_osm.py,sha256=v0rC8xaqibZq9jZm5HVonmmC9VFXzgZwhqsxHA3sPlc,5904
|
|
11
11
|
objectnat/methods/provision/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
objectnat/methods/provision/city_provision.py,sha256=
|
|
13
|
-
objectnat/methods/provision/provision.py,sha256=
|
|
12
|
+
objectnat/methods/provision/city_provision.py,sha256=cFmJJVb0qLR7yCILP9S5cS8fx136EsCUmjbmT0HQA6U,13264
|
|
13
|
+
objectnat/methods/provision/provision.py,sha256=lKgROkj1EmRgk9nLEQ0jGuQcY6BFnkG3oBuhHrRFbno,4619
|
|
14
14
|
objectnat/methods/provision/provision_exceptions.py,sha256=-TK4A-vacUuzlPJGSt2YyawRwKDLCZFlAbuIvIf1FnY,1723
|
|
15
15
|
objectnat/methods/visibility_analysis.py,sha256=__S01m4YcIZbUcr6Umzvr4NpaCsajXxKNcfJm3zquVY,20690
|
|
16
16
|
objectnat/utils/__init__.py,sha256=w8R5V_Ws_GUt4hLwpudMgjXvocG4vCxWSzVw_jTReQ4,44
|
|
17
17
|
objectnat/utils/utils.py,sha256=_vbCW-XTHwZOR3yNlzf_vgNwbYwonhGlduSznGufEgs,638
|
|
18
|
-
objectnat-0.2.
|
|
19
|
-
objectnat-0.2.
|
|
20
|
-
objectnat-0.2.
|
|
21
|
-
objectnat-0.2.
|
|
18
|
+
objectnat-0.2.2.dist-info/LICENSE.txt,sha256=yPEioMfTd7JAQgAU6J13inS1BSjwd82HFlRSoIb4My8,1498
|
|
19
|
+
objectnat-0.2.2.dist-info/METADATA,sha256=ROkC-bO-4ZWFHfsmL6mLSHza9lXdbT8Aj8FaHMFtmik,5881
|
|
20
|
+
objectnat-0.2.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
21
|
+
objectnat-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|