mph conversion // Version 1.08 - 20-Sep-2007 update to reflect change in NDBC website // Version 1.09 - 22-Sep-2007 more update to add debugging code and more NDBC website changes // Version 1.10 - 15-Oct-2007 new kts=Y/N parm, $showKnots spec force wind to Knots display // Version 1.11 - 28-Dec-2007 added another fix for processing on IIS servers // Version 1.12 - 21-Feb-2008 added features for simpler include to webpage // Version 1.13 - 22-Mar-2008 corrected generated CSS + more ISS support // Version 1.14 - 20-Mar-2009 added support for IE8 rotating conditions // Version 1.15 - 29-Apr-2009 minor fixes for PHP 5.2+ // Version 1.16 - 21-Nov-2010 added diagnostics to page get from NDBC // Version 1.17 - 31-Aug-2012 added template support+decode chunked responses from NDBC // Version 1.18 - 24-Feb-2015 fixed gmmktime() call for deprecated argument in PHP 5.6+ // Version 1.19 - 27-Jun-2018 use curl to fetch with https for www.ndbc.noaa.gov // Version 1.20 - 27-Dec-2022 fixes for PHP 8.1 // Version 1.21 - 21-Apr-2023 fixes for NDBC search page format changes // $Version = "buoy-data.php V1.21 21-Apr-2023"; // error_reporting(E_ALL); // uncomment to turn on full error reporting // script available at http://saratoga-weather.org/scripts.php // // you may copy/modify/use this script as you see fit, // no warranty is expressed or implied. // // Customized for: buoy data from www.nbdc.noaa.gov using // https://www.ndbc.noaa.gov/radial_search.php // // output: creates XHTML 1.0-Strict HTML page // // Options on URL: // units=E (default)-- use English units in display // units=M -- use Metric units in display // // cnv=Y (default)-- convert wind speed m/s->kph and kts->mph // cnv=N -- leave wind speed in m/s and kts // // inc=Y -- returns only the body code for inclusion // in other webpages. Omit to return full HTML. // inc=CSS -- returns the CSS style sheet only // inc=MAP -- returns the map with rotating values display // inc=TABLE -- returns the table of values // // for debugging purposes, these are useful: // // show=normal (default) - shows normal graphic // show=hotspots - shows graphic with hotspots outlined in green // show=map - returns graphic only with outlined hotspots // // cfg=list - display config info as HTML comments // // example URL: // http://your.website/buoy-data.php?inc=Y&distance=300 // would return data without HTML header/footer for buoys // within a 300 nautical mile radius of your location. // // Usage: // you can use this webpage standalone (customize the HTML portion below) // or you can include it in an existing page by doing the following: // // // $doPrintBUOY = false; // include("buoy-data.php"); // print $BUOY_CSS; // // in the
section of the page, then in the section // // print $BUOY_MAP; print $BUOY_TABLE; // // NOTE: make settings changes in buoy-data-config.txt instead of here.. that way // you can easily update the buoy-data.php when new releases are available // // settings (will be overridden by buoy-data-config.txt ----------------------------------- // most of the script is controlled by the $Config file, so make sure // it is correct ;-) // // ------- REQUIRED SETTINGS ------ $Config = 'mybuoy-Monterey_Bay.txt'; // configuration file name // // use the config file listed above to control the mapimage name, location, // and buoys to be processed. See the sample files for more examples. // // $DefaultUnits = 'E'; // 'E' = English, 'M' = Metric, may be // overridden by units=M or units=E parameter // $ourTZ = 'America/Los_Angeles'; //NOTE: this *MUST* be set correctly to // translate UTC times to your LOCAL time for the displays. // http://saratoga-weather.org/timezone.txt has the list of timezone names // pick the one that is closest to your location and put in $ourTZ like: // $ourTZ = 'America/Los_Angeles'; // or // $ourTZ = 'Europe/Brussels'; // also available is the list of country codes (helpful to pick your zone // from the timezone.txt table // http://saratoga-weather.org/country-codes.txt : list of country codes // ------- END OF REQUIRED SETTINGS ------ // // cacheName is name of file used to store cached NDBC webpage // $cacheFileDir = './'; // default cache file directory $cacheName = "NDBC-buoydata.txt"; // used to store the file so we // don't have to fetch it each time // Note: the cache name will have an E or M prepended to the .txt so there // can be separate cache files for English and Metric measurements. // If you use more than one instance of the script in a directory with // different config files, then be sure to change $cacheName to prevent // interference between the scripts. // $refetchSeconds = 600; // refetch every nnnn seconds (600=10 minutes) // $doWindConvert = true; // convert knots->mph and m/s->kph (override by // cvt=Y or cvt=N parm on URL. $showKnots = false; // always show wind in Knots (set =true ) // override by kts=Y or kts=N on URL. // $windArrowDir = './arrows/'; // set to directory with wind arrows, or // set to '' if wind arrows are not wanted // the program will test to see if images are // available, and set it to '' if no images are // found in the directory specified. // // used for rotating legend display : $windArrowSize = 'S'; // ='S' for Small 9x9 arrows (*-sm.gif) // ='L' for Large 14x14 arrows (*.gif) // // program control variables. Set to false to turn off, or true to turn on // $showNoData = true; // display 'no recent data' in table // set to true if you want to see those $doPrintTable = true; // turn on/off print of the table data $doPrintMap = true; // turn on/off print of the meso-map // //$timeFormat = 'D, Y-m-d H:i:s T'; // Fri, 2006-03-31 14:03:22 TZone $timeFormat = 'D, Y-m-d H:i:s T'; // // // // end of settings ------------------------------------------------------ // // Changes to the code below are not required for normal operation. There // are areas where the HTML can be tweaked (marked with comments). If you // want to do code changes, be aware that the HTML is generated and stored // in variables, then output at the last of the main program (before the // function definitions) so finding what to tweak may be a challenge.. // Good luck, and best regards, Ken // if (isset($_REQUEST['sce']) && strtolower($_REQUEST['sce']) == 'view' ) { //--self downloader -- $filenameReal = __FILE__; $download_size = filesize($filenameReal); header('Pragma: public'); header('Cache-Control: private'); header('Cache-Control: no-cache, must-revalidate'); header("Content-type: text/plain"); header("Accept-Ranges: bytes"); header("Content-Length: $download_size"); header('Connection: close'); readfile($filenameReal); exit; } // process parameters global $Status; $Status = ''; if (isset($_REQUEST['errors']) and $_REQUEST['errors'] == 'all') { error_reporting(E_ALL); $Status .= "\n"; } // include the settings override file if it exists if (file_exists("buoy-data-config.txt")) { require_once("buoy-data-config.txt"); $Status .= "\n"; } // --------------------------------------------------------- // overrides from Settings.php if available global $SITE; if (isset($SITE['tz'])) {$ourTZ = $SITE['tz'];} if (isset($SITE['timeFormat'])) {$timeFormat = $SITE['timeFormat'];} if (isset($SITE['cacheFileDir'])) {$cacheFileDir = $SITE['cacheFileDir']; } // end of overrides from Settings.php if available if(isset($_REQUEST['units'])) { $myUOM = strtoupper($_REQUEST['units']); } else { $myUOM = ''; } if ($myUOM <> "E" and $myUOM <> "M" ) { $myUOM = "$DefaultUnits"; } if ($myUOM == "E") { $distUnits = "nm"; } else { $distUnits = "km"; } if (isset($_REQUEST['cvt']) && strtoupper($_REQUEST['cvt']) == 'N') { $doWindConvert = false; } if (isset($_REQUEST['cvt']) && strtoupper($_REQUEST['cvt']) == 'Y') { $doWindConvert = true; } if (isset($_REQUEST['kts']) && strtoupper($_REQUEST['kts']) == 'Y') { $showKnots = true; } if (isset($_REQUEST['kts']) && strtoupper($_REQUEST['kts']) == 'N') { $showKnots = false; } if (! isset($doPrintBUOY) ) { $doPrintBUOY = true; } if (isset($_REQUEST['inc'])) { $includeOnly = strtoupper($_REQUEST['inc']); // any nonblank is ok } else { $includeOnly = ''; } if ($includeOnly == 'CSS') { $CSSonly = true; } else { $CSSonly = false; } if ($includeOnly == 'MAP') { $doPrintTable = false; $doPrintMap = true; } if ($includeOnly == 'TABLE') { $doPrintTable = true; $doPrintMap = false; } // show map with hotspots outlined if (isset($_REQUEST['show']) && strtolower($_REQUEST['show']) == 'map' ) { $ShowMap = '&show=map'; $genJPEG = true; } else { $ShowMap = ''; // no outlines for map $genJPEG = false; } if($windArrowDir && ! file_exists("$windArrowDir" . 'NNE.gif') ) { $windArrowDir = ''; // bad spec.. no arrows found } if (isset($_REQUEST['cfg']) && strtolower($_REQUEST['cfg']) == 'list') { $doListConfig = true; } else { $doListConfig = false; } if (isset($_REQUEST['cache']) && strtolower($_REQUEST['cache']) == 'no') { $refetchSeconds = 0; // set short period for refresh of cache } $t = pathinfo(__FILE__); $Program = $t['basename']; if (!isset($PHP_SELF)) {$PHP_SELF = $_SERVER['PHP_SELF']; } // Constants ------------------------------------------------------------- // don't change $baseURL or $fileName or script may break ;-) $NDBCURL = "https://www.ndbc.noaa.gov"; //NDBC website (omit trailing slash) // end of constants ------------------------------------------------------ // ------------- Main code starts here ------------------- global $seenBuoy,$MapImage,$myLat,$myLong,$Buoys,$table,$scroller; global $myUOM,$doWindConvert,$showKnots; // Establish timezone offset for time display if (!function_exists('date_default_timezone_set')) { putenv("TZ=" . $ourTZ); $Status .= "\n"; } else { date_default_timezone_set("$ourTZ"); $Status .= "\n"; } $timediff = tzdelta(); $curTZ = date('T',time()); $Status .= "\n" . "\n" . "\n"; // $Status .= "\n"; load_config($Config); // Load the configuration file // show image of map with hotspots outlined if (isset($_REQUEST['show']) && strtolower($_REQUEST['show']) == 'hotspots' ) { $ourGraphic = $PHP_SELF . "?show=map"; $toggleState = "Now showing Hotspots -- click to show normal graphic.\n"; } else { $ourGraphic = $MapImage; // no outlines for map $toggleState = "Now showing normal graphic -- click to show hotspots outlined.\n"; } if ($genJPEG) { // just produce the map with hotspots outlined outline_hotspots($MapImage); exit; } $Units = load_units(); // setup display units $Status .= "\n"; load_strings(); // load the assembly strings for CSS, HTML, Maps // Change cache name to handle both English and Metric caches $cacheName = str_replace(".txt","$myUOM.txt",$cacheName); $cacheName = $cacheFileDir . $cacheName; $fileName = "https://www.ndbc.noaa.gov/radial_search.php?lat1=$myLat&lon1=$myLong&uom=$myUOM&dist=$maxDistance&ot=A&time=2"; // refresh cached copy of page if needed // fetch/cache code by Tom at carterlake.org if (file_exists($cacheName) and filemtime($cacheName) + $refetchSeconds > time()) { $Status .= "\n"; $html = implode('', file($cacheName)); $Status .= "\n"; } else { $Status .= "\n"; $html = BUOY_fetchUrlWithoutHanging($fileName); $Status .= "\n"; if(strlen($html) < 10) { $Status .= "\n"; } else { $fp = fopen($cacheName, "w"); if ($fp) { $write = fputs($fp, $html); $Status .= "\n"; fclose($fp); } else { $Status .= "\n"; } $Status .= "\n"; } } // parse and handle the returned buoy page from www.ndbc.noaa.gov // extract buoy data lines from page $buoyrawdata = preg_replace('|\r|Uis',"\n",$html); // V1.06 - IIS fix preg_match_all('|(.*)|si',$html,$betweenspan);
//$Status .= "\n";
$buoyrawdata = $betweenspan[1][0];
$Status .= "\n";
$buoydata = explode("\n",$buoyrawdata); // get lines of html to process
$Status .= "\n";
// things to clean out of the buoy table row
$removestr = array (
"']+>||]+>|'si"
);
// examine, process and format each line of the buoy data
$buoysFound = 0;
$seenBuoy = array(); // storage area for buoy data lines
foreach ($buoydata as $key => $buoy) { // read each text line of buoy data
if (preg_match("|\d+ observations from|i",$buoy) ) {
// found the from - to timestamp line
list($nObs,$firstDate,$firstTime,$lastDate,$lastTime) = getTimeRange($buoy);
$Status .= "\n";
}
if (preg_match("|^|i",$buoy)) // keep only the buoy data )
{
// clear out NDBC formatting of and
$buoy = preg_replace($removestr,"",$buoy);
$buoy = preg_replace("/\s\-\s/si",' n/a ',$buoy); // change '-' to 'n/a'
list($ID,$TYPE,$TIME,$LAT,$LON,$DIST,$HDG,$WDIR,$WSPD,$GST,$WVHT,$DPD,$APD,$MWD,$PRES,$PTDY,$ATMP,$WTMP,$DEWP,$VIS,$TCC,$TIDE,$S1HT,$S1PD,$S1DIR,$S2HT,$S2PD,$S2DIR,$Ice,$Sea) = preg_split("/[\s]+/",$buoy); //all the data for one buoy
// save only the first-seen entry.. it's the most recent one
if (! isset($seenBuoy["$ID"]) && isset($Buoys["$ID"]) ) {
$seenBuoy["$ID"] = $buoy; // save off record we want for later
$buoysFound++; // keep a tally of quakes for summary
} // end $seenBuoy
// else { print "\n"; }
} // end skip non-data
} // end foreach loop
// now generate the data table by looking at our buoys
$doneHeader = 0; // flag to determine when to do the header
foreach ($Buoys as $key => $buoy) { // loop over buoys in config file
if (! $doneHeader) { // print the header if needed
prt_tablehead();
$doneHeader = 1;
} // end doneHeader
prt_tabledata($key);
} // all table data is now in $table
// Write trailer info
if ($doneHeader) {
// --------------- customize HTML if you like -----------------------
$table .= "\n";
} else {
// --------------- customize HTML if you like -----------------------
$table .= " No buoys within $maxDistance $distUnits of $myLat $myLong found.
Air Temperature units: " . $Units['temp'] . "
Water Temperature units: " . $Units['temp'] . "
Wind speed units: " . $Units['wind'] . "
Gust speed units: " . $Units['wind'] . "
Barometer units: . $Units['wind'] . "
Barometer trend units: " . $Units['baro'] . "
Wave height units: " . $Units['wave'] . "
Wave Dominant Period units: sec
Air Temperature
Water Temperature
Wind Direction @ Speed
Wind Gust Speed
Barometer
Barometer Trend
Wave Height
Wave Dominant Period
$ATMP " . $Units['temp'] . "
$WTMP " . $Units['temp'] . "
";
$wda = '';
if ($WDIR <> 'n/a') {
$wda = getWindDir($WDIR);
if ($windArrowDir) {
$scroller .= "";
}
$scroller .= getWindDir($WDIR) . " " .
convertWind($WSPD) . " " . $Units['wind'] ;
} else {
$scroller .= $WDIR;
}
$scroller .= "
";
// if ($GST <> 'n/a' && $wda && $windArrowDir) {
// $scroller .= "";
// }
$scroller .= convertWind($GST) . " " . $Units['wind'] . "
$PRES ". $Units['baro'] . "
$PTDY ". $Units['baro'] . "
$WVHT " . $Units['wave'] . "
{$DPD} sec
";
/* --------- older style -----------
$scroller .= "ID
Name
Time
" . $Units['time'] . "Air
" . $Units['temp'] . "Water
" . $Units['temp'] . "Wind
" . $Units['wind'] . "Gust
" . $Units['wind'] . "Baro
" . $Units['baro'] . "Trend
" . $Units['baro'] . "Waves
" . $Units['wave'] . "Period
Sec
";
} // end showNoData
return;
}
// got data for one of our buoys.. format the table entry
list($ID,$TYPE,$TIME,$LAT,$LON,$DIST,$HDG,$WDIR,$WSPD,$GST,$WVHT,$DPD,$APD,$MWD,$PRES,$PTDY,$ATMP,$WTMP,$DEWP,$VIS,$TCC,$TIDE,$S1HT,$S1PD,$S1DIR,$S2HT,$S2PD,$S2DIR,$Ice,$Sea) = preg_split("/[\s]+/",$seenBuoy["$ID"]);
$TIME = chgTime($TIME); // go adjust the time if needed
if ($windArrowSize == 'S') {
$windGIF = '-sm.gif';
$windSIZE = 'height="9" width="9"';
} else {
$windGIF = '.gif';
$windSIZE = 'height="14" width="14"';
}
// --------------- customize HTML if you like -----------------------
$table .= "
$ID
$Name
No recent reports.
\n";
// generate the data for the changing conditions display
// NOTE: changes here may break the rotating conditions display..
$scroller .= "$ID
$Name
$TIME
$ATMP
$WTMP
";
if ($WDIR == 'n/a') {
$table .= $WDIR;
} else {
$wda = getWindDir($WDIR);
$table .= $wda . " ";
if ($windArrowDir) {
$table .= "";
}
$table .= " " .convertWind($WSPD);
}
$table .= "
" . convertWind($GST) . "
$PRES
$PTDY
$WVHT
$DPD