ObjectNat 1.3.0__tar.gz → 1.3.2__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.0 → objectnat-1.3.2}/PKG-INFO +3 -3
  2. {objectnat-1.3.0 → objectnat-1.3.2}/README.rst +2 -2
  3. {objectnat-1.3.0 → objectnat-1.3.2}/pyproject.toml +1 -1
  4. objectnat-1.3.2/src/objectnat/_version.py +1 -0
  5. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/noise/noise_simulation.py +1 -1
  6. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/visibility/visibility_analysis.py +79 -56
  7. objectnat-1.3.0/src/objectnat/_version.py +0 -1
  8. {objectnat-1.3.0 → objectnat-1.3.2}/LICENSE.txt +0 -0
  9. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/__init__.py +0 -0
  10. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/_api.py +0 -0
  11. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/_config.py +0 -0
  12. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/__init__.py +0 -0
  13. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/coverage_zones/__init__.py +0 -0
  14. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/coverage_zones/graph_coverage.py +0 -0
  15. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/coverage_zones/radius_voronoi_coverage.py +0 -0
  16. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/coverage_zones/stepped_coverage.py +0 -0
  17. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/isochrones/__init__.py +0 -0
  18. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/isochrones/isochrone_utils.py +0 -0
  19. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/isochrones/isochrones.py +0 -0
  20. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/noise/__init__.py +0 -0
  21. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/noise/noise_init_data.py +0 -0
  22. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/noise/noise_reduce.py +0 -0
  23. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/noise/noise_simulation_simplified.py +0 -0
  24. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/point_clustering/__init__.py +0 -0
  25. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/point_clustering/cluster_points_in_polygons.py +0 -0
  26. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/provision/__init__.py +0 -0
  27. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/provision/provision.py +0 -0
  28. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/provision/provision_exceptions.py +0 -0
  29. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/provision/provision_model.py +0 -0
  30. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/utils/__init__.py +0 -0
  31. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/utils/geom_utils.py +0 -0
  32. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/utils/graph_utils.py +0 -0
  33. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/utils/math_utils.py +0 -0
  34. {objectnat-1.3.0 → objectnat-1.3.2}/src/objectnat/methods/visibility/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ObjectNat
3
- Version: 1.3.0
3
+ Version: 1.3.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
@@ -51,9 +51,9 @@ Object-oriented Network Analysis Tools
51
51
 
52
52
  |badge-black| |badge-pypi| |badge-ci| |badge-codecov| |badge-license| |badge-docs|
53
53
 
54
- `РИДМИ (Russian) <https://github.com/IDUclub/ObjectNat/blob/main/README_RU.rst>`__
54
+ `РИДМИ (Russian) <https://github.com/IDUclub/ObjectNat/blob/master/README_RU.rst>`__
55
55
 
56
- .. image:: https://raw.githubusercontent.com/IDUclub/ObjectNat/main/docs/_static/ONlogo.svg
56
+ .. image:: https://raw.githubusercontent.com/IDUclub/ObjectNat/master/docs/_static/ONlogo.svg
57
57
  :align: center
58
58
  :width: 400
59
59
  :alt: ObjectNat logo
@@ -30,9 +30,9 @@ Object-oriented Network Analysis Tools
30
30
 
31
31
  |badge-black| |badge-pypi| |badge-ci| |badge-codecov| |badge-license| |badge-docs|
32
32
 
33
- `РИДМИ (Russian) <https://github.com/IDUclub/ObjectNat/blob/main/README_RU.rst>`__
33
+ `РИДМИ (Russian) <https://github.com/IDUclub/ObjectNat/blob/master/README_RU.rst>`__
34
34
 
35
- .. image:: https://raw.githubusercontent.com/IDUclub/ObjectNat/main/docs/_static/ONlogo.svg
35
+ .. image:: https://raw.githubusercontent.com/IDUclub/ObjectNat/master/docs/_static/ONlogo.svg
36
36
  :align: center
37
37
  :width: 400
38
38
  :alt: ObjectNat logo
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ObjectNat"
3
- version = "1.3.0"
3
+ version = "1.3.2"
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.2"
@@ -145,9 +145,9 @@ def simulate_noise(
145
145
  source_points = source_points.copy()
146
146
  if len(obstacles) > 0:
147
147
  obstacles = obstacles.copy()
148
- obstacles.geometry = obstacles.geometry.simplify(tolerance=1)
149
148
  local_crs = obstacles.estimate_utm_crs()
150
149
  obstacles.to_crs(local_crs, inplace=True)
150
+ obstacles.geometry = obstacles.geometry.simplify(tolerance=1)
151
151
  source_points.to_crs(local_crs, inplace=True)
152
152
  else:
153
153
  local_crs = source_points.estimate_utm_crs()
@@ -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():
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():
80
100
  return Polygon()
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.0"
File without changes