@@ -51,7 +51,8 @@ def _crop_dtm(dtm_path, tile_min_x, tile_min_y, tile_max_x, tile_max_y):
5151def process_with_tiles (ept_file , tile_size , output_path , metric , voxel_size ,
5252 voxel_height = 1 , buffer_size = 0.1 , srs = None , hag = False ,
5353 hag_dtm = False , dtm = None , bounds = None , interpolation = None , remove_outliers = False ,
54- cover_min_height : float = 2.0 , cover_k : float = 0.5 ) -> None :
54+ cover_min_height : float = 2.0 , cover_k : float = 0.5 ,
55+ skip_existing : bool = False , verbose : bool = False ) -> None :
5556 """
5657 Process a large EPT point cloud by tiling, compute CHM or other metrics for each tile,
5758 and write the results to the specified output directory.
@@ -75,6 +76,8 @@ def process_with_tiles(ept_file, tile_size, output_path, metric, voxel_size,
7576 remove_outliers (bool, optional): Whether to remove statistical outliers before calculating metrics. Defaults to False.
7677 cover_min_height (float, optional): Height threshold (in meters) for canopy cover (used when metric == "cover"). Defaults to 2.0.
7778 cover_k (float, optional): Beer–Lambert extinction coefficient for canopy cover. Defaults to 0.5.
79+ skip_existing (bool, optional): If True, skip tiles whose output file already exists. Defaults to False.
80+ verbose (bool, optional): If True, print warnings for empty/invalid tiles and buffer adjustments. Defaults to False.
7881
7982 Returns:
8083 None
@@ -126,7 +129,17 @@ def process_with_tiles(ept_file, tile_size, output_path, metric, voxel_size,
126129 tile_max_y = min (max_y , tile_max_y )
127130
128131 if tile_max_x <= tile_min_x or tile_max_y <= tile_min_y :
129- print (f"Warning: Skipping tile ({ i } , { j } ) due to invalid spatial extent." )
132+ if verbose :
133+ print (f"Warning: Skipping tile ({ i } , { j } ) due to invalid spatial extent." )
134+ pbar .update (1 )
135+ continue
136+
137+ if metric == "chm" :
138+ result_file = os .path .join (output_path , f"tile_{ i } _{ j } _chm.tif" )
139+ else :
140+ result_file = os .path .join (output_path , f"tile_{ i } _{ j } _{ metric } .tif" )
141+
142+ if skip_existing and os .path .isfile (result_file ):
130143 pbar .update (1 )
131144 continue
132145
@@ -164,7 +177,8 @@ def process_with_tiles(ept_file, tile_size, output_path, metric, voxel_size,
164177 tile_points = tile_pipeline .arrays [0 ]
165178
166179 if tile_points .size == 0 :
167- print (f"Warning: No data in tile ({ i } , { j } ). Skipping." )
180+ if verbose :
181+ print (f"Warning: No data in tile ({ i } , { j } ). Skipping." )
168182 pbar .update (1 )
169183 continue
170184
@@ -175,12 +189,29 @@ def process_with_tiles(ept_file, tile_size, output_path, metric, voxel_size,
175189 chm , extent = calculate_chm (tile_points , voxel_size , interpolation = interpolation )
176190
177191 if buffer_pixels_x * 2 >= chm .shape [1 ] or buffer_pixels_y * 2 >= chm .shape [0 ]:
178- print (
179- f"Warning: Buffer size exceeds CHM dimensions for tile ({ i } , { j } ). Adjusting buffer size." )
192+ if verbose :
193+ print (
194+ f"Warning: Buffer size exceeds CHM dimensions for tile ({ i } , { j } ). Adjusting buffer size." )
180195 buffer_pixels_x = max (0 , chm .shape [1 ] // 2 - 1 )
181196 buffer_pixels_y = max (0 , chm .shape [0 ] // 2 - 1 )
182197
183- chm = chm [buffer_pixels_y :- buffer_pixels_y , buffer_pixels_x :- buffer_pixels_x ]
198+ # Safe crop: avoid Python's -0 slice behavior producing empty arrays.
199+ if buffer_pixels_x < 0 or buffer_pixels_y < 0 :
200+ raise ValueError ("Computed negative buffer pixels; check voxel and buffer sizes." )
201+
202+ start_x = buffer_pixels_x
203+ end_x = chm .shape [1 ] - buffer_pixels_x if buffer_pixels_x > 0 else chm .shape [1 ]
204+ start_y = buffer_pixels_y
205+ end_y = chm .shape [0 ] - buffer_pixels_y if buffer_pixels_y > 0 else chm .shape [0 ]
206+
207+ # If cropping would yield an empty array due to small tiles, skip cropping
208+ if end_x <= start_x or end_y <= start_y :
209+ # Fall back to no buffer crop for this tile
210+ core = chm
211+ else :
212+ core = chm [start_y :end_y , start_x :end_x ]
213+
214+ chm = core
184215
185216 core_extent = (
186217 tile_min_x + buffer_x ,
@@ -189,7 +220,6 @@ def process_with_tiles(ept_file, tile_size, output_path, metric, voxel_size,
189220 tile_max_y - buffer_y ,
190221 )
191222
192- result_file = os .path .join (output_path , f"tile_{ i } _{ j } _chm.tif" )
193223 create_geotiff (chm , result_file , srs , core_extent )
194224 elif metric in ["fhd" , "pai" , "cover" ]:
195225 voxels , spatial_extent = assign_voxels (tile_points , voxel_size )
@@ -243,11 +273,11 @@ def process_with_tiles(ept_file, tile_size, output_path, metric, voxel_size,
243273 )
244274
245275 if core_extent [1 ] <= core_extent [0 ] or core_extent [3 ] <= core_extent [2 ]:
246- print (f"Warning: Invalid core extent for tile ({ i } , { j } ): { core_extent } . Skipping." )
276+ if verbose :
277+ print (f"Warning: Invalid core extent for tile ({ i } , { j } ): { core_extent } . Skipping." )
247278 pbar .update (1 )
248279 continue
249280
250- result_file = os .path .join (output_path , f"tile_{ i } _{ j } _{ metric } .tif" )
251281 create_geotiff (result , result_file , srs , core_extent )
252282 else :
253283 raise ValueError (f"Unsupported metric: { metric } " )
0 commit comments