ObjectNat 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ObjectNat might be problematic. Click here for more details.

Files changed (22) hide show
  1. {objectnat-0.2.0 → objectnat-0.2.1}/PKG-INFO +8 -3
  2. {objectnat-0.2.0 → objectnat-0.2.1}/README.md +6 -1
  3. {objectnat-0.2.0 → objectnat-0.2.1}/pyproject.toml +2 -2
  4. objectnat-0.2.1/src/objectnat/_version.py +1 -0
  5. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/provision/city_provision.py +49 -15
  6. objectnat-0.2.0/src/objectnat/_version.py +0 -1
  7. {objectnat-0.2.0 → objectnat-0.2.1}/LICENSE.txt +0 -0
  8. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/__init__.py +0 -0
  9. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/_api.py +0 -0
  10. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/_config.py +0 -0
  11. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/__init__.py +0 -0
  12. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/balanced_buildings.py +0 -0
  13. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/cluster_points_in_polygons.py +0 -0
  14. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/coverage_zones.py +0 -0
  15. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/isochrones.py +0 -0
  16. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/living_buildings_osm.py +0 -0
  17. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/provision/__init__.py +0 -0
  18. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/provision/provision.py +0 -0
  19. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/provision/provision_exceptions.py +0 -0
  20. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/methods/visibility_analysis.py +0 -0
  21. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/utils/__init__.py +0 -0
  22. {objectnat-0.2.0 → objectnat-0.2.1}/src/objectnat/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ObjectNat
3
- Version: 0.2.0
3
+ Version: 0.2.1
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.3,<0.2.0)
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
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
31
31
  [![PyPI version](https://img.shields.io/pypi/v/objectnat.svg)](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>
@@ -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``:
@@ -3,6 +3,7 @@
3
3
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
4
4
  [![PyPI version](https://img.shields.io/pypi/v/objectnat.svg)](https://pypi.org/project/objectnat/)
5
5
 
6
+ - [РИДМИ (Russian)](README_ru.md)
6
7
  <p align="center">
7
8
  <img src="https://github.com/user-attachments/assets/d3878cce-8eba-4f96-8458-9a798d436120" alt="logo" width="400">
8
9
  </p>
@@ -53,7 +54,11 @@
53
54
  between types of services within the clusters.
54
55
 
55
56
  <img src="https://github.com/user-attachments/assets/2a9ad722-87d2-4954-9612-5ac3765aa824" alt="service-clusterization" height="250">
56
-
57
+
58
+ 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).
59
+
60
+ <img src="https://github.com/user-attachments/assets/d60dcd85-1a2e-4342-aae4-561aeda18858" alt="Living buildings" height="250">
61
+
57
62
  ## Installation
58
63
 
59
64
  **ObjectNat** can be installed with ``pip``:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ObjectNat"
3
- version = "0.2.0"
3
+ version = "0.2.1"
4
4
  description = "ObjectNat is an open-source library created for geospatial analysis created by IDU team"
5
5
  license = "BSD-3-Clause"
6
6
  authors = ["DDonnyy <63115678+DDonnyy@users.noreply.github.com>"]
@@ -19,7 +19,7 @@ networkx = "^3.2.1"
19
19
  networkit = "^11.0"
20
20
  pulp = "^2.8.0"
21
21
  population-restorator = "^0.2.3"
22
- iduedu = "^0.1.3"
22
+ iduedu = "^0.1.4"
23
23
  joblib = "^1.4.2"
24
24
  scikit-learn = "^1.4.0"
25
25
 
@@ -0,0 +1 @@
1
+ VERSION = "0.2.1"
@@ -12,6 +12,8 @@ from .provision_exceptions import CapacityKeyError, DemandKeyError
12
12
 
13
13
  logger = config.logger
14
14
 
15
+ from pandarallel import pandarallel
16
+
15
17
 
16
18
  class CityProvision:
17
19
  """
@@ -43,10 +45,10 @@ class CityProvision:
43
45
  ):
44
46
  self.services = self.ensure_services(services)
45
47
  self.demanded_buildings = self.ensure_buildings(demanded_buildings)
46
- self.adjacency_matrix = adjacency_matrix
48
+ self.adjacency_matrix = self.delete_useless_matrix_rows(adjacency_matrix.copy(), demanded_buildings, services)
47
49
  self.threshold = threshold
48
50
  self.check_crs(self.demanded_buildings, self.services)
49
- self.delete_useless_matrix_rows(self.adjacency_matrix, self.demanded_buildings)
51
+ pandarallel.initialize(progress_bar=False, verbose=0)
50
52
 
51
53
  @staticmethod
52
54
  def ensure_buildings(v: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
@@ -71,13 +73,20 @@ class CityProvision:
71
73
  ), f"\nThe CRS in the provided geodataframes are different.\nBuildings CRS:{demanded_buildings.crs}\nServices CRS:{services.crs} \n"
72
74
 
73
75
  @staticmethod
74
- def delete_useless_matrix_rows(adjacency_matrix, demanded_buildings):
76
+ def delete_useless_matrix_rows(adjacency_matrix, demanded_buildings, services):
75
77
  adjacency_matrix.index = adjacency_matrix.index.astype(int)
76
- indexes = set(demanded_buildings.index.astype(int).tolist())
78
+
79
+ builds_indexes = set(demanded_buildings.index.astype(int).tolist())
77
80
  rows = set(adjacency_matrix.index.astype(int).tolist())
78
- dif = rows ^ indexes
81
+ dif = rows ^ builds_indexes
79
82
  adjacency_matrix.drop(index=(list(dif)), axis=0, inplace=True)
80
83
 
84
+ service_indexes = set(services.index.astype(int).tolist())
85
+ columns = set(adjacency_matrix.columns.astype(int).tolist())
86
+ dif = columns ^ service_indexes
87
+ adjacency_matrix.drop(columns=(list(dif)), axis=0, inplace=True)
88
+ return adjacency_matrix
89
+
81
90
  def get_provisions(self) -> Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame, gpd.GeoDataFrame]:
82
91
  self._destination_matrix = pd.DataFrame(
83
92
  0,
@@ -90,6 +99,8 @@ class CityProvision:
90
99
  len(self.services),
91
100
  len(self.demanded_buildings),
92
101
  )
102
+ self.adjacency_matrix = self.adjacency_matrix.where(self.adjacency_matrix <= self.threshold * 3, np.inf)
103
+
93
104
  self._destination_matrix = self._provision_loop_gravity(
94
105
  self.demanded_buildings.copy(),
95
106
  self.services.copy(),
@@ -126,13 +137,22 @@ class CityProvision:
126
137
  distance_matrix: pd.DataFrame,
127
138
  selection_range,
128
139
  destination_matrix: pd.DataFrame,
129
- temp_destination_matrix=None,
140
+ best_houses=0.9,
130
141
  ):
142
+ def apply_function_based_on_size(df, func, axis, threshold=500):
143
+ if len(df) > threshold:
144
+ return df.parallel_apply(func, axis=axis)
145
+ else:
146
+ return df.apply(func, axis=axis)
147
+
131
148
  def _calculate_flows_y(loc):
149
+ import numpy as np
150
+ import pandas as pd
151
+
132
152
  c = services_table.loc[loc.name]["capacity_left"]
133
153
  p = 1 / loc / loc
134
154
  p = p / p.sum()
135
- threshold = p.quantile(0.9)
155
+ threshold = p.quantile(best_houses)
136
156
  p = p[p >= threshold]
137
157
  p = p / p.sum()
138
158
  if p.sum() == 0:
@@ -141,9 +161,13 @@ class CityProvision:
141
161
  r = pd.Series(0, p.index)
142
162
  choice = np.unique(rng.choice(p.index, int(c), p=p.values), return_counts=True)
143
163
  choice = r.add(pd.Series(choice[1], choice[0]), fill_value=0)
164
+
144
165
  return choice
145
166
 
146
167
  def _balance_flows_to_demands(loc):
168
+ import numpy as np
169
+ import pandas as pd
170
+
147
171
  d = houses_table.loc[loc.name]["demand_left"]
148
172
  loc = loc[loc > 0]
149
173
  if loc.sum() > 0:
@@ -159,13 +183,20 @@ class CityProvision:
159
183
  return choice
160
184
  return loc
161
185
 
162
- temp_destination_matrix = distance_matrix.apply(lambda x: _calculate_flows_y(x[x <= selection_range]), axis=1)
186
+ temp_destination_matrix = apply_function_based_on_size(
187
+ distance_matrix, lambda x: _calculate_flows_y(x[x <= selection_range]), 1
188
+ )
189
+
163
190
  temp_destination_matrix = temp_destination_matrix.fillna(0)
164
- temp_destination_matrix = temp_destination_matrix.apply(_balance_flows_to_demands)
191
+
192
+ temp_destination_matrix = apply_function_based_on_size(temp_destination_matrix, _balance_flows_to_demands, 0)
193
+
165
194
  temp_destination_matrix = temp_destination_matrix.fillna(0)
166
195
  destination_matrix = destination_matrix.add(temp_destination_matrix, fill_value=0)
196
+
167
197
  axis_1 = destination_matrix.sum(axis=1)
168
198
  axis_0 = destination_matrix.sum(axis=0)
199
+
169
200
  services_table["capacity_left"] = services_table["capacity"].subtract(axis_1, fill_value=0)
170
201
  houses_table["demand_left"] = houses_table["demand"].subtract(axis_0, fill_value=0)
171
202
 
@@ -174,16 +205,19 @@ class CityProvision:
174
205
  columns=houses_table[houses_table["demand_left"] == 0].index.values,
175
206
  errors="ignore",
176
207
  )
208
+
209
+ distance_matrix = distance_matrix.loc[~(distance_matrix == np.inf).all(axis=1)]
210
+ distance_matrix = distance_matrix.loc[:, ~(distance_matrix == np.inf).all(axis=0)]
211
+
177
212
  selection_range += selection_range
178
213
 
214
+ if best_houses > 0.1:
215
+ best_houses -= 0.1
216
+ if best_houses <= 0.1:
217
+ best_houses = 0
179
218
  if len(distance_matrix.columns) > 0 and len(distance_matrix.index) > 0:
180
219
  return self._provision_loop_gravity(
181
- houses_table,
182
- services_table,
183
- distance_matrix,
184
- selection_range,
185
- destination_matrix,
186
- temp_destination_matrix,
220
+ houses_table, services_table, distance_matrix, selection_range, destination_matrix, best_houses
187
221
  )
188
222
  return destination_matrix
189
223
 
@@ -1 +0,0 @@
1
- VERSION = "0.2.0"
File without changes