Add multi-GPU support and fix scale bar / location map overlap
Multi-GPU: - gpu.py: lazy CuPy initialization so CUDA_VISIBLE_DEVICES takes effect before context creation in worker processes - gpu.py: detect GPU count via nvidia-smi (no CUDA import needed) - gpu.py: add set_active_gpu() to assign workers to specific GPUs - pipeline.py: distribute files across GPUs (file % num_gpus) in parallel mode so both GPUs are used simultaneously - pipeline.py: log GPU count when multiple GPUs detected Layout fixes: - rendering.py: move scale bar left of location map to avoid overlap (scale bar ends at fig_x=0.78, map starts at 0.82) - rendering.py: expand location map inset to 0.16x0.13 fig coords - rendering.py: return bounds from _download_location_map so imshow extent matches the actual IGN tile coverage (80km context) - ign.py: add min_zoom parameter to download_ign_tiles, fixing the location map that was broken (zoom 10 blocked by hardcoded min_zoom=15)
This commit is contained in:
@ -274,15 +274,16 @@ def _apply_colormap(data, tif_file):
|
||||
def _download_location_map(min_x, max_x, min_y, max_y):
|
||||
"""Download a wide-area IGN topographic map for location context.
|
||||
|
||||
Downloads a zoomed-out IGN PLANIGNV2 tile covering 5-10x the processed
|
||||
zone extent, giving a wider geographic context. Results are cached to
|
||||
Downloads a zoomed-out IGN PLANIGNV2 tile covering ~80km around the
|
||||
processed zone, giving wider geographic context. Results are cached to
|
||||
avoid re-downloading for each visualization in the same tile.
|
||||
|
||||
Args:
|
||||
min_x, max_x, min_y, max_y: DTM bounds in Lambert 93.
|
||||
|
||||
Returns:
|
||||
numpy array (H, W, 3) uint8, or None on failure.
|
||||
Tuple (image_array, bounds_dict) where bounds_dict has keys
|
||||
'min_x', 'max_x', 'min_y', 'max_y' in Lambert 93, or None on failure.
|
||||
"""
|
||||
import hashlib
|
||||
|
||||
@ -323,7 +324,13 @@ def _download_location_map(min_x, max_x, min_y, max_y):
|
||||
)
|
||||
|
||||
if result is not None:
|
||||
_location_map_cache[cache_key] = result
|
||||
bounds = {
|
||||
'min_x': context_min_x, 'max_x': context_max_x,
|
||||
'min_y': context_min_y, 'max_y': context_max_y,
|
||||
}
|
||||
cached = (result, bounds)
|
||||
_location_map_cache[cache_key] = cached
|
||||
return cached
|
||||
|
||||
return result
|
||||
except Exception as e:
|
||||
@ -637,6 +644,7 @@ def tif_to_png(tif_file, vis_dir, resolution, keep_tif=False, source_info=None,
|
||||
edgecolor='#cccccc', alpha=0.9))
|
||||
|
||||
# Scale bar — adaptive with alternating black/white segments
|
||||
# Position: left of the location map to avoid overlap
|
||||
extent_m_x = max_x - min_x
|
||||
scale_m, scale_label = _nice_scale(extent_m_x)
|
||||
pixels_per_meter = 1.0 / pixel_size_x
|
||||
@ -646,7 +654,17 @@ def tif_to_png(tif_file, vis_dir, resolution, keep_tif=False, source_info=None,
|
||||
bar_bottom_y = 0.55
|
||||
bar_top_y = 0.85
|
||||
bar_height = bar_top_y - bar_bottom_y
|
||||
scale_start_x = 0.80
|
||||
# Place scale bar so it ends before the location map (map starts at x=0.82 in fig coords)
|
||||
# map_ax occupies [0.82, 0.02, 0.16, 0.13] in figure coords
|
||||
# info_ax occupies [data_left, 0.015, width, 0.09]
|
||||
# Scale bar end in fig coords = info_ax.left + (scale_start_x + scale_px/width) * info_ax.width
|
||||
# We need: info_ax.left + (scale_start_x + scale_px/width) * info_ax.width < 0.80
|
||||
scale_end_frac = scale_px / width # fraction of info_ax width
|
||||
info_ax_width = data_width_frac + cbar_width + 0.02
|
||||
# Calculate scale_start_x so scale bar ends at fig_x = 0.78 (leaving gap before map at 0.82)
|
||||
max_scale_end_fig = 0.78
|
||||
scale_end_in_info = (max_scale_end_fig - data_left) / info_ax_width
|
||||
scale_start_x = max(0.05, scale_end_in_info - scale_end_frac)
|
||||
|
||||
for seg_i in range(n_segments):
|
||||
color = 'black' if seg_i % 2 == 0 else 'white'
|
||||
@ -666,15 +684,17 @@ def tif_to_png(tif_file, vis_dir, resolution, keep_tif=False, source_info=None,
|
||||
color='black', linewidth=1, transform=info_ax.transAxes, clip_on=False)
|
||||
|
||||
# Location inset map — IGN topographic background with processed zone marker
|
||||
map_ax = fig.add_axes([0.84, 0.02, 0.14, 0.12])
|
||||
# Positioned in lower-right corner, above the info bar
|
||||
map_ax = fig.add_axes([0.82, 0.02, 0.16, 0.13])
|
||||
|
||||
# Try to download a wide-area IGN topo map for location context
|
||||
location_map = _download_location_map(min_x, max_x, min_y, max_y)
|
||||
if location_map is not None:
|
||||
# Draw IGN topo map as background
|
||||
location_result = _download_location_map(min_x, max_x, min_y, max_y)
|
||||
if location_result is not None:
|
||||
location_map, loc_bounds = location_result
|
||||
# Draw IGN topo map as background with correct bounds
|
||||
map_ax.imshow(location_map, aspect='equal', extent=[
|
||||
min_x - (max_x - min_x) * 2, max_x + (max_x - min_x) * 2,
|
||||
min_y - (max_y - min_y) * 2, max_y + (max_y - min_y) * 2
|
||||
loc_bounds['min_x'], loc_bounds['max_x'],
|
||||
loc_bounds['min_y'], loc_bounds['max_y']
|
||||
])
|
||||
# Mark the processed zone with a red rectangle
|
||||
rect_x1, rect_x2 = min_x, max_x
|
||||
|
||||
Reference in New Issue
Block a user