Spaces:
Sleeping
fix(alignment-consistency): unify coordinate handling across all visualization modes and add validation
Browse filesCOMPREHENSIVE ALIGNMENT FIXES:
1. **Consistent Coordinate Handling**:
- Fixed Leaflet overlay to use geographic bounds approach (same as Plotly)
- Replaced array corner indexing [0,0], [0,-1] with lat/lon min/max bounds
- Applied proper orientation correction to GIF and PNG animation frames
- Ensured all visualization modes use same coordinate transformation logic
2. **Animation Alignment Fixes**:
- Added latitude orientation detection to GIF generation function
- Added latitude orientation detection to PNG frames function
- Apply np.flipud() when data is south-to-north ordered
- Consistent with Plotly and Leaflet coordinate handling
3. **Official NOAA Validation Section**:
- Added validation panel with links to official NOAA HRRR sources
- SPC HRRR Model Browser (Composite Reflectivity)
- NOAA HRRR CONUS Hourly Graphics
- NWS National Radar (NEXRAD)
- NOAA Graphical Forecast CONUS
- User guidance for validating alignment against official sources
4. **Enhanced Diagnostics**:
- Added coordinate logging for Leaflet overlays
- Consistent diagnostic output across all visualization modes
- Better validation and error reporting
This ensures ALL visualization modes (Plotly, Leaflet, GIF, PNG animations) use
the same coordinate system and orientation, providing consistent radar alignment.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
|
@@ -1044,12 +1044,24 @@ def generate_radar_animation_gif(detail_level: int = 5, min_dbz: float = 0.0):
|
|
| 1044 |
grid = process_hrrr_grid(ds, target_cells={1:20000,2:40000,3:60000,4:90000,5:120000}.get(int(detail_level), 120000), param_type='radar', min_threshold=float(min_dbz))
|
| 1045 |
if grid is None:
|
| 1046 |
continue
|
|
|
|
|
|
|
|
|
|
| 1047 |
z2d = grid['z2d']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1048 |
zmask = np.ma.masked_invalid(z2d)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1049 |
cmap = build_mpl_colormap(get_radar_colorscale())
|
| 1050 |
if cmap is None:
|
| 1051 |
continue
|
| 1052 |
-
ny, nx = z2d.shape
|
| 1053 |
scale_map = {1: 1.0, 2: 1.2, 3: 1.6, 4: 2.0, 5: 2.5}
|
| 1054 |
scale = scale_map.get(int(detail_level), 2.5)
|
| 1055 |
width = int(nx * scale)
|
|
@@ -1095,12 +1107,24 @@ def generate_radar_animation_png_frames(detail_level: int = 5, min_dbz: float =
|
|
| 1095 |
grid = process_hrrr_grid(ds, target_cells={1:20000,2:40000,3:60000,4:90000,5:120000}.get(int(detail_level), 120000), param_type='radar', min_threshold=float(min_dbz))
|
| 1096 |
if grid is None:
|
| 1097 |
continue
|
|
|
|
|
|
|
|
|
|
| 1098 |
z2d = grid['z2d']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1099 |
zmask = np.ma.masked_invalid(z2d)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1100 |
cmap = build_mpl_colormap(get_radar_colorscale())
|
| 1101 |
if cmap is None:
|
| 1102 |
continue
|
| 1103 |
-
ny, nx = z2d.shape
|
| 1104 |
scale_map = {1: 1.0, 2: 1.2, 3: 1.6, 4: 2.0, 5: 2.5}
|
| 1105 |
scale = scale_map.get(int(detail_level), 2.0)
|
| 1106 |
width = int(nx * scale)
|
|
@@ -1143,14 +1167,17 @@ def build_leaflet_overlay_from_frames(frame_data_urls: List[str], grid: Optional
|
|
| 1143 |
c_lat = float(np.nanmean(lat2d))
|
| 1144 |
c_lon = float(np.nanmean(lon2d))
|
| 1145 |
# Corner control points for HRRR Lambert Conformal grid transformation
|
| 1146 |
-
#
|
| 1147 |
ny, nx = lat2d.shape
|
| 1148 |
|
| 1149 |
-
#
|
| 1150 |
-
|
| 1151 |
-
|
| 1152 |
-
|
| 1153 |
-
|
|
|
|
|
|
|
|
|
|
| 1154 |
|
| 1155 |
# Validate corner coordinates are within expected CONUS bounds
|
| 1156 |
corners = [(lat_tl, lon_tl), (lat_tr, lon_tr), (lat_br, lon_br), (lat_bl, lon_bl)]
|
|
@@ -1978,6 +2005,20 @@ with gr.Blocks(title="HRRR Weather + Radar") as app:
|
|
| 1978 |
</div>
|
| 1979 |
""")
|
| 1980 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1981 |
with gr.Row():
|
| 1982 |
with gr.Column():
|
| 1983 |
location = gr.Textbox(value="Kansas City, MO", label="Location")
|
|
|
|
| 1044 |
grid = process_hrrr_grid(ds, target_cells={1:20000,2:40000,3:60000,4:90000,5:120000}.get(int(detail_level), 120000), param_type='radar', min_threshold=float(min_dbz))
|
| 1045 |
if grid is None:
|
| 1046 |
continue
|
| 1047 |
+
|
| 1048 |
+
lat2d = grid['lat2d']
|
| 1049 |
+
lon2d = grid['lon2d']
|
| 1050 |
z2d = grid['z2d']
|
| 1051 |
+
|
| 1052 |
+
# Apply same orientation correction as other visualizations
|
| 1053 |
+
ny, nx = lat2d.shape
|
| 1054 |
+
lat_top = float(lat2d[0, nx//2])
|
| 1055 |
+
lat_bottom = float(lat2d[-1, nx//2])
|
| 1056 |
+
|
| 1057 |
zmask = np.ma.masked_invalid(z2d)
|
| 1058 |
+
if lat_top < lat_bottom:
|
| 1059 |
+
# Data is ordered south-to-north, flip for proper display
|
| 1060 |
+
zmask = np.flipud(zmask)
|
| 1061 |
+
|
| 1062 |
cmap = build_mpl_colormap(get_radar_colorscale())
|
| 1063 |
if cmap is None:
|
| 1064 |
continue
|
|
|
|
| 1065 |
scale_map = {1: 1.0, 2: 1.2, 3: 1.6, 4: 2.0, 5: 2.5}
|
| 1066 |
scale = scale_map.get(int(detail_level), 2.5)
|
| 1067 |
width = int(nx * scale)
|
|
|
|
| 1107 |
grid = process_hrrr_grid(ds, target_cells={1:20000,2:40000,3:60000,4:90000,5:120000}.get(int(detail_level), 120000), param_type='radar', min_threshold=float(min_dbz))
|
| 1108 |
if grid is None:
|
| 1109 |
continue
|
| 1110 |
+
|
| 1111 |
+
lat2d = grid['lat2d']
|
| 1112 |
+
lon2d = grid['lon2d']
|
| 1113 |
z2d = grid['z2d']
|
| 1114 |
+
|
| 1115 |
+
# Apply same orientation correction as other visualizations
|
| 1116 |
+
ny, nx = lat2d.shape
|
| 1117 |
+
lat_top = float(lat2d[0, nx//2])
|
| 1118 |
+
lat_bottom = float(lat2d[-1, nx//2])
|
| 1119 |
+
|
| 1120 |
zmask = np.ma.masked_invalid(z2d)
|
| 1121 |
+
if lat_top < lat_bottom:
|
| 1122 |
+
# Data is ordered south-to-north, flip for proper display
|
| 1123 |
+
zmask = np.flipud(zmask)
|
| 1124 |
+
|
| 1125 |
cmap = build_mpl_colormap(get_radar_colorscale())
|
| 1126 |
if cmap is None:
|
| 1127 |
continue
|
|
|
|
| 1128 |
scale_map = {1: 1.0, 2: 1.2, 3: 1.6, 4: 2.0, 5: 2.5}
|
| 1129 |
scale = scale_map.get(int(detail_level), 2.0)
|
| 1130 |
width = int(nx * scale)
|
|
|
|
| 1167 |
c_lat = float(np.nanmean(lat2d))
|
| 1168 |
c_lon = float(np.nanmean(lon2d))
|
| 1169 |
# Corner control points for HRRR Lambert Conformal grid transformation
|
| 1170 |
+
# Use geographic bounds approach (same as Plotly) for consistent alignment
|
| 1171 |
ny, nx = lat2d.shape
|
| 1172 |
|
| 1173 |
+
# HRRR uses curvilinear Lambert Conformal grid - use geographic bounds for corners
|
| 1174 |
+
# This matches the approach used in the Plotly visualization for consistency
|
| 1175 |
+
lat_tl, lon_tl = max_lat, min_lon # Top-left: northern edge, western edge
|
| 1176 |
+
lat_tr, lon_tr = max_lat, max_lon # Top-right: northern edge, eastern edge
|
| 1177 |
+
lat_br, lon_br = min_lat, max_lon # Bottom-right: southern edge, eastern edge
|
| 1178 |
+
lat_bl, lon_bl = min_lat, min_lon # Bottom-left: southern edge, western edge
|
| 1179 |
+
|
| 1180 |
+
print(f"Leaflet corners using geographic bounds: TL({lat_tl:.3f},{lon_tl:.3f}) TR({lat_tr:.3f},{lon_tr:.3f}) BR({lat_br:.3f},{lon_br:.3f}) BL({lat_bl:.3f},{lon_bl:.3f})")
|
| 1181 |
|
| 1182 |
# Validate corner coordinates are within expected CONUS bounds
|
| 1183 |
corners = [(lat_tl, lon_tl), (lat_tr, lon_tr), (lat_br, lon_br), (lat_bl, lon_bl)]
|
|
|
|
| 2005 |
</div>
|
| 2006 |
""")
|
| 2007 |
|
| 2008 |
+
gr.HTML("""
|
| 2009 |
+
<div style="background: #f8f9fa; padding: 1rem; border-radius: 8px; margin-bottom: 1rem;">
|
| 2010 |
+
<h3>📍 Validate Radar Alignment Against Official NOAA Sources</h3>
|
| 2011 |
+
<p>Compare our radar overlay alignment with these official NOAA HRRR visualizations:</p>
|
| 2012 |
+
<ul style="margin: 0.5rem 0;">
|
| 2013 |
+
<li><strong>HRRR Model Browser:</strong> <a href="https://www.spc.noaa.gov/exper/hrrr/" target="_blank">SPC HRRR Composite Reflectivity</a></li>
|
| 2014 |
+
<li><strong>HRRR CONUS Hourly:</strong> <a href="https://rapidrefresh.noaa.gov/hrrr/HRRR/Welcome.cgi?dsKey=hrrr_ncep_jet" target="_blank">NOAA HRRR Graphics</a></li>
|
| 2015 |
+
<li><strong>National Radar:</strong> <a href="https://radar.weather.gov/" target="_blank">NWS Radar (NEXRAD)</a></li>
|
| 2016 |
+
<li><strong>Graphical Forecast:</strong> <a href="https://graphical.weather.gov/sectors/conus.php" target="_blank">NOAA CONUS Graphics</a></li>
|
| 2017 |
+
</ul>
|
| 2018 |
+
<p><em>💡 Tip: Use the same forecast time and look for matching radar patterns, storm positions, and geographic alignment with cities/coastlines.</em></p>
|
| 2019 |
+
</div>
|
| 2020 |
+
""")
|
| 2021 |
+
|
| 2022 |
with gr.Row():
|
| 2023 |
with gr.Column():
|
| 2024 |
location = gr.Textbox(value="Kansas City, MO", label="Location")
|