ObjectNat 1.3.1__tar.gz → 1.3.3__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.
Files changed (34) hide show
  1. {objectnat-1.3.1 → objectnat-1.3.3}/PKG-INFO +1 -1
  2. {objectnat-1.3.1 → objectnat-1.3.3}/pyproject.toml +1 -1
  3. objectnat-1.3.3/src/objectnat/_version.py +1 -0
  4. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/noise/noise_simulation_simplified.py +18 -6
  5. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/visibility/visibility_analysis.py +80 -57
  6. objectnat-1.3.1/src/objectnat/_version.py +0 -1
  7. {objectnat-1.3.1 → objectnat-1.3.3}/LICENSE.txt +0 -0
  8. {objectnat-1.3.1 → objectnat-1.3.3}/README.rst +0 -0
  9. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/__init__.py +0 -0
  10. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/_api.py +0 -0
  11. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/_config.py +0 -0
  12. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/__init__.py +0 -0
  13. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/coverage_zones/__init__.py +0 -0
  14. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/coverage_zones/graph_coverage.py +0 -0
  15. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/coverage_zones/radius_voronoi_coverage.py +0 -0
  16. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/coverage_zones/stepped_coverage.py +0 -0
  17. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/isochrones/__init__.py +0 -0
  18. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/isochrones/isochrone_utils.py +0 -0
  19. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/isochrones/isochrones.py +0 -0
  20. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/noise/__init__.py +0 -0
  21. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/noise/noise_init_data.py +0 -0
  22. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/noise/noise_reduce.py +0 -0
  23. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/noise/noise_simulation.py +0 -0
  24. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/point_clustering/__init__.py +0 -0
  25. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/point_clustering/cluster_points_in_polygons.py +0 -0
  26. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/provision/__init__.py +0 -0
  27. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/provision/provision.py +0 -0
  28. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/provision/provision_exceptions.py +0 -0
  29. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/provision/provision_model.py +0 -0
  30. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/utils/__init__.py +0 -0
  31. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/utils/geom_utils.py +0 -0
  32. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/utils/graph_utils.py +0 -0
  33. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/utils/math_utils.py +0 -0
  34. {objectnat-1.3.1 → objectnat-1.3.3}/src/objectnat/methods/visibility/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ObjectNat
