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 CHANGED
@@ -1 +1 @@
1
- VERSION = "0.2.0"
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["service_code"] = loc["service_code"].astype(str)
27
- services_count = loc.groupby("service_code").size()
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 "service_code" not in points.columns:
77
+ if service_code_column not in points.columns:
76
78
  logger.warning(
77
- "No 'service_code' column in provided GeoDataFrame, cluster polygons will be without relative ratio."
79
+ f"No {service_code_column} column in provided GeoDataFrame, cluster polygons will be without relative ratio"
78
80
  )
79
- points["service_code"] = 1
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(_get_service_ratio)
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(_get_service_ratio)
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
 
@@ -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 : GeoDataFrame with the calculated isochrone geometries.
49
- - public transport stops (if applicable) : GeoDataFrame with public transport stops within the isochrone, or None if not applicable.
50
- - public transport routes (if applicable) : GeoDataFrame with public transport routes within the isochrone, or None if not applicable.
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, weight_value=15, weight_type="time_min", graph_nx=my_graph)
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
- if row["building"] in (
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' attributes for a specified territory from OpenStreetMap.
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(f"There are no buildings in the specified territory. Output GeoDataFrame is empty.")
147
+ logger.warning("There are no buildings in the specified territory. Output GeoDataFrame is empty.")
150
148
  return buildings
151
- else:
152
- buildings[is_living_column] = buildings.apply(eval_is_living, axis=1)
153
- buildings = eval_population(buildings, population_column, area_per_person)
154
- buildings.reset_index(drop=True, inplace=True)
155
- logger.debug("Done!")
156
- return buildings[
157
- [
158
- "building",
159
- "addr:street",
160
- "addr:housenumber",
161
- "amenity",
162
- "area",
163
- "name",
164
- "building:levels",
165
- "leisure",
166
- "design:year",
167
- is_living_column,
168
- "building:levels_is_real",
169
- population_column,
170
- "geometry",
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
- self.delete_useless_matrix_rows(self.adjacency_matrix, self.demanded_buildings)
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 delete_useless_matrix_rows(adjacency_matrix, demanded_buildings):
72
+ def delete_useless_matrix_rows_columns(adjacency_matrix, demanded_buildings, services):
75
73
  adjacency_matrix.index = adjacency_matrix.index.astype(int)
76
- indexes = set(demanded_buildings.index.astype(int).tolist())
74
+
75
+ builds_indexes = set(demanded_buildings.index.astype(int).tolist())
77
76
  rows = set(adjacency_matrix.index.astype(int).tolist())
78
- dif = rows ^ indexes
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
- temp_destination_matrix=None,
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(0.9)
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 = distance_matrix.apply(lambda x: _calculate_flows_y(x[x <= selection_range]), axis=1)
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
- temp_destination_matrix = temp_destination_matrix.apply(_balance_flows_to_demands)
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_["geometry"][loc["building_index"]],
214
- services_["geometry"][loc["service_index"]],
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.0
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.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>
@@ -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)](https://github.com/DDonnyy/IduEdu/blob/main/examples/get_any_graph.ipynb)** - Functions to assemble a road, pedestrian,
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=w9Iw7QVvd8lme2wKwEbCo5IgetVjSfFBOOYAcA_Q0Ns,18
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=2oHK-_CauEz8dZX6r-UGEopkqdUENLGaJPpMTuVV_o8,4678
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=CeNTVpUnlITaacamB5mJQjnbphXckC1FJ0L1EThswhU,6111
10
- objectnat/methods/living_buildings_osm.py,sha256=pHyeDSKhs4j05Wr3Z_QBxLfLbiZpbwdj_SXz7qQ7V2M,6041
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=Ireg4AGoMdDvf9Fa7ILIOzLJcbqTtpeIHAKp_rqvTAE,11876
13
- objectnat/methods/provision/provision.py,sha256=BrwfKcGoRwNcLL9fZ6mU9m6kMuGnAIgxGHfXdgHA39o,4057
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.0.dist-info/LICENSE.txt,sha256=yPEioMfTd7JAQgAU6J13inS1BSjwd82HFlRSoIb4My8,1498
19
- objectnat-0.2.0.dist-info/METADATA,sha256=5rwXA6qcLxRoUftmD2yoZP3715ghrA1v-7g_MmeLYXI,5354
20
- objectnat-0.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
21
- objectnat-0.2.0.dist-info/RECORD,,
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,,