Skip to content

Commit c3f57c2

Browse files
committed
- Added garbage collector
- Updated incline in batch of 1000 and after each batch freeing some memory
1 parent aa962f6 commit c3f57c2

File tree

3 files changed

+61
-45
lines changed

3 files changed

+61
-45
lines changed

src/osw_incline/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .version import __version__
88
from .dem_processor import DEMProcessor
99

10+
1011
class OSWIncline:
1112
def __init__(self, dem_files: List[str], nodes_file: str, edges_file: str, debug=False):
1213
self.dem_files = dem_files
@@ -16,7 +17,6 @@ def __init__(self, dem_files: List[str], nodes_file: str, edges_file: str, debug
1617
if self.debug:
1718
Logger.debug('Debug mode is enabled')
1819

19-
2020
def calculate(self):
2121
try:
2222
if self.debug:
@@ -35,10 +35,13 @@ def calculate(self):
3535
nodes_path=graph_nodes_path,
3636
edges_path=graph_edges_path
3737
)
38+
39+
# Delete osm_graph and dem_processor to force garbage collection
40+
del osm_graph, dem_processor
41+
gc.collect()
42+
3843
end_time = time.time()
3944
time_taken = end_time - start_time
40-
del osm_graph
41-
del dem_processor
4245
if self.debug:
4346
Logger.info(f'Entire processing took: {time_taken} seconds')
4447
return True
@@ -50,5 +53,4 @@ def calculate(self):
5053
gc.collect()
5154

5255

53-
54-
OSWIncline.__version__ = __version__
56+
OSWIncline.__version__ = __version__

src/osw_incline/dem_processor.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,17 @@ def __init__(self, osm_graph: OSMGraph, dem_files: List[str], debug=False):
2323
self.debug = debug
2424

2525
def process(self, nodes_path, edges_path):
26+
gc.disable()
2627
for dem_file in self.dem_files:
2728
dem_file_path = Path(dem_file)
2829
if self.debug:
2930
Logger.debug(f'Processing DEM tile: {dem_file_path}')
3031

3132
try:
3233
with rasterio.open(dem_file_path) as dem:
33-
for u, v, d in self.OG.G.edges(data=True):
34-
if 'geometry' in d:
35-
incline = self.infer_incline(linestring=d['geometry'], dem=dem, precision=3)
36-
if incline is not None:
37-
# Add incline to the edge properties
38-
d['incline'] = incline
39-
else:
40-
if self.debug:
41-
Logger.info(f'No geometry found for edge {u}-{v}')
42-
43-
dem.close()
34+
edges = list(self.OG.G.edges(data=True)) # Get all edges, even if fewer than batch_size
35+
self._process_in_batches(edges, dem, batch_size=1000)
36+
4437
self.OG.to_geojson(nodes_path, edges_path)
4538
except rasterio.errors.RasterioIOError:
4639
if self.debug:
@@ -53,8 +46,19 @@ def process(self, nodes_path, edges_path):
5346
finally:
5447
gc.collect()
5548

56-
del self.OG
57-
gc.collect()
49+
gc.disable()
50+
51+
def _process_in_batches(self, edges, dem, batch_size=1000):
52+
# Process edges in batches
53+
for i in range(0, len(edges), batch_size):
54+
batch = edges[i:i + batch_size]
55+
for u, v, d in batch:
56+
if 'geometry' in d:
57+
incline = self.infer_incline(linestring=d['geometry'], dem=dem, precision=3)
58+
if incline is not None:
59+
d['incline'] = incline
60+
# Trigger garbage collection after each batch
61+
gc.collect()
5862

5963
def infer_incline(self, linestring, dem, precision=3):
6064
first_point = linestring.coords[0]
@@ -161,6 +165,7 @@ def interpolated_value(self, x, y, dem, method='idw', scaling_factor=1.0):
161165

162166
interpolated = interpolator(dx, dy, dem_arr)
163167

168+
del dem_arr
164169
if interpolated is None:
165170
return interpolated
166171
else:
@@ -194,6 +199,7 @@ def idw(self, dx, dy, masked_array):
194199

195200
value = weighted_values.sum()
196201

202+
del xs, ys, values_masked, weighted_values
197203

198204
if np.isnan(value):
199205
return None
@@ -217,4 +223,4 @@ def bivariate_spline(self, dx, dy, arr):
217223
spline = RectBivariateSpline(
218224
np.array(range(ncol)), np.array(range(nrow)), arr, kx=kx, ky=ky
219225
)
220-
return spline(dx, dy)[0][0]
226+
return spline(dx, dy)[0][0]

src/osw_incline/osm_graph.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import networkx as nx
55
from shapely.geometry import shape, mapping
66

7+
SCHEMA = 'https://sidewalks.washington.edu/opensidewalks/0.2/schema.json'
78

89
class OSMGraph:
910
def __init__(self, G=None):
@@ -30,14 +31,16 @@ def from_geojson(cls, nodes_path, edges_path):
3031
props['geometry'] = shape(node_feature['geometry'])
3132
G.add_node(n, **props)
3233

34+
del nodes_fc
35+
gc.collect()
36+
3337
for edge_feature in edges_fc['features']:
3438
props = edge_feature['properties']
3539
u = props.pop('_u_id')
3640
v = props.pop('_v_id')
3741
props['geometry'] = shape(edge_feature['geometry'])
3842
G.add_edge(u, v, **props)
3943

40-
del nodes_fc
4144
del edges_fc
4245
gc.collect()
4346

@@ -46,15 +49,6 @@ def from_geojson(cls, nodes_path, edges_path):
4649
def to_geojson(self, *args):
4750
nodes_path = args[0]
4851
edges_path = args[1]
49-
50-
# Load the original files to retain the original top-level keys
51-
with open(nodes_path) as f:
52-
original_nodes_fc = json.load(f)
53-
54-
with open(edges_path) as f:
55-
original_edges_fc = json.load(f)
56-
57-
# Process the edges
5852
edge_features = []
5953
for u, v, d in self.G.edges(data=True):
6054
d_copy = {**d}
@@ -72,11 +66,19 @@ def to_geojson(self, *args):
7266
'geometry': geometry,
7367
'properties': d_copy
7468
})
69+
edges_fc = {
70+
'type': 'FeatureCollection',
71+
'features': edge_features,
72+
'$schema': SCHEMA
73+
}
7574

76-
# Update the original edges feature collection
77-
original_edges_fc['features'] = edge_features
75+
with open(edges_path, 'w') as f:
76+
json.dump(edges_fc, f)
77+
78+
# Delete edge_features and force garbage collection
79+
del edge_features, edges_fc
80+
gc.collect()
7881

79-
# Process the nodes
8082
node_features = []
8183
for n, d in self.G.nodes(data=True):
8284
d_copy = {**d}
@@ -99,18 +101,19 @@ def to_geojson(self, *args):
99101
'geometry': geometry,
100102
'properties': d_copy
101103
})
102-
103-
# Update the original nodes feature collection
104-
original_nodes_fc['features'] = node_features
105-
106-
# Write the updated nodes and edges to the files
107-
with open(edges_path, 'w') as f:
108-
json.dump(original_edges_fc, f)
104+
nodes_fc = {
105+
'type': 'FeatureCollection',
106+
'features': node_features,
107+
'$schema': SCHEMA
108+
}
109109

110110
with open(nodes_path, 'w') as f:
111-
json.dump(original_nodes_fc, f)
111+
json.dump(nodes_fc, f)
112+
113+
# Delete node_features and force garbage collection
114+
del node_features, nodes_fc
115+
gc.collect()
112116

113-
# Handle points if the third argument (points_path) is provided
114117
if len(args) == 3:
115118
points_path = args[2]
116119
point_features = []
@@ -137,10 +140,15 @@ def to_geojson(self, *args):
137140
'geometry': geometry,
138141
'properties': d_copy
139142
})
143+
points_fc = {
144+
'type': 'FeatureCollection',
145+
'features': point_features,
146+
'$schema': SCHEMA
147+
}
140148

141149
with open(points_path, 'w') as f:
142-
json.dump({'type': 'FeatureCollection', 'features': point_features}, f)
150+
json.dump(points_fc, f)
143151

144-
del original_nodes_fc
145-
del original_edges_fc
146-
gc.collect()
152+
# Delete point_features and force garbage collection
153+
del point_features, points_fc
154+
gc.collect()

0 commit comments

Comments
 (0)