3
- Version: 1.3.1
3
+ Version: 1.3.3
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
  License-File: LICENSE.txt
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ObjectNat"
3
- version = "1.3.1"
3
+ version = "1.3.3"
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>"]
@@ -0,0 +1 @@
1
+ VERSION = "1.3.3"
@@ -131,9 +131,16 @@ def calculate_simplified_noise_frame(
131
131
  point_from = row.geometry
132
132
  point_buffer = point_from.buffer(max_dist, resolution=16)
133
133
  local_obstacles = obstacles[obstacles.intersects(point_buffer)]
134
- vis_poly = get_visibility_accurate(point_from, obstacles=local_obstacles, view_distance=max_dist)
135
- noise_from_feature = _eval_donuts_gdf(point_from, dist_db, local_crs, vis_poly)
136
- frame_result.append(noise_from_feature)
134
+ vis_poly_gdf = get_visibility_accurate(
135
+ gpd.GeoDataFrame(geometry=[point_from], crs=local_crs),
136
+ obstacles=local_obstacles,
137
+ view_distance=max_dist,
138
+ )
139
+ if len(vis_poly_gdf) > 0:
140
+ noise_from_feature = _eval_donuts_gdf(
141
+ point_from, dist_db, local_crs, vis_poly_gdf.iloc[0].geometry
142
+ )
143
+ frame_result.append(noise_from_feature)
137
144
  pbar.update(1)
138
145
 
139
146
  elif geom_type == "LineString":
@@ -148,7 +155,7 @@ def calculate_simplified_noise_frame(
148
155
  )
149
156
  frame_result.append(noise_from_feature)
150
157
  elif geom_type == "Polygon":
151
- group_gdf.geometry = group_gdf.buffer(0.1, resolution=1)
158
+ group_gdf.geometry = group_gdf.buffer(0.1, resolution=2)
152
159
  layer_points = distribute_points_on_polygons(
153
160
  group_gdf, only_exterior=False, radius=polygon_point_radius, lloyd_relax_n=1
154
161
  )
@@ -186,8 +193,13 @@ def _process_lines_or_polygons(
186
193
  local_obstacles = obstacles[obstacles.intersects(layer_buffer)]
187
194
  for _, row in layer_points.iterrows():
188
195
  point_from = row.geometry
189
- vis_poly = get_visibility_accurate(point_from, obstacles=local_obstacles, view_distance=max_dist)
190
- features_vision_polys.append(vis_poly)
196
+ vis_poly_gdf = get_visibility_accurate(
197
+ gpd.GeoDataFrame(geometry=[point_from], crs=local_crs),
198
+ obstacles=local_obstacles,
199
+ view_distance=max_dist,
200
+ )
201
+ if len(vis_poly_gdf) > 0:
202
+ features_vision_polys.append(vis_poly_gdf.iloc[0].geometry)
191
203
  pbar.update(1)
192
204
  features_vision_polys = unary_union(features_vision_polys)
193
205
  return _eval_donuts_gdf(group_gdf.union_all(), dist_db, local_crs, features_vision_polys)
@@ -21,6 +21,44 @@ from objectnat.methods.utils.math_utils import min_max_normalization
21
21
  logger = config.logger
22
22
 
23
23
 
24
+ def _ensure_crs(point_from, obstacles):
25
+ if isinstance(point_from, gpd.GeoDataFrame):
26
+ if point_from.empty:
27
+ raise ValueError("GeoDataFrame 'point_from' is empty.")
28
+ return_gdf = True
29
+ original_crs = point_from.crs
30
+ local_crs = point_from.estimate_utm_crs()
31
+ point_from = point_from.to_crs(local_crs)
32
+ if len(point_from) > 1:
33
+ logger.warning(
34
+ "This method processes only a single point. "
35
+ f"The GeoDataFrame contains {len(point_from)} points – "
36
+ "only the first geometry will be used for visibility calculation."
37
+ )
38
+ point_geom = point_from.iloc[0].geometry
39
+ if len(obstacles) > 0:
40
+ obstacles = obstacles.to_crs(local_crs)
41
+
42
+ else:
43
+ return_gdf = False
44
+
45
+ if len(obstacles) > 0:
46
+ original_crs = obstacles.crs
47
+ local_crs = obstacles.estimate_utm_crs()
48
+ obstacles = obstacles.to_crs(local_crs)
49
+ point_geom = gpd.GeoDataFrame(geometry=[point_from], crs=original_crs).to_crs(local_crs).iloc[0].geometry
50
+ else:
51
+ original_crs = 4326
52
+ logger.warning("No information about CRS was found, accepting everything in 4326")
53
+ point_gdf = gpd.GeoDataFrame(geometry=[point_from], crs=original_crs)
54
+ local_crs = point_gdf.estimate_utm_crs()
55
+
56
+ point_gdf = point_gdf.to_crs(local_crs)
57
+ point_geom = point_gdf.geometry.iloc[0]
58
+
59
+ return point_geom, obstacles, original_crs, local_crs, return_gdf
60
+
61
+
24
62
  def get_visibility_accurate(
25
63
  point_from: Point | gpd.GeoDataFrame, obstacles: gpd.GeoDataFrame, view_distance, return_max_view_dist=False
26
64
  ) -> Polygon | gpd.GeoDataFrame | tuple[Polygon | gpd.GeoDataFrame, float]:
@@ -56,31 +94,21 @@ def get_visibility_accurate(
56
94
  raise e
57
95
  return res
58
96
 
59
- local_crs = None
60
- original_crs = None
61
- return_gdf = False
62
- if isinstance(point_from, gpd.GeoDataFrame):
63
- original_crs = point_from.crs
64
- return_gdf = True
65
- if len(obstacles) > 0:
66
- local_crs = obstacles.estimate_utm_crs()
67
- else:
68
- local_crs = point_from.estimate_utm_crs()
69
- obstacles = obstacles.to_crs(local_crs)
70
- point_from = point_from.to_crs(local_crs)
71
- if len(point_from) > 1:
72
- logger.warning(
73
- f"This method processes only single point. The GeoDataFrame contains {len(point_from)} points - "
74
- "only the first geometry will be used for isochrone calculation. "
75
- )
76
- point_from = point_from.iloc[0].geometry
77
- else:
78
- obstacles = obstacles.copy()
79
- if obstacles.contains(point_from).any():
80
- return Polygon()
97
+ point_geom, obstacles, original_crs, local_crs, return_gdf = _ensure_crs(point_from, obstacles)
98
+
99
+ if len(obstacles) > 0 and obstacles.contains(point_geom).any():
100
+ return gpd.GeoDataFrame() if return_gdf else Point()
101
+
102
+ point_buffer = point_geom.buffer(view_distance, resolution=32)
103
+
104
+ if len(obstacles) == 0:
105
+ full_vision_gdf = gpd.GeoDataFrame(geometry=[point_buffer], crs=local_crs).to_crs(original_crs)
106
+ return full_vision_gdf if return_gdf else full_vision_gdf.iloc[0].geometry
107
+
81
108
  obstacles.reset_index(inplace=True, drop=True)
82
- point_buffer = point_from.buffer(view_distance, resolution=32)
109
+
83
110
  allowed_geom_types = ["MultiPolygon", "Polygon", "LineString", "MultiLineString"]
111
+
84
112
  obstacles = obstacles[obstacles.geom_type.isin(allowed_geom_types)]
85
113
  s = obstacles.intersects(point_buffer)
86
114
  obstacles_in_buffer = obstacles.loc[s[s].index].geometry
@@ -98,26 +126,26 @@ def get_visibility_accurate(
98
126
  + [Point(line.coords[-1]) for line in buildings_lines_in_buffer.geometry]
99
127
  )
100
128
 
101
- max_dist = max(view_distance, buildings_in_buffer_points.distance(point_from).max())
129
+ max_dist = max(view_distance, buildings_in_buffer_points.distance(point_geom).max())
102
130
  polygons = []
103
131
  buildings_lines_in_buffer = gpd.GeoDataFrame(geometry=buildings_lines_in_buffer, crs=obstacles.crs).reset_index()
104
132
  logger.debug("Calculation vis polygon")
105
133
  while not buildings_lines_in_buffer.empty:
106
134
  gdf_sindex = buildings_lines_in_buffer.sindex
107
135
  # TODO check if 2 walls are nearest and use the widest angle between points
108
- nearest_wall_sind = gdf_sindex.nearest(point_from, return_all=False, max_distance=max_dist)
136
+ nearest_wall_sind = gdf_sindex.nearest(point_geom, return_all=False, max_distance=max_dist)
109
137
  nearest_wall = buildings_lines_in_buffer.loc[nearest_wall_sind[1]].iloc[0]
110
138
  wall_points = [Point(coords) for coords in nearest_wall.geometry.coords]
111
139
 
112
140
  # Calculate angles and sort by angle
113
141
  points_with_angle = sorted(
114
- [(pt, math.atan2(pt.y - point_from.y, pt.x - point_from.x)) for pt in wall_points], key=lambda x: x[1]
142
+ [(pt, math.atan2(pt.y - point_geom.y, pt.x - point_geom.x)) for pt in wall_points], key=lambda x: x[1]
115
143
  )
116
144
  delta_angle = 2 * math.pi + points_with_angle[0][1] - points_with_angle[-1][1]
117
145
  if round(delta_angle, 10) == round(math.pi, 10):
118
146
  wall_b_centroid = obstacles_in_buffer.loc[nearest_wall["index"]].centroid
119
- p1 = get_point_from_a_thorough_b(point_from, points_with_angle[0][0], max_dist)
120
- p2 = get_point_from_a_thorough_b(point_from, points_with_angle[1][0], max_dist)
147
+ p1 = get_point_from_a_thorough_b(point_geom, points_with_angle[0][0], max_dist)
148
+ p2 = get_point_from_a_thorough_b(point_geom, points_with_angle[1][0], max_dist)
121
149
  polygon = LineString([p1, p2])
122
150
  polygon = polygon.buffer(
123
151
  distance=max_dist * point_side_of_line(polygon, wall_b_centroid), single_sided=True
@@ -126,8 +154,8 @@ def get_visibility_accurate(
126
154
  if delta_angle > math.pi:
127
155
  delta_angle = 2 * math.pi - delta_angle
128
156
  a = math.sqrt((max_dist**2) * (1 + (math.tan(delta_angle / 2) ** 2)))
129
- p1 = get_point_from_a_thorough_b(point_from, points_with_angle[0][0], a)
130
- p2 = get_point_from_a_thorough_b(point_from, points_with_angle[-1][0], a)
157
+ p1 = get_point_from_a_thorough_b(point_geom, points_with_angle[0][0], a)
158
+ p2 = get_point_from_a_thorough_b(point_geom, points_with_angle[-1][0], a)
131
159
  polygon = Polygon([points_with_angle[0][0], p1, p2, points_with_angle[1][0]])
132
160
 
133
161
  polygons.append(polygon)
@@ -146,15 +174,16 @@ def get_visibility_accurate(
146
174
  if isinstance(res, MultiPolygon):
147
175
  res = list(res.geoms)
148
176
  for polygon in res:
149
- if polygon.intersects(point_from):
177
+ if polygon.intersects(point_geom):
150
178
  res = polygon
151
179
  break
152
180
 
181
+ result_gdf = gpd.GeoDataFrame(geometry=[res], crs=local_crs).to_crs(original_crs)
153
182
  if return_gdf:
154
- res = gpd.GeoDataFrame(geometry=[res], crs=local_crs).to_crs(original_crs)
155
-
183
+ return result_gdf
184
+ res = result_gdf.to_crs(original_crs).iloc[0].geometry
156
185
  if return_max_view_dist:
157
- return res, find_furthest_point(point_from, res)
186
+ return res, find_furthest_point(point_geom, res)
158
187
  return res
159
188
 
160
189
 
@@ -184,29 +213,24 @@ def get_visibility(
184
213
  This function provides a quicker but less accurate result compared to `get_visibility_accurate()`.
185
214
  If accuracy is important, consider using `get_visibility_accurate()` instead.
186
215
  """
187
- return_gdf = False
188
- if isinstance(point_from, gpd.GeoDataFrame):
189
- original_crs = point_from.crs
190
- return_gdf = True
191
- if len(obstacles) > 0:
192
- local_crs = obstacles.estimate_utm_crs()
193
- else:
194
- local_crs = point_from.estimate_utm_crs()
195
- obstacles = obstacles.to_crs(local_crs)
196
- point_from = point_from.to_crs(local_crs)
197
- if len(point_from) > 1:
198
- logger.warning(
199
- f"This method processes only single point. The GeoDataFrame contains {len(point_from)} points - "
200
- "only the first geometry will be used for isochrone calculation. "
201
- )
202
- point_from = point_from.iloc[0].geometry
203
- else:
204
- obstacles = obstacles.copy()
205
- point_buffer = point_from.buffer(view_distance, resolution=resolution)
216
+
217
+ obstacles = obstacles.copy()
218
+
219
+ point_geom, obstacles, original_crs, local_crs, return_gdf = _ensure_crs(point_from, obstacles)
220
+ print(return_gdf)
221
+ if len(obstacles) > 0 and obstacles.contains(point_geom).any():
222
+ return Polygon()
223
+
224
+ point_buffer = point_geom.buffer(view_distance, resolution=resolution)
225
+
226
+ if len(obstacles) == 0:
227
+ full_vision_gdf = gpd.GeoDataFrame(geometry=[point_buffer], crs=local_crs).to_crs(original_crs)
228
+ return full_vision_gdf if return_gdf else full_vision_gdf.iloc[0].geometry
229
+
206
230
  s = obstacles.intersects(point_buffer)
207
231
  buildings_in_buffer = obstacles.loc[s[s].index].reset_index(drop=True)
208
232
  buffer_exterior_ = list(point_buffer.exterior.coords)
209
- line_geometry = [LineString([point_from, ext]) for ext in buffer_exterior_]
233
+ line_geometry = [LineString([point_geom, ext]) for ext in buffer_exterior_]
210
234
  buffer_lines_gdf = gpd.GeoDataFrame(geometry=line_geometry)
211
235
  united_buildings = buildings_in_buffer.union_all()
212
236
  if united_buildings:
@@ -223,9 +247,8 @@ def get_visibility(
223
247
  if united_buildings:
224
248
  circuit = circuit.difference(united_buildings)
225
249
 
226
- if return_gdf:
227
- circuit = gpd.GeoDataFrame(geometry=[circuit], crs=local_crs).to_crs(original_crs)
228
- return circuit
250
+ result_gdf = gpd.GeoDataFrame(geometry=[circuit], crs=local_crs).to_crs(original_crs)
251
+ return result_gdf if return_gdf else result_gdf.iloc[0].geometry
229
252
 
230
253
 
231
254
  def get_visibilities_from_points(
@@ -1 +0,0 @@
1
- VERSION = "1.3.1"
File without changes
File without changes