ObjectNat 0.2.0__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 +54 -24
- objectnat/methods/provision/provision.py +13 -0
- {objectnat-0.2.0.dist-info → objectnat-0.2.2.dist-info}/METADATA +9 -4
- {objectnat-0.2.0.dist-info → objectnat-0.2.2.dist-info}/RECORD +10 -10
- {objectnat-0.2.0.dist-info → objectnat-0.2.2.dist-info}/LICENSE.txt +0 -0
- {objectnat-0.2.0.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,7 +5,7 @@ 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
|
|
@@ -43,16 +43,15 @@ class CityProvision:
|
|
|
43
43
|
):
|
|
44
44
|
self.services = self.ensure_services(services)
|
|
45
45
|
self.demanded_buildings = self.ensure_buildings(demanded_buildings)
|
|
46
|
-
self.adjacency_matrix = adjacency_matrix
|
|
46
|
+
self.adjacency_matrix = self.delete_useless_matrix_rows_columns(adjacency_matrix, demanded_buildings, services)
|
|
47
47
|
self.threshold = threshold
|
|
48
48
|
self.check_crs(self.demanded_buildings, self.services)
|
|
49
|
-
|
|
49
|
+
pandarallel.initialize(progress_bar=False, verbose=0)
|
|
50
50
|
|
|
51
51
|
@staticmethod
|
|
52
52
|
def ensure_buildings(v: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
|
53
53
|
if "demand" not in v.columns:
|
|
54
54
|
raise DemandKeyError
|
|
55
|
-
v = v.copy()
|
|
56
55
|
v["demand_left"] = v["demand"]
|
|
57
56
|
return v
|
|
58
57
|
|
|
@@ -60,7 +59,6 @@ class CityProvision:
|
|
|
60
59
|
def ensure_services(v: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
|
|
61
60
|
if "capacity" not in v.columns:
|
|
62
61
|
raise CapacityKeyError
|
|
63
|
-
v = v.copy()
|
|
64
62
|
v["capacity_left"] = v["capacity"]
|
|
65
63
|
return v
|
|
66
64
|
|
|
@@ -71,13 +69,20 @@ class CityProvision:
|
|
|
71
69
|
), f"\nThe CRS in the provided geodataframes are different.\nBuildings CRS:{demanded_buildings.crs}\nServices CRS:{services.crs} \n"
|
|
72
70
|
|
|
73
71
|
@staticmethod
|
|
74
|
-
def
|
|
72
|
+
def delete_useless_matrix_rows_columns(adjacency_matrix, demanded_buildings, services):
|
|
75
73
|
adjacency_matrix.index = adjacency_matrix.index.astype(int)
|
|
76
|
-
|
|
74
|
+
|
|
75
|
+
builds_indexes = set(demanded_buildings.index.astype(int).tolist())
|
|
77
76
|
rows = set(adjacency_matrix.index.astype(int).tolist())
|
|
78
|
-
dif = rows ^
|
|
77
|
+
dif = rows ^ builds_indexes
|
|
79
78
|
adjacency_matrix.drop(index=(list(dif)), axis=0, inplace=True)
|
|
80
79
|
|
|
80
|
+
service_indexes = set(services.index.astype(int).tolist())
|
|
81
|
+
columns = set(adjacency_matrix.columns.astype(int).tolist())
|
|
82
|
+
dif = columns ^ service_indexes
|
|
83
|
+
adjacency_matrix.drop(columns=(list(dif)), axis=0, inplace=True)
|
|
84
|
+
return adjacency_matrix
|
|
85
|
+
|
|
81
86
|
def get_provisions(self) -> Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
|
|
82
87
|
self._destination_matrix = pd.DataFrame(
|
|
83
88
|
0,
|
|
@@ -90,6 +95,8 @@ class CityProvision:
|
|
|
90
95
|
len(self.services),
|
|
91
96
|
len(self.demanded_buildings),
|
|
92
97
|
)
|
|
98
|
+
self.adjacency_matrix = self.adjacency_matrix.where(self.adjacency_matrix <= self.threshold * 3, np.inf)
|
|
99
|
+
|
|
93
100
|
self._destination_matrix = self._provision_loop_gravity(
|
|
94
101
|
self.demanded_buildings.copy(),
|
|
95
102
|
self.services.copy(),
|
|
@@ -126,13 +133,21 @@ class CityProvision:
|
|
|
126
133
|
distance_matrix: pd.DataFrame,
|
|
127
134
|
selection_range,
|
|
128
135
|
destination_matrix: pd.DataFrame,
|
|
129
|
-
|
|
136
|
+
best_houses=0.9,
|
|
130
137
|
):
|
|
138
|
+
def apply_function_based_on_size(df, func, axis, threshold=500):
|
|
139
|
+
if len(df) > threshold:
|
|
140
|
+
return df.parallel_apply(func, axis=axis)
|
|
141
|
+
return df.apply(func, axis=axis)
|
|
142
|
+
|
|
131
143
|
def _calculate_flows_y(loc):
|
|
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
|
|
146
|
+
|
|
132
147
|
c = services_table.loc[loc.name]["capacity_left"]
|
|
133
148
|
p = 1 / loc / loc
|
|
134
149
|
p = p / p.sum()
|
|
135
|
-
threshold = p.quantile(
|
|
150
|
+
threshold = p.quantile(best_houses)
|
|
136
151
|
p = p[p >= threshold]
|
|
137
152
|
p = p / p.sum()
|
|
138
153
|
if p.sum() == 0:
|
|
@@ -141,9 +156,13 @@ class CityProvision:
|
|
|
141
156
|
r = pd.Series(0, p.index)
|
|
142
157
|
choice = np.unique(rng.choice(p.index, int(c), p=p.values), return_counts=True)
|
|
143
158
|
choice = r.add(pd.Series(choice[1], choice[0]), fill_value=0)
|
|
159
|
+
|
|
144
160
|
return choice
|
|
145
161
|
|
|
146
162
|
def _balance_flows_to_demands(loc):
|
|
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
|
|
165
|
+
|
|
147
166
|
d = houses_table.loc[loc.name]["demand_left"]
|
|
148
167
|
loc = loc[loc > 0]
|
|
149
168
|
if loc.sum() > 0:
|
|
@@ -159,13 +178,20 @@ class CityProvision:
|
|
|
159
178
|
return choice
|
|
160
179
|
return loc
|
|
161
180
|
|
|
162
|
-
temp_destination_matrix =
|
|
181
|
+
temp_destination_matrix = apply_function_based_on_size(
|
|
182
|
+
distance_matrix, lambda x: _calculate_flows_y(x[x <= selection_range]), 1
|
|
183
|
+
)
|
|
184
|
+
|
|
163
185
|
temp_destination_matrix = temp_destination_matrix.fillna(0)
|
|
164
|
-
|
|
186
|
+
|
|
187
|
+
temp_destination_matrix = apply_function_based_on_size(temp_destination_matrix, _balance_flows_to_demands, 0)
|
|
188
|
+
|
|
165
189
|
temp_destination_matrix = temp_destination_matrix.fillna(0)
|
|
166
190
|
destination_matrix = destination_matrix.add(temp_destination_matrix, fill_value=0)
|
|
191
|
+
|
|
167
192
|
axis_1 = destination_matrix.sum(axis=1)
|
|
168
193
|
axis_0 = destination_matrix.sum(axis=0)
|
|
194
|
+
|
|
169
195
|
services_table["capacity_left"] = services_table["capacity"].subtract(axis_1, fill_value=0)
|
|
170
196
|
houses_table["demand_left"] = houses_table["demand"].subtract(axis_0, fill_value=0)
|
|
171
197
|
|
|
@@ -174,16 +200,19 @@ class CityProvision:
|
|
|
174
200
|
columns=houses_table[houses_table["demand_left"] == 0].index.values,
|
|
175
201
|
errors="ignore",
|
|
176
202
|
)
|
|
203
|
+
|
|
204
|
+
distance_matrix = distance_matrix.loc[~(distance_matrix == np.inf).all(axis=1)]
|
|
205
|
+
distance_matrix = distance_matrix.loc[:, ~(distance_matrix == np.inf).all(axis=0)]
|
|
206
|
+
|
|
177
207
|
selection_range += selection_range
|
|
178
208
|
|
|
209
|
+
if best_houses > 0.1:
|
|
210
|
+
best_houses -= 0.1
|
|
211
|
+
if best_houses <= 0.1:
|
|
212
|
+
best_houses = 0
|
|
179
213
|
if len(distance_matrix.columns) > 0 and len(distance_matrix.index) > 0:
|
|
180
214
|
return self._provision_loop_gravity(
|
|
181
|
-
houses_table,
|
|
182
|
-
services_table,
|
|
183
|
-
distance_matrix,
|
|
184
|
-
selection_range,
|
|
185
|
-
destination_matrix,
|
|
186
|
-
temp_destination_matrix,
|
|
215
|
+
houses_table, services_table, distance_matrix, selection_range, destination_matrix, best_houses
|
|
187
216
|
)
|
|
188
217
|
return destination_matrix
|
|
189
218
|
|
|
@@ -194,6 +223,11 @@ def _calc_links(
|
|
|
194
223
|
buildings: gpd.GeoDataFrame,
|
|
195
224
|
distance_matrix: pd.DataFrame,
|
|
196
225
|
):
|
|
226
|
+
buildings_ = buildings.copy()
|
|
227
|
+
services_ = services.copy()
|
|
228
|
+
buildings_.geometry = buildings_.representative_point()
|
|
229
|
+
services_.geometry = services_.representative_point()
|
|
230
|
+
|
|
197
231
|
def subfunc(loc):
|
|
198
232
|
try:
|
|
199
233
|
return [
|
|
@@ -210,15 +244,11 @@ def _calc_links(
|
|
|
210
244
|
def subfunc_geom(loc):
|
|
211
245
|
return LineString(
|
|
212
246
|
(
|
|
213
|
-
buildings_
|
|
214
|
-
services_
|
|
247
|
+
buildings_.geometry[loc["building_index"]],
|
|
248
|
+
services_.geometry[loc["service_index"]],
|
|
215
249
|
)
|
|
216
250
|
)
|
|
217
251
|
|
|
218
|
-
buildings_ = buildings.copy()
|
|
219
|
-
services_ = services.copy()
|
|
220
|
-
buildings_.geometry = buildings_.representative_point()
|
|
221
|
-
services_.geometry = services_.representative_point()
|
|
222
252
|
flat_matrix = destination_matrix.transpose().apply(lambda x: subfunc(x[x > 0]), result_type="reduce")
|
|
223
253
|
|
|
224
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
|
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Requires-Dist: geopandas (>=0.14.3,<0.15.0)
|
|
15
|
-
Requires-Dist: iduedu (>=0.1.
|
|
15
|
+
Requires-Dist: iduedu (>=0.1.4,<0.2.0)
|
|
16
16
|
Requires-Dist: joblib (>=1.4.2,<2.0.0)
|
|
17
17
|
Requires-Dist: networkit (>=11.0,<12.0)
|
|
18
18
|
Requires-Dist: networkx (>=3.2.1,<4.0.0)
|
|
@@ -30,6 +30,7 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
[](https://github.com/psf/black)
|
|
31
31
|
[](https://pypi.org/project/objectnat/)
|
|
32
32
|
|
|
33
|
+
- [РИДМИ (Russian)](README_ru.md)
|
|
33
34
|
<p align="center">
|
|
34
35
|
<img src="https://github.com/user-attachments/assets/d3878cce-8eba-4f96-8458-9a798d436120" alt="logo" width="400">
|
|
35
36
|
</p>
|
|
@@ -43,7 +44,7 @@ Description-Content-Type: text/markdown
|
|
|
43
44
|
|
|
44
45
|
## Features and how to use
|
|
45
46
|
|
|
46
|
-
1. **[City graph from OSM (IduEdu)](
|
|
47
|
+
1. **[City graph from OSM (IduEdu)](./examples/get_any_graph.ipynb)** - Functions to assemble a road, pedestrian,
|
|
47
48
|
and public transport graph from OpenStreetMap (OSM) and creating Intermodal graph.
|
|
48
49
|
|
|
49
50
|
<img src="https://github.com/user-attachments/assets/8dc98da9-8462-415e-8cc8-bdfca788e206" alt="IntermodalGraph" height="250">
|
|
@@ -80,7 +81,11 @@ Description-Content-Type: text/markdown
|
|
|
80
81
|
between types of services within the clusters.
|
|
81
82
|
|
|
82
83
|
<img src="https://github.com/user-attachments/assets/2a9ad722-87d2-4954-9612-5ac3765aa824" alt="service-clusterization" height="250">
|
|
83
|
-
|
|
84
|
+
|
|
85
|
+
8. **[Living buildings from OSM](./examples/download_buildings_from_osm.ipynb)** - This function downloads building geometries from OpenStreetMap (OSM) for a specified territory and assigns attributes to each building. Specifically, it determines whether a building is residential (`is_living` attribute) and estimates the approximate number of inhabitants (`approximate_pop` attribute).
|
|
86
|
+
|
|
87
|
+
<img src="https://github.com/user-attachments/assets/d60dcd85-1a2e-4342-aae4-561aeda18858" alt="Living buildings" height="250">
|
|
88
|
+
|
|
84
89
|
## Installation
|
|
85
90
|
|
|
86
91
|
**ObjectNat** can be installed with ``pip``:
|
|
@@ -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
|