{"id":503,"date":"2025-10-19T16:22:54","date_gmt":"2025-10-19T20:22:54","guid":{"rendered":"https:\/\/scootercam.net\/notes\/?p=503"},"modified":"2025-10-19T16:32:03","modified_gmt":"2025-10-19T20:32:03","slug":"scootercam-wave-height-layered-views","status":"publish","type":"post","link":"https:\/\/scootercam.net\/blog\/scootercam-wave-height-layered-views\/","title":{"rendered":"ScooterCam Wave Height &#8211; Layered Views"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<p><strong>Version:<\/strong> 3.0<br><strong>Author:<\/strong> ScooterCam<br><strong>For:<\/strong> ScooterCam.net<\/p>\n\n\n\n<p>Clean, discrete layers for buoy observations and forecast models &#8211; <strong>no more mixing yesterday&#8217;s data!<\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfaf Why Layered Views?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The Problem with Hybrid<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Forecast data was bleeding into yesterday<\/li>\n\n\n\n<li>Timezone confusion between data sources<\/li>\n\n\n\n<li>Hard to see which data is which<\/li>\n\n\n\n<li>Mixed data = messy debugging<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">The Solution: Separate Layers<\/h3>\n\n\n\n<p>\u2705 <strong>Buoy layer<\/strong> = Past 48 hours of real observations<br>\u2705 <strong>Forecast layer<\/strong> = Next 48 hours of predictions<br>\u2705 <strong>No mixing<\/strong> = No timezone confusion<br>\u2705 <strong>Clear data source<\/strong> = Easy debugging<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf0a Three Shortcodes, Three Use Cases<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. <code>[wave_buoy]<\/code> &#8211; Buoy Observations Only<\/h3>\n\n\n\n<p><strong>Shows:<\/strong> Past 48 hours of real buoy measurements<br><strong>When to use:<\/strong> Historical data, validation, actual conditions<br><strong>Data source:<\/strong> NDBC Buoy 45168 (South Haven)<\/p>\n\n\n<p>[wave_buoy]<\/p>\n\n\n\n<p>24<\/p>\n\n\n<p>[wave_buoy hours=&#8221;24&#8243;]<\/p>\n\n\n\n<p>72<\/p>\n\n\n<p>[wave_buoy hours=&#8221;72&#8243;]<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;wave_buoy]\n&#91;wave_buoy hours=\"24\"]  \/\/ Last 24 hours only\n&#91;wave_buoy hours=\"72\"]  \/\/ Last 3 days\n<\/code><\/pre>\n\n\n\n<p><strong>Features:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Real measurements from 12 miles offshore<\/li>\n\n\n\n<li>\u2705 Current wave stats (height, period, direction)<\/li>\n\n\n\n<li>\u2705 Solid blue line<\/li>\n\n\n\n<li>\u2705 Seasonal (April-October)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">2. <code>[wave_forecast]<\/code> &#8211; Forecast Only<\/h3>\n\n\n\n<p><strong>Shows:<\/strong> Next 48 hours of wave predictions<br><strong>When to use:<\/strong> Planning, future conditions<br><strong>Data source:<\/strong> Open-Meteo marine model<\/p>\n\n\n<p>[wave_forecast]<\/p>\n\n\n<p>[wave_forecast hours=&#8221;24&#8243;]<\/p>\n\n\n<p>[wave_forecast hours=&#8221;72&#8243;]<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;wave_forecast]\n&#91;wave_forecast hours=\"24\"]  \/\/ Next 24 hours\n&#91;wave_forecast hours=\"72\"]  \/\/ Next 3 days\n<\/code><\/pre>\n\n\n\n<p><strong>Features:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Future predictions from now forward<\/li>\n\n\n\n<li>\u2705 No historical\/yesterday data<\/li>\n\n\n\n<li>\u2705 Dashed orange line<\/li>\n\n\n\n<li>\u2705 Year-round availability<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3. <code>[wave_layered]<\/code> &#8211; Tabbed Interface<\/h3>\n\n\n\n<p><strong>Shows:<\/strong> Both views with tab switcher<br><strong>When to use:<\/strong> Complete conditions page<br><strong>Data sources:<\/strong> Both buoy + forecast<\/p>\n\n\n<p>[wave_layered]<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;wave_layered]\n<\/code><\/pre>\n\n\n\n<p><strong>Features:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Tab 1: Buoy Observations (real data)<\/li>\n\n\n\n<li>\u2705 Tab 2: Forecast Model (predictions)<\/li>\n\n\n\n<li>\u2705 Single shortcode, both views<\/li>\n\n\n\n<li>\u2705 Easy switching between layers<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udce6 Installation<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">File Structure<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/wp-content\/plugins\/scootercam-wave-layered\/\n\u251c\u2500\u2500 wave-height-layered.php\n\u251c\u2500\u2500 js\/\n\u2502   \u2514\u2500\u2500 wave-layered-chart.js\n\u2514\u2500\u2500 README.md\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Upload &amp; Activate<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Upload to <code>\/wp-content\/plugins\/scootercam-wave-layered\/<\/code><\/li>\n\n\n\n<li>WordPress Admin \u2192 Plugins \u2192 Activate<\/li>\n\n\n\n<li>Use any of the three shortcodes!<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfa8 Visual Examples<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Buoy Layer (Past Data)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Yesterday \u2190\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 Now\n        48h of observations\n        Solid blue line\n        Real measurements\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Forecast Layer (Future Data)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>        Now \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 +48h\n        48h of predictions\n        Dashed orange line\n        Model forecasts\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Layered View (Both)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 \ud83c\udf0a Buoy | \ud83d\udcca Forecast  \u2502 \u2190 Tabs\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502                         \u2502\n\u2502   &#91;Active layer here]   \u2502\n\u2502                         \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd27 How It Works<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Separate AJAX Endpoints<\/h3>\n\n\n\n<p><strong>Buoy Endpoint:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wp_ajax_get_buoy_data\n\u2192 Fetches NDBC 45168\n\u2192 Returns past X hours\n\u2192 Cached 30 minutes\n<\/code><\/pre>\n\n\n\n<p><strong>Forecast Endpoint:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wp_ajax_get_forecast_data  \n\u2192 Fetches Open-Meteo\n\u2192 Returns ONLY future data\n\u2192 Cached 1 hour\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">No More Timezone Issues!<\/h3>\n\n\n\n<p><strong>Old hybrid approach:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Mixed buoy (UTC) + forecast (Chicago)\n\/\/ Comparison: time() vs strtotime()\n\/\/ Result: Yesterday bleeds through \u274c\n<\/code><\/pre>\n\n\n\n<p><strong>New layered approach:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Buoy: Shows past only\n\/\/ Forecast: Shows future only\n\/\/ No comparison needed \u2705\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfaf Usage Examples<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Main Conditions Page<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;h1&gt;Lake Michigan Wave Conditions&lt;\/h1&gt;\n\n&lt;h2&gt;Current Observations&lt;\/h2&gt;<\/code><\/pre>\n\n\n<p>[wave_buoy hours=&#8221;48&#8243;]<\/p>\n\n\n\n<p>&lt;h2&gt;48-Hour Forecast&lt;\/h2&gt;<\/p>\n\n\n<p>[wave_forecast hours=&#8221;48&#8243;]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tabbed Interface<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;h1&gt;Wave Data&lt;\/h1&gt;<\/code><\/pre>\n\n\n<p>[wave_layered]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Sidebar Widget<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;h3&gt;Current Waves&lt;\/h3&gt;<\/code><\/pre>\n\n\n<p>[wave_buoy hours=&#8221;6&#8243;]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Blog Post Embed<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;p&gt;Check today's wave forecast:&lt;\/p&gt;<\/code><\/pre>\n\n\n<p>[wave_forecast hours=&#8221;24&#8243;]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Comparison View<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 20px;\"&gt;\n  &lt;div&gt;\n    &lt;h3&gt;What Just Happened&lt;\/h3&gt;<\/code><\/pre>\n\n\n<p>[wave_buoy hours=&#8221;24&#8243;]<\/p>\n\n\n\n<p>&lt;\/div&gt; &lt;div&gt; &lt;h3&gt;What&#8217;s Coming&lt;\/h3&gt;<\/p>\n\n\n<p>[wave_forecast hours=&#8221;24&#8243;]<\/p>\n\n\n\n<p>&lt;\/div&gt; &lt;\/div&gt;<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcca Data Specifications<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Buoy Layer (45168)<\/h3>\n\n\n\n<p><strong>Location:<\/strong> 42.397\u00b0N, 86.331\u00b0W<br><strong>Distance:<\/strong> 12 miles west of South Haven<br><strong>Active:<\/strong> April &#8211; October<br><strong>Update:<\/strong> Hourly<\/p>\n\n\n\n<p><strong>Returns:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wave height (meters)<\/li>\n\n\n\n<li>Wave period (seconds)<\/li>\n\n\n\n<li>Wave direction (degrees)<\/li>\n\n\n\n<li>Timestamps in Chicago timezone<\/li>\n<\/ul>\n\n\n\n<p><strong>Behavior when offline:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Shows friendly message<\/li>\n\n\n\n<li>Explains seasonal deployment<\/li>\n\n\n\n<li>No chart displayed<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Forecast Layer<\/h3>\n\n\n\n<p><strong>Location:<\/strong> 42.5593\u00b0N, 86.2359\u00b0W (South Haven)<br><strong>Source:<\/strong> Open-Meteo marine model<br><strong>Active:<\/strong> Year-round<br><strong>Update:<\/strong> Every 6 hours<\/p>\n\n\n\n<p><strong>Returns:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Wave height forecast (meters)<\/li>\n\n\n\n<li>Wave period forecast (seconds)<\/li>\n\n\n\n<li>Wave direction forecast (degrees)<\/li>\n\n\n\n<li>ONLY future timestamps<\/li>\n<\/ul>\n\n\n\n<p><strong>Filtering:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Server-side filter\nif ($timestamp &lt;= $nowTimestamp) {\n    continue; \/\/ Skip past data\n}\n\n\/\/ Guaranteed: Only shows future\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udc1b Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Issue: Forecast Still Shows Yesterday<\/h3>\n\n\n\n<p><strong>This shouldn&#8217;t happen anymore!<\/strong> But if it does:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Clear transients:<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>wp transient delete openmeteo_forecast\n<\/code><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Check server time:<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>echo date('Y-m-d H:i:s T');  \/\/ Should match reality\n<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Verify timezone:<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>WordPress Admin \u2192 Settings \u2192 General\nTimezone: America\/Chicago\n<\/code><\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>Hard refresh:<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>Ctrl+Shift+R (Windows\/Linux)\nCmd+Shift+R (Mac)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Issue: Buoy Shows &#8220;Seasonal&#8221; Message<\/h3>\n\n\n\n<p><strong>This is normal November-April!<\/strong><\/p>\n\n\n\n<p>The South Haven buoy is physically removed from the water in fall and redeployed in spring. This is expected behavior.<\/p>\n\n\n\n<p><strong>To verify it&#8217;s actually off-season:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Check: https:\/\/www.ndbc.noaa.gov\/station_page.php?station=45168<\/li>\n\n\n\n<li>Look for recent data (should be none)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Issue: Charts Not Loading<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Check browser console (F12):<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Look for errors like:\n\"Failed to fetch\"\n\"Chart is not a constructor\"\n\"waveLayeredData is not defined\"\n<\/code><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Verify AJAX endpoints:<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code># Test buoy endpoint\ncurl -X POST https:\/\/scootercam.net\/wp-admin\/admin-ajax.php \\\n  -d \"action=get_buoy_data&amp;nonce=...\"\n\n# Test forecast endpoint  \ncurl -X POST https:\/\/scootercam.net\/wp-admin\/admin-ajax.php \\\n  -d \"action=get_forecast_data&amp;nonce=...\"\n<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Clear ALL caches:<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>wp transient delete --all\nwp cache flush\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udd04 Migration from Hybrid<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">If Currently Using Hybrid Plugin<\/h3>\n\n\n\n<p><strong>Option 1: Run Both (Recommended)<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Keep hybrid plugin active<\/li>\n\n\n\n<li>Install layered plugin<\/li>\n\n\n\n<li>Test layered on separate page<\/li>\n\n\n\n<li>Gradually migrate<\/li>\n<\/ul>\n\n\n\n<p><strong>Option 2: Replace Entirely<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Old: &#91;wave_hybrid_chart]\nNew: &#91;wave_layered]\n<\/code><\/pre>\n\n\n\n<p><strong>Option 3: Separate Displays<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Old: &#91;wave_hybrid_chart]\nNew: &#91;wave_buoy] + &#91;wave_forecast]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Shortcode Conversion<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Old Hybrid<\/th><th>New Layered<\/th><\/tr><\/thead><tbody><tr><td><code>[wave_hybrid_chart]<\/code><\/td><td><code>[wave_layered]<\/code><\/td><\/tr><tr><td><code>[wave_hybrid_chart show_buoy=\"true\" show_forecast=\"false\"]<\/code><\/td><td><code>[wave_buoy]<\/code><\/td><\/tr><tr><td><code>[wave_hybrid_chart show_buoy=\"false\" show_forecast=\"true\"]<\/code><\/td><td><code>[wave_forecast]<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfa8 Customization<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Tab Colors<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>.wave-tab.active {\n    background: #10b981; \/* Green instead of blue *\/\n}\n\n.wave-tab-badge {\n    background: rgba(255,255,255,0.3);\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Chart Heights<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>.wave-chart-wrapper {\n    height: 500px !important; \/* Taller charts *\/\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Buoy Chart Color<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ In wave-layered-chart.js\nborderColor: '#0ea5e9',  \/\/ Sky blue instead of blue\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Forecast Chart Color<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ In wave-layered-chart.js\nborderColor: '#8b5cf6',  \/\/ Purple instead of orange\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcc8 Performance<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Cache Strategy<\/h3>\n\n\n\n<p><strong>Buoy data:<\/strong> 30 minutes<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Real-time not critical<\/li>\n\n\n\n<li>Reduces NDBC server load<\/li>\n\n\n\n<li>Balances freshness vs performance<\/li>\n<\/ul>\n\n\n\n<p><strong>Forecast data:<\/strong> 1 hour<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Model updates every 6 hours<\/li>\n\n\n\n<li>No need for frequent checks<\/li>\n\n\n\n<li>Significant performance gain<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Load Times<\/h3>\n\n\n\n<p><strong>Single layer (buoy or forecast):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Initial load: &lt;1 second<\/li>\n\n\n\n<li>AJAX call: 1-2 seconds<\/li>\n\n\n\n<li>Chart render: &lt;100ms<\/li>\n\n\n\n<li>Total: 2-3 seconds<\/li>\n<\/ul>\n\n\n\n<p><strong>Layered view (tabs):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Both layers load on page load<\/li>\n\n\n\n<li>Slightly slower: 3-4 seconds<\/li>\n\n\n\n<li>But switching tabs is instant<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Advantages Over Hybrid<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Data Integrity<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 No mixed timeframes<\/li>\n\n\n\n<li>\u2705 No timezone confusion<\/li>\n\n\n\n<li>\u2705 Clear data boundaries<\/li>\n\n\n\n<li>\u2705 Easy validation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Debugging<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Simple data flow<\/li>\n\n\n\n<li>\u2705 Separate endpoints<\/li>\n\n\n\n<li>\u2705 Clear error messages<\/li>\n\n\n\n<li>\u2705 Isolated failures<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">User Experience<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Choose what to see<\/li>\n\n\n\n<li>\u2705 Understand data source<\/li>\n\n\n\n<li>\u2705 No confusing overlaps<\/li>\n\n\n\n<li>\u2705 Clean visual separation<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Maintenance<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Easier to update<\/li>\n\n\n\n<li>\u2705 Independent caching<\/li>\n\n\n\n<li>\u2705 Clearer code<\/li>\n\n\n\n<li>\u2705 Better testability<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udd9a When to Use Which<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Use <code>[wave_buoy]<\/code> when:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Want actual observed data<\/li>\n\n\n\n<li>\u2705 Validating forecast accuracy<\/li>\n\n\n\n<li>\u2705 Showing recent trends<\/li>\n\n\n\n<li>\u2705 It&#8217;s buoy season (Apr-Oct)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Use <code>[wave_forecast]<\/code> when:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Planning future activities<\/li>\n\n\n\n<li>\u2705 Need predictions<\/li>\n\n\n\n<li>\u2705 It&#8217;s off-season (Nov-Mar)<\/li>\n\n\n\n<li>\u2705 Want clean forward-looking data<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Use <code>[wave_layered]<\/code> when:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Want complete picture<\/li>\n\n\n\n<li>\u2705 Professional display<\/li>\n\n\n\n<li>\u2705 Main conditions page<\/li>\n\n\n\n<li>\u2705 Let users choose view<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcdd Best Practices<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Page Layout<\/h3>\n\n\n\n<p><strong>Good:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;h1&gt;Wave Conditions&lt;\/h1&gt;<\/code><\/pre>\n\n\n<p>[wave_layered]<\/p>\n\n\n\n<p>&lt;!&#8211; Single comprehensive view &#8211;&gt;<\/p>\n\n\n\n<p><strong>Also Good:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;h2&gt;Recent Observations&lt;\/h2&gt;<\/code><\/pre>\n\n\n<p>[wave_buoy hours=&#8221;24&#8243;]<\/p>\n\n\n\n<p>&lt;h2&gt;Upcoming Forecast&lt;\/h2&gt;<\/p>\n\n\n<p>[wave_forecast hours=&#8221;48&#8243;]<\/p>\n\n\n\n<p>&lt;!&#8211; Clear separation &#8211;&gt;<\/p>\n\n\n\n<p><strong>Avoid:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;wave_layered]<\/code><\/pre>\n\n\n<p>[wave_buoy]<\/p>\n\n\n\n<p>[wave_forecast] &lt;!&#8211; Too much! Pick one approach &#8211;&gt;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Cache Management<\/h3>\n\n\n\n<p><strong>During testing:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Clear before each test\nwp transient delete openmeteo_forecast\nwp transient delete ndbc_buoy_45168\n<\/code><\/pre>\n\n\n\n<p><strong>In production:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Let caches work normally\n# 30min\/1hr is good balance\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Seasonal Messaging<\/h3>\n\n\n\n<p><strong>April-October (Active):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Promote buoy data<\/li>\n\n\n\n<li>Show observed stats<\/li>\n\n\n\n<li>Highlight &#8220;real&#8221; measurements<\/li>\n<\/ul>\n\n\n\n<p><strong>November-March (Inactive):<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Default to forecast<\/li>\n\n\n\n<li>Explain seasonal nature<\/li>\n\n\n\n<li>Show &#8220;next deployment&#8221; date<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf89 Success Checklist<\/h2>\n\n\n\n<p>After deploying layered views:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] All three shortcodes work<\/li>\n\n\n\n<li>[ ] Buoy shows past data only<\/li>\n\n\n\n<li>[ ] Forecast shows future data only<\/li>\n\n\n\n<li>[ ] No &#8220;yesterday&#8221; in forecast<\/li>\n\n\n\n<li>[ ] Tabs switch properly (layered view)<\/li>\n\n\n\n<li>[ ] Current stats update (buoy view)<\/li>\n\n\n\n<li>[ ] Seasonal message shows correctly<\/li>\n\n\n\n<li>[ ] Mobile responsive<\/li>\n\n\n\n<li>[ ] No console errors<\/li>\n\n\n\n<li>[ ] Caching works<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcde Support<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Quick Diagnostics<\/h3>\n\n\n\n<p><strong>Test buoy data:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl https:\/\/www.ndbc.noaa.gov\/data\/realtime2\/45168.txt | head -5\n<\/code><\/pre>\n\n\n\n<p><strong>Test forecast API:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl \"https:\/\/marine-api.open-meteo.com\/v1\/marine?latitude=42.5593&amp;longitude=-86.2359&amp;hourly=wave_height&amp;timezone=America\/Chicago&amp;forecast_days=2\" | jq\n<\/code><\/pre>\n\n\n\n<p><strong>Check WordPress transients:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wp transient list | grep -E 'buoy|forecast'\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udfc6 Summary<\/h2>\n\n\n\n<p><strong>Layered = Better than Hybrid because:<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Cleaner separation<\/strong> &#8211; No data mixing<\/li>\n\n\n\n<li><strong>No timezone issues<\/strong> &#8211; Each layer independent<\/li>\n\n\n\n<li><strong>Easier debugging<\/strong> &#8211; Isolated data flows<\/li>\n\n\n\n<li><strong>Better UX<\/strong> &#8211; Users choose what to see<\/li>\n\n\n\n<li><strong>More reliable<\/strong> &#8211; Simpler = fewer bugs<\/li>\n<\/ol>\n\n\n\n<p><strong>Three shortcodes for every use case:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>[wave_buoy]<\/code> \u2192 Past observations<\/li>\n\n\n\n<li><code>[wave_forecast]<\/code> \u2192 Future predictions<\/li>\n\n\n\n<li><code>[wave_layered]<\/code> \u2192 Both in tabs<\/li>\n<\/ul>\n\n\n\n<p><strong>The forecast layer guarantees future-only data!<\/strong> \ud83c\udfaf<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><strong>Made with \ud83c\udf0a for accurate wave forecasting<\/strong><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Version: 3.0Author: ScooterCamFor: ScooterCam.net Clean, discrete layers for buoy observations and forecast models &#8211; no more mixing yesterday&#8217;s data! \ud83c\udfaf Why Layered Views? The Problem with Hybrid The Solution: Separate Layers \u2705 Buoy layer = Past 48 hours of real observations\u2705 Forecast layer = Next 48 hours of predictions\u2705 No mixing = No timezone confusion\u2705 &#8230; <a title=\"ScooterCam Wave Height &#8211; Layered Views\" class=\"read-more\" href=\"https:\/\/scootercam.net\/blog\/scootercam-wave-height-layered-views\/\" aria-label=\"Read more about ScooterCam Wave Height &#8211; Layered Views\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[38],"tags":[],"class_list":["post-503","post","type-post","status-publish","format-standard","hentry","category-plugins"],"_links":{"self":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts\/503","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/comments?post=503"}],"version-history":[{"count":4,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts\/503\/revisions"}],"predecessor-version":[{"id":556,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/posts\/503\/revisions\/556"}],"wp:attachment":[{"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/media?parent=503"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/categories?post=503"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/scootercam.net\/blog\/wp-json\/wp\/v2\/tags?post=503"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}