Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php // error_reporting(E_ALL); // This is a more advanced version of the forecast scri..
Decoded Output download
<?php
// error_reporting(E_ALL);
// This is a more advanced version of the forecast script
// It uses file caching and feed failure to better handle when NOAA is down
//
// Version 2.00 - 15-Jan-2007 - modified Tom's script for XHTML 1.0-Strict
// Version 2.01 - 14-Feb-2007 - modified for ERH->CRH.noaa.gov redirects (no point forecasts)
// Version 2.02 - 02-Mar-2007 - added auto-failover to CRH and better include in page.
// Version 2.03 - 29-Apr-2007 - modified for /images/wtf -> /forecast/images change
// Version 2.04 - 05-Jun-2007 - improvement to auto-failover
// Version 2.05 - 29-Jun-2007 - additional check for alternative no-icon forecast, then failover.
// Version 2.06 - 24-Nov-2007 - rewrite for zone forecast, intrepret icons from text-only forecast
// Version 2.07 - 24-Nov-2007 - support new zone forecast with different temp formats
// Version 2.08 - 25-Nov-2007 - fix zone forecast icons, new temp formats supported
// Version 2.09 - 26-Nov-2007 - add support for new temperature phrases and below zero temps
// Version 2.10 - 17-Dec-2007 - added safety features from Mike Challis http://www.carmosaic.com/weather/
// Version 2.11 - 20-Dec-2007 - added cache-refresh on request, fixed rising/falling temp arrow
// Version 2.12 - 31-Dec-2007 - fixed New Year"s to New Year's display problem
// Version 2.13 - 01-Jan-2008 - added integration features for carterlake/WD/PHP/AJAX template set
// Version 2.14 - 14-Jan-2009 - corrected Zone forecast parsing for below zero temperatures
// Version 2.15 - 28-Feb-2010 - updated Zone forecast parsing for new phrases
// Version 2.16 - 14-Dec-2010 - added support for warning messages from NWS and $forecastwarnings string
// Version 2.17 - 08-Jan-2011 - fixed validation issue when NWS uses '&' in condition
// Version 2.18 - 27-Feb-2011 - added support for common cache directory
// Version 3.00 - 12-Mar-2011 - support for multi-forecast added by Curly at http://www.ricksturf.com/
// Version 3.01 - 01-Oct-2011 - added support for alternative animated icon set from http://www.meteotreviglio.com/
// Version 3.02 - 05-Oct-2011 - corrected warning links to forecast.weather.gov
// Version 3.03 - 02-Jul-2012 - added fixes for NWS website changes
// Version 3.04 - 03-Jul-2012 - added fixes for W3C validation issues
// Version 3.05 - 05-Jul-2012 - added fixes for Zone forecast use with new NWS website design
// Version 3.06 - 07-Jul-2012 - fixed validation issue for Rising/Falling temp arrows with new NWS website design
// Version 3.07 - 09-Aug-2012 - fixed failover to Zone forecast with new NWS website design
// Version 3.08 - 23-Nov-2012 - fixed issue with Zone forecast parsing due to NWS website changes
// Version 3.09 - 28-Jun-2013 - added fixes for Zone forecast parsing due to NWS website changes
// Version 3.10 - 18-Nov-2013 - fixed issue with Zone forecast URL due to NWS website changes
// Version 3.11 - 13-Mar-2014 - fixed point forecast text non-display due to NWS website changes
// Version 3.12 - 15-Mar-2014 - fixes for Zone forecast, warnings and auto-correct old URLs
// Version 3.13 - 17-Mar-2015 - fixes for Zone forecast w/new NWS site design
// Version 3.14 - 31-Mar-2015 - fixes for NWS site changes
// Version 3.15 - 13-May-2015 - fix for Zone forecast w/NWS website change
// Version 3.16 - 15-May-2015 - fixes for different Zone forecast format in failover
// Version 4.00 - 06-Jul-2015 - added support for DualImage processing
// Version 4.01 - 07-Jul-2015 - fixed HTML for $forecasticons for new NWS website
// Version 4.02 - 07-Nov-2015 - fixed Zone forecast when using .gif icons issue
//
// Version 5.00 - 22-Apr-2017 - complete rewrite to use api.weather.gov JSON feeds for forecasts and alerts
// Version 5.01 - 26-Apr-2017 - switch to use test NWS site for data waiting for June 19, 2017 prod cutover
// Version 5.02 - 22-May-2017 - added temperature trend indicator
// Version 5.03 - 16-Jul-2017 - if point forecast HTTP return code > 400, force zone forecast fetch
// Version 5.04 - 11-Oct-2017 - fix for stale point-forecast via API to failover to Zone forecast
// Version 5.05 - 27-Feb-2018 - more fixes for point-forecast/refetch fail->Zone failover
// Version 5.06 - 12-Apr-2018 - fix PHP warning when no alerts available
// Version 5.07 - 14-Apr-2018 - add caching for point->gridpoint forecast URLs + WFO info on Zone fcst
// Version 5.08 - 25-May-2018 - fixes for point/zone JSON changes from api.weather.gov
// Version 5.09 - 26-May-2018 - added new NWS API icons for tropical storm/hurricane
// Version 5.10 - 13-Apr-2019 - fix for HTTP/2 responses from api.weather.gov
// Version 5.11 - 30-Apr-2019 - use new point->meta->gridpoint method for point forecast URL data
// Version 5.12 - 01-May-2019 - fix for link URL at bottom of page
// Version 5.13 - 08-Sep-2019 - fix for link URL with NWS discontinuation of forecast-v3.weather.gov site
// Version 5.14 - 10-May-2020 - fix for Alert link URL for alerts-v2.weather.gov (Thanks Jasiu!)
// Version 5.15 - 25-Jun-2020 - updated diagnostic info to help diagnose Akamai cache issues from api.weather.gov site
// Version 5.16 - 08-Jul-2020 - added diagnostic logging capability ($doLogging = true; to enable);
// Version 5.17 - 15-Jul-2020 - switch to geo+json queries from ld+json queries for api.weather.gov; cache file name changes too.
// Version 5.18 - 18-Jan-2022 - added fix for PHP8.1 Deprecated errata
// Version 5.18a - 07-Feb-2022 - use 'updateTime' instead of deprecated 'updated' for age of gridpoint forecast
// Version 5.19 - 27-Dec-2022 - fixes for PHP 8.2
//
$Version = 'advforecast2.php (JSON) - V5.19 - 27-Dec-2022';
//
// import NOAA Forecast info
// data ends up in four different arrays:
// $forecasticons[x] x = 0 thru 13 This is the icon and text around it
// $forecasttemp[x] x= 0 thru 13 This is forecast temperature with styling
// $forecasttitles[x] x = 0 thru 13 This is the title word for the text forecast time period
// $forecasttext[x] x = 0 thru 13 This is the detail text for the text forecast time period
//
// $forecastupdated This is the time of last update
// $forecastcity This is the city name for the forecast
// $forecastoffice This is the NWS Office providing the forecast
// $forecastwarnings This is the text/links to NWS Warnings, Watches, Advisories, Outlooks, Special Statements
//
// Also, in order for this to work correctly, you need the NOAA icons (or make your own...
// there are 750!).
//
// http://saratoga-weather.org/saratoga-icons2.zip
// unzip the above and upload to your site (preserving the directory structure):
//
// ./forecast/images (for the static images)
// ./forecast/icon-templates (for the dual-image icon master files)
// ./DualImage.php (script to create the dual-image icons as required by the forecast)
//
// The URL(s) below --MUST BE-- the Printable Point Forecast from the NOAA website
// Not every area of the US has a printable point forecast
//
// This script will ONLY WORK with a printable point forecast of either the OLD format like:
// http://forecast.weather.gov/MapClick.php?
// CityName=Saratoga&state=CA&site=MTR&textField1=37.2639&textField2=-122.022
// &e=1&TextType=2
//
// or the new format (after June 24, 2019 ) of
// https://forecast.weather.gov/point/37.2639,-122.022
//
// To find yours in your area:
//
// Go to www.weather.gov
// Put your city, state in the search box and press Search
// copy the URL from your browser into the $fileName variable below.
// Also put your NOAA Warning Zone (like ssZnnn) in caps in the $NOAAZone variable below.
// It is used for automatic backup in case the point printable forecast is not available.
//
// ----------------------SETTINGS---------------------------------------------
// V3.00 -- this following array can be used for multiple forecasts in *standalone* mode
// for Saratpga template use, add a $SITE['NWSforecasts'] entry in Settings.php to have these entries.
// To activate the definitions below, replace the /* with //* to uncomment the array definition
/*
$NWSforecasts = array(
// the entries below are for testing use.. replace them with your own entries if using the script
// outside the AJAX/PHP templates.
// ZONE|Location|point-forecast-URL (separated by | characters
"CAZ513|Saratoga, CA (WRH)|http://forecast.weather.gov/MapClick.php?CityName=Saratoga&state=CA&site=MTR&textField1=37.2639&textField2=-122.022&e=1&TextType=2",
"NEZ052|Omaha, NE (CRH)|http://forecast.weather.gov/MapClick.php?lat=41.2586&lon=-95.9378&unit=0&lg=english&FcstType=text&TextType=2",
"ALZ266|Gulf Shores, AL (SRH)|http://forecast.weather.gov/MapClick.php?CityName=Gulf+Shores&state=AL&site=MOB&textField1=30.27&textField2=-87.7015&e=0&TextType=2",
'MDZ022|Salisbury, MD (ERH)|http://forecast.weather.gov/MapClick.php?lat=38.36818&lon=-75.59761&unit=0&lg=english&FcstType=text&TextType=2',
'AKZ101|Anchorage, AK (ARH)|http://forecast.weather.gov/MapClick.php?lat=61.21806&lon=-149.90027780000003&unit=0&lg=english&FcstType=text&TextType=2',
'HIZ005|Honolulu, HI (HRH)|http://forecast.weather.gov/MapClick.php?lat=21.30694&lon=-157.85833330000003&unit=0&lg=english&FcstType=text&TextType=2',
'IAZ068|Riverdale, IA|http://forecast.weather.gov/MapClick.php?lat=41.5354&lon=-90.4671&unit=0&lg=english&FcstType=text&TextType=2',
'MEZ030|Bar Harbor, ME|http://forecast.weather.gov/MapClick.php?lat=44.76&lon=-67.5477&unit=0&lg=english&FcstType=text&TextType=2',
'TXZ147|Fairfield, TX|http://forecast.weather.gov/MapClick.php?&lat=31.7188&lon=-96.1655&lg=english&FcstType=text&TextType=2',
'SDZ021|Millbank, SD|http://forecast.weather.gov/MapClick.php?lat=45.194&lon=-96.6869&unit=0&lg=english&FcstType=text&TextType=2',
'MNZ034|Brainerd, MN|http://forecast.weather.gov/MapClick.php?lat=46.3544&lon=-94.1941&unit=0&lg=english&FcstType=text&TextType=2',
'COZ010|Vail, CO|http://forecast.weather.gov/MapClick.php?lat=39.5864&lon=-106.3822&unit=0&lg=english&FcstType=text&TextType=2',
'CAZ072|South Lake Tahoe, CA|http://forecast.weather.gov/MapClick.php?CityName=Tahoe Vista&state=CA&site=MTR&textField1=39.2425194&textField2=-120.0604858&e=1&TextType=2',
'WAZ037|Colville, WA|http://forecast.weather.gov/MapClick.php?lat=48.5433&lon=-117.8951&unit=0&lg=english&FcstType=text&TextType=2',
'ILZ103|Hoffman Estates, IL|http://forecast.weather.gov/MapClick.php?lat=42.03921&lon=-88.11001&unit=0&lg=english&FcstType=text&TextType=2',
'NDZ027|Grand Forks, ND|http://forecast.weather.gov/MapClick.php?lat=47.9169&lon=-97.072&unit=0&lg=english&FcstType=text&TextType=2',
'MTZ055|Bozeman, MT|http://forecast.weather.gov/MapClick.php?lat=45.6354&lon=-111.0633&unit=0&lg=english&FcstType=text&TextType=2',
"AZZ544|Phoenix|http://forecast.weather.gov/MapClick.php?CityName=Phoenix&state=AZ&site=PSR&textField1=33.646&textField2=-112.007&e=0&TextType=2",
'KSZ078|Dodge City, KS|http://forecast.weather.gov/MapClick.php?lat=37.7528&lon=-100.0171&unit=0&lg=english&FcstType=text&TextType=2',
'OKZ020|Stillwater, OK|http://forecast.weather.gov/MapClick.php?lat=36.1156&lon=-97.0584&unit=0&lg=english&FcstType=text&TextType=2',
'GAZ034|Lawrenceville, GA|http://forecast.weather.gov/MapClick.php?CityName=Lawrenceville&state=GA&site=FFC&lat=33.9495&lon=-83.9922&TextType=2',
'ARZ011|Rockhouse, AR|http://forecast.weather.gov/MapClick.php?lat=36.289&lon=-93.7258&unit=0&lg=english&FcstType=text&TextType=2',
'ARZ040|Mena, AR|http://forecast.weather.gov/MapClick.php?lat=34.6036&lon=-94.2631&unit=0&lg=english&FcstType=text&TextType=2',
'MOZ090|Springfield, MO|http://forecast.weather.gov/MapClick.php?lat=37.1962&lon=-93.2861&unit=0&lg=english&FcstType=text&TextType=2',
'MTZ019|Plentywood, MT|http://forecast.weather.gov/MapClick.php?lat=48.7747&lon=-104.5625&unit=0&lg=english&FcstType=text&TextType=2',
'ARZ044|Little Rock, AR|https://forecast.weather.gov/MapClick.php?lat=34.7224&lon=-92.3541&unit=0&lg=english&FcstType=text&TextType=2',
"NYZ040|Hessville, NY|https://forecast.weather.gov/MapClick.php?lat=42.8739&lon=-74.687&unit=0&lg=english&FcstType=text&TextType=2",
"NYZ049|Albany, NY|http://forecast.weather.gov/MapClick.php?CityName=Albany&state=NY&site=MTR&textField1=42.6525&textField2=-73.757&e=0&TextType=2",
"NYZ056|Binghamton, NY|http://forecast.weather.gov/MapClick.php?CityName=Binghamton&state=NY&site=MTR&textField1=42.0956&textField2=-75.910&e=0&TextType=2",
"MAZ017|Boston, MA|http://forecast.weather.gov/MapClick.php?CityName=Boston&state=MA&site=MTR&textField1=42.3586&textField2=-71.060&e=0&TextType=2",
"MNZ060|Robbinsdale, MN|http://forecast.weather.gov/MapClick.php?lat=45.02233988655459&lon=-93.34722518920898&site=mpx&unit=0&lg=en&FcstType=text&TextType=2",
"FLZ112|Upper Grand Lagoon, FL|http://forecast.weather.gov/MapClick.php?lat=30.1981&lon=-85.8077&unit=0&lg=english&FcstType=text&TextType=2",
"MSZ066|Ellisville, MS|https://forecast.weather.gov/MapClick.php?lat=31.62265&lon=-89.23384&unit=0&lg=english&FcstType=text&TextType=2",
"NEZ066|Lincoln, NE|https://forecast.weather.gov/MapClick.php?CityName=Lincoln&state=NE&site=OAX&textField1=40.8164&textField2=-96.6882&e=0&TextType=2",
"IAZ066|Clinton, IA|https://forecast-v3.weather.gov/point/41.839,-90.192",
'ALZ266|Gulf Shores, AL|https://forecast.weather.gov/MapClick.php?lat=30.2775&lon=-87.6831&unit=0&lg=english&FcstType=text&TextType=2',
'KSZ050|3 Miles SE Lyons KS|https://forecast.weather.gov/MapClick.php?lat=38.3134&lon=-98.1617&unit=0&lg=english&FcstType=text&TextType=2',
'ILZ029|Glasford IL|https://forecast.weather.gov/MapClick.php?lat=40.5723&lon=-89.8132&unit=0&lg=english&FcstType=text&TextType=2',
);
//*/
//*
//
$NOAAZone = 'CAZ513'; // change this line to your NOAA warning zone.
// set $fileName to the URL for the point-printable forecast for your area
// NOTE: this value (and $NOAAZone) will be overridden by the first entry in $NWSforecasts if it exists.
$fileName = "http://forecast.weather.gov/MapClick.php?CityName=Saratoga&state=CA&site=MTR&textField1=37.2639&textField2=-122.022&e=1&TextType=2";
//*/
$showTwoIconRows = true; // =true; show all icons, =false; show 9 icons in one row (new V5.00)
//
$showZoneWarning = true; // =true; show note when Zone forecast used. =false; suppress Zone warning (new V5.00)
// $iconDir = './forecast/imagesPNG-86x86/'; // testing only
// $iconDir = './forecast/imagesGIF-55x58/'; // testing only
$iconDir = './forecast/images/';
$iconType = '.jpg'; // default type='.jpg' -- use '.gif' for animated icons from http://www.meteotreviglio.com/
$cacheFileDir = './'; // default cache file directory
$iconHeight = 55; // default height of conditions icon (saratoga-icons.zip)
$iconWidth = 55; // default width of conditions icon (saratoga-icons.zip)
$refreshTime = 600; // default refresh of cache 600=10 minutes
//
$ourTZ = 'America/Los_Angeles'; // default timezone (new V5.00)
// $timeFormat = 'd-M-Y g:ia T'; // Fri, 31-Mar-2006 6:35pm TZone (new V5.00)
$timeFormat = 'g:i a T M d, Y'; // 3:17 am PST Jan 28, 2017 (new V5.00, like old forecast timestamp display)
# V5.16 logging capability for curl accesses to api.weather.gov
$doLogging = true; // =true to enable summary logging, =false for no summary log file
# log saved to $cacheFileDir/advforecast2-log-YYYY-MM-DD.csv (tab-delimited CSV file)
#
$doLoggingDetail = true; // =true to eanable detail logging, =false for no detail file
# log saved to $cacheFileDir/advforecast2-log-YYYY-MM-DD.txt
//
// ----------------------END OF SETTINGS--------------------------------------
// changing stuff below this may cause you issues when updates to the script are done.
// you change it, you own it :)
//
$useProdNWS = false; // =false, use preview V3 sites, =true, use production sites (after June 24, 2019)
//
$forceDualIconURL = false; // for TESTING prior to 7-Jul-2015 when new icons were used by NWS
// following is for possible FUTURE use
$getGridpointData = false; // =true; for getting gridpoint data, =false for not getting the data
//
// following is for possible FUTURE hourly forecast/graphics use
$getHourlyData = false; // =true; for getting hourly data, =false for not getting the data
if(file_exists('Settings.php')) { include_once('Settings.php'); }
// overrides from Settings.php if available
global $SITE;
if (isset($SITE['NWSforecasts'])) {$NWSforecasts = $SITE['NWSforecasts']; }
if (isset($SITE['cacheFileDir'])) {$cacheFileDir = $SITE['cacheFileDir']; }
if (isset($SITE['noaazone'])) {$NOAAZone = $SITE['noaazone'];}
if (isset($SITE['fcsturlNWS'])) {$fileName = $SITE['fcsturlNWS'];}
if (isset($SITE['fcsticonsdir'])) {$iconDir = $SITE['fcsticonsdir'];}
if (isset($SITE['fcsticonstype'])) {$iconType = $SITE['fcsticonstype'];}
if (isset($SITE['fcsticonsheight'])) {$iconHeight = $SITE['fcsticonsheight'];}
if (isset($SITE['fcsticonswidth'])) {$iconWidth = $SITE['fcsticonswidth'];}
if (isset($SITE['tz'])) {$ourTZ = $SITE['tz'];} // V5.00
if (isset($SITE['timeFormat'])) {$timeFormat = $SITE['timeFormat'];} // V5.00
if (isset($SITE['showTwoIconRows'])) {$showTwoIconRows = $SITE['showTwoIconRows'];} // V5.00
if (isset($SITE['showZoneWarning'])) {$showZoneWarning = $SITE['showZoneWarning'];} // V5.00
if (isset($SITE['advLogging'])) {$doLogging = $SITE['advLogging'];} // V5.16
if (isset($SITE['advLoggingDetail'])) {$doLoggingDetail = $SITE['advLoggingDetail'];} // V5.16
// end of overrides from Settings.php
$doDebug = (isset($_REQUEST['debug']) and preg_match('|y|i',$_REQUEST['debug']))?true:false;
if (isset($_REQUEST['rows'])) {
if ($_REQUEST['rows'] == '1') {
$showTwoIconRows = false;
}
if ($_REQUEST['rows'] == '2') {
$showTwoIconRows = true;
}
}
// hosts providing API and Forecasts and Alerts
if ($useProdNWS) {
// final production URLs
define('APIURL', "https://api.weather.gov");
define('FCSTURL', "https://forecast.weather.gov");
define('ALERTAPIURL', 'https://api.weather.gov/alerts?active=1&point=');
define('ALERTURL', 'https://alerts.weather.gov/products/');
}
else {
// pre-production/testing URLs
define('APIURL', "https://api.weather.gov");
define('FCSTURL', "https://forecast-v3.weather.gov");
define('ALERTAPIURL','https://api.weather.gov/alerts?active=1&point=');
define('ALERTURL', 'https://alerts-v2.weather.gov/#/?id='); // Jasiu fix 10-May-2020
}
// get the selected zone code
$haveZone = '0';
if (!empty($_GET['z']) && preg_match("/^[0-9]+$/i", htmlspecialchars($_GET['z']))) {
$haveZone = htmlspecialchars(strip_tags($_GET['z'])); // valid zone syntax from input
}
$DualImageAvailable = file_exists("./DualImage.php") ? true : false;
// $DualImageAvailable = false;
if (isset($_REQUEST['convert'])) { // display new URLs if requested
gen_new_settings();
exit;
}
if (!isset($NWSforecasts[0])) {
// print "<!-- making NWSforecasts array default -->
";
$NWSforecasts = array(
"$NOAAZone||$fileName"
); // create default entry
}
// print "<!-- NWSforecasts
".print_r($NWSforecasts,true). " -->
";
// Set the default zone. The first entry in the $SITE['NWSforecasts'] array.
list($Nz, $Nl, $Nn) = explode('|', $NWSforecasts[0] . '|||');
$NOAAZone = $Nz;
$NOAAlocation = $Nl;
$fileName = $Nn;
$newFormat = false;
if (!isset($NWSforecasts[$haveZone])) {
$haveZone = 0;
}
// locations added to the drop down menu and set selected zone values
$dDownMenu = '';
for ($m = 0; $m < count($NWSforecasts); $m++) { // for each locations
list($Nzone, $Nlocation, $Nname) = explode('|', $NWSforecasts[$m] . '|||');
$dDownMenu.= " <option value=\"" . $m . "\">" . $Nlocation . "</option>
";
if ($haveZone == $m) {
$NOAAZone = $Nzone;
$NOAAlocation = $Nlocation;
$fileName = $Nname;
}
}
// build the drop down menu
$ddMenu = '';
// create menu if at least two locations are listed in the array
if (isset($NWSforecasts[0]) and isset($NWSforecasts[1])) {
$ddMenu.= '<tr align="center">
<td style="font-size: 14px; font-family: Arial, Helvetica, sans-serif">
<script type="text/javascript">
<!--
function menu_goto( menuform ){
selecteditem = menuform.logfile.selectedIndex ;
logfile = menuform.logfile.options[ selecteditem ].value ;
if (logfile.length != 0) {
location.href = logfile ;
}
}
// -->
</script>
<form action="" method="get">
<p><select name="z" onchange="this.form.submit()">
<option value=""> - Select Forecast - </option>
' . $dDownMenu . $ddMenu . ' </select></p>
<div><noscript><pre><input name="submit" type="submit" value="Get Forecast" /></pre></noscript></div>
</form>
</td>
</tr>
';
}
// You can now force the cache to update by adding ?force=1 to the end of the URL
if (empty($_REQUEST['force'])) $_REQUEST['force'] = "0";
$Force = $_REQUEST['force'];
$forceBackup = false;
if ($Force > 1) {
$forceBackup = true;
}
$cacheName = $cacheFileDir . "forecast-" . $NOAAZone . "-$haveZone-geojson.txt";
// dont change the $backupfileName!
// new Zone URL with V5.00:
$Status = "<!-- $Version on PHP " . phpversion() . "-->
<!-- RAW NWS URL: $fileName zone=$NOAAZone -->
";
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,charset=ISO-8859-1");
header("Accept-Ranges: bytes");
header("Content-Length: $download_size");
header('Connection: close');
readfile($filenameReal);
exit;
}
global $fcstPeriods, $NWSICONLIST;
load_lookups();
$usingFile = "";
if (!function_exists('date_default_timezone_set')) {
if (!ini_get('safe_mode')) {
putenv("TZ=$ourTZ"); // set our timezone for 'as of' date on file
}
}
else {
date_default_timezone_set($ourTZ);
}
if (version_compare(PHP_VERSION, '5.3', '<')) {
print "<p>This script requires PHP V5.3+ to operate. You are running PHP " . PHP_VERSION . "<br/>";
print "Please update PHP to be able to run $Version script</p>
";
$forecasttitles = array();
$forecasttext = array();
$forecasticons = array();
$forecasttemp = array();
print $Status;
exit;
}
// grab the meta info for point, zone and forecast office + gridpoint URLs to use
if (strpos($iconType, 'gif') !== false and $DualImageAvailable) {
$DualImageAvailable = false;
$Status.= "<!-- DualImage function is not available with $iconType icons; -->
";
$Status.= "<!-- first image of any dual-image found will be displayed instead -->
";
}
$oldFileName = $fileName;
$backupfileName = APIURL . "/zones/forecast/$NOAAZone/forecast";
list($fileName, $pointURL) = convert_filename($fileName, $NOAAZone); // correct the filename if necessary to API format
$META = array();
$META = get_meta_info($cacheName, $fileName, $backupfileName);
if (isset($META['forecastZone']) and $META['forecastZone'] !== $NOAAZone) {
$Status.= "<!-- WARNING: NOAAZone='$NOAAZone' is not correct. Will use '" . $META['forecastZone'] . "' for this point location per NWS. -->
";
$NOAAZone = $META['forecastZone'];
}
// V5.11 - use the META data for the real gridpoint forecast and zone forecast URLs.
$pointURL = $META['forecastURL'];
$fileName = $META['forecastURL'];
$backupfileName = APIURL . "/zones/forecast/".$META['forecastZone']."/forecast";
if($fileName == '') {
$Status .= "<!-- pointURL is null. Using Zone URL instead -->
";
$fileName = $backupfileName;
}
$Status .= "<!-- point forecastURL = '$pointURL' -->
";
$Status .= "<!-- zone forecastURL = '$backupfileName' -->
";
$html = '';
$lastURL = '';
if ($Force == 1 or
!file_exists($cacheName) or
(file_exists($cacheName) and filemtime($cacheName) + $refreshTime < time())) {
$html = ADV_fetchUrlWithoutHanging($fileName);
$stuff = explode("
",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
preg_match('/HTTP\/\S+ (\d+)/', $headers, $m);
//$Status .= "<!-- m=".print_r($m,true)." -->
";
//$Status .= "<!-- html=".print_r($html,true)." -->
";
if(isset($m[1])) {$lastRC = (string)$m[1]; } else {$lastRC = '0'; }
if ($lastRC >= '400') {
$Force = 2;
$Status.= "<!-- Oops.. point forecast unavailable RC=" . $lastRC . " - using Zone instead -->
";
} else {
$lastURL = $fileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded point-forecast $fileName - $fSize bytes -->
";
if (strpos($content, '{') !== false and $lastRC == '200') { // got a file.. save it
$fp = fopen($cacheName, "w");
if ($fp) {
$write = fputs($fp, $html);
fclose($fp);
$Status.= "<!-- wrote cache file $cacheName -->
";
} else {
$Status.= "<!-- unable to write cache file $cacheName -->
";
}
}
}
}
if ($Force == 2) {
$usingFile = "(Zone forecast)";
$html = ADV_fetchUrlWithoutHanging($backupfileName);
$lastURL = $backupfileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded $usingFile $backupfileName - $fSize bytes -->
";
$stuff = explode("
",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
preg_match('/HTTP\/\S+ (\d+)/', $headers, $m);
//$Status .= "<!-- m=".print_r($m,true)." -->
";
//$Status .= "<!-- html=".print_r($html,true)." -->
";
if(isset($m[1])) {$lastRC = (string)$m[1];} else {$lastRC = '0';}
if (strpos($html, '{') !== false and $lastRC == '200') { // got a file.. save it
$fp = fopen($cacheName, "w");
if ($fp) {
$write = fputs($fp, $html);
fclose($fp);
$Status.= "<!-- wrote cache file $cacheName -->
";
}
else {
$Status.= "<!-- unable to write cache file $cacheName -->
";
}
}
}
if (strlen($html) < 50 and file_exists($cacheName)) { // haven't loaded it by fetch.. load from cache
$html = file_get_contents($cacheName);
$fSize = strlen($html);
$Status.= "<!-- loaded cache file $cacheName - $fSize bytes -->
";
$stuff = explode("
",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if (preg_match('/Temporary|Location:|defaulting to|window\.location\.href\=/Uis', $headers)) {
$usingFile = "(Zone forecast)";
$html = ADV_fetchUrlWithoutHanging($backupfileName);
$lastURL = $backupfileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded $usingFile $backupfileName - $fSize bytes -->
";
}
}
if ($Force != 2) {
// check point-forecast age and load zone forecast if too old
preg_match('!"updateTime":\s*"([^"]+)"!is', $html, $matches);
if (isset($matches[1])) {
$age = time() - strtotime($matches[1]);
$ts = $matches[1];
if ($age > 18 * 60 * 60) {
$agehms = sec2hmsADV($age);
$Status.= "<!-- point forecast more than 18hrs old (age h:m:s is $agehms) updated:'$ts' .. use Zone forecast instead -->
";
list($headers,$content) = explode("
",$html);
$Status .= "<!-- headers from the gridpoint request
".$headers."
-->
";
ADV_log('Stale',"Forecast Stale Age=$agehms from gridpoint - use Zone forecast instead
".$curlStatus."
".$headers,"
");
$Force = 2;
$usingFile = "(Zone forecast)";
$html = ADV_fetchUrlWithoutHanging($backupfileName);
$lastURL = $backupfileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded $usingFile $backupfileName - $fSize bytes -->
";
if (strpos($html, '{') !== false) { // got a file.. save it
$fp = fopen($cacheName, "w");
if ($fp) {
$write = fputs($fp, $html);
fclose($fp);
$Status.= "<!-- wrote cache file $cacheName -->
";
}
else {
$Status.= "<!-- unable to write cache file $cacheName -->
";
}
}
}
}
}
// now split off the headers from the contents of the return
$stuff = explode("
",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
// check age of forecast
preg_match('!"updateTime":\s*"([^"]+)"!is', $html, $matches);
if(!isset($matches[1])) {
preg_match('!"updated":\s*"([^"]+)"!is', $html, $matches);
}
if (isset($matches[1])) {
$age = time() - strtotime($matches[1]);
$ts = $matches[1];
if ($age > 18 * 60 * 60) {
$agehms = sec2hmsADV($age);
$Status.= "<!-- forecast more than 18hrs old (age h:m:s is $agehms) updated:'$ts' -->
";
list($headers,$content) = explode("
",$html);
$Status .= "<!-- headers from the $usingFile request
".$headers."
-->
";
}
}
$rawJSON = json_decode($content, true); // parse the JSON into an associative array
$FCSTJSON = $rawJSON['properties']; // geoJSON format
if (strlen($content > 10) and function_exists('json_last_error')) { // report status, php >= 5.3.0 only
switch (json_last_error()) {
case JSON_ERROR_NONE:
$JSONerror = '- No errors';
break;
case JSON_ERROR_DEPTH:
$JSONerror = '- Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$JSONerror = '- Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$JSONerror = '- Unexpected control character found';
break;
case JSON_ERROR_SYNTAX:
$JSONerror = '- Syntax error, malformed JSON';
break;
case JSON_ERROR_UTF8:
$JSONerror = '- Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$JSONerror = '- Unknown error';
break;
}
$Status.= "<!-- JSON decode $JSONerror -->
";
if (json_last_error() !== JSON_ERROR_NONE) {
$Status.= "<!-- content='" . print_r($content, true) . "' -->
";
}
}
/*
<!-- FCSTJSON
Array
{
"periods": [
{
"number": 1,
"name": "Today",
"startTime": "2017-05-01T07:00:00-07:00",
"endTime": "2017-05-01T18:00:00-07:00",
"isDaytime": true,
"temperature": 83,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "8 to 17 mph",
"windDirection": "NW",
"icon": "https://api.weather.gov/icons/land/day/skc?size=medium",
"shortForecast": "Sunny",
"detailedForecast": "Sunny, with a high near 83. Northwest wind 8 to 17 mph, with gusts as high as 23 mph."
},
{
"number": 2,
"name": "Tonight",
"startTime": "2017-05-01T18:00:00-07:00",
"endTime": "2017-05-02T06:00:00-07:00",
"isDaytime": false,
"temperature": 53,
"temperatureUnit": "F",
"temperatureTrend": "rising",
"windSpeed": "3 to 17 mph",
"windDirection": "NW",
"icon": "https://api.weather.gov/icons/land/night/skc?size=medium",
"shortForecast": "Clear",
"detailedForecast": "Clear. Low around 53, with temperatures rising to around 55 overnight. Northwest wind 3 to 17 mph, with gusts as high as 23 mph."
},
*/
if ($getGridpointData and isset($META['forecastGridDataURL'])) {
// grab the gridpoint JSON if need be
get_gridpoint_data($cacheName, $META['forecastGridDataURL'], $Force, 3600);
}
if ($getHourlyData and isset($META['forecastHourlyURL'])) {
// grab the Hourly JSON if need be
get_hourly_data($cacheName, $META['forecastHourlyURL'], $Force, 3600);
}
// now process the point or zone forecast
if (isset($FCSTJSON['periods'][0]['icon']) and
strpos($FCSTJSON['periods'][0]['icon'], 'icons') !== false) { // got a point forecast
// -------------- POINT forecast process -----------------
$isZone = false;
$rawForecasts = $FCSTJSON['periods'];
$Status.= "<!-- point forecast processing -->
";
/*
<!-- rawForecasts
Array
(
[0] => Array
(
[number] => 1
[name] => Today
[startTime] => 2016-11-16T08:00:00-08:00
[endTime] => 2016-11-16T18:00:00-08:00
[isDaytime] => 1
[temperature] => 66
[windSpeed] => 15 mph
[windDirection] => NW
[icon] => https://api-v1.weather.gov/icons/land/day/rain_showers,20/sct,20?size=medium
[shortForecast] => Slight Chance Rain Showers then Mostly Sunny
[detailedForecast] => A slight chance of rain showers, mainly before 10am. Mostly sunny, with a high near 66. Northwest wind around 15 mph, with gusts as high as 20 mph. Chance of precipitation is 20%. New rainfall amounts less than a tenth of an inch possible.
)
[1] => Array
(
[number] => 2
[name] => Tonight
[startTime] => 2016-11-16T18:00:00-08:00
[endTime] => 2016-11-17T06:00:00-08:00
[isDaytime] =>
[temperature] => 45
[windSpeed] => 6 to 15 mph
[windDirection] => NW
[icon] => https://api-v1.weather.gov/icons/land/night/sct?size=medium
[shortForecast] => Partly Cloudy
[detailedForecast] => Partly cloudy, with a low around 45. Northwest wind 6 to 15 mph, with gusts as high as 20 mph.
)
*/
$rawUpdated = $FCSTJSON['updateTime'];
if (isset($META['timeZone'])) {
date_default_timezone_set($META['timeZone']);
}
$forecastupdated = date($timeFormat, strtotime($rawUpdated));
$ourPoint = '';
if (preg_match('|/([^/]+)/forecast|i', $fileName, $matches)) {
$ourPoint = $matches[1];
}
$forecastlatlong = $ourPoint;
$forecastcity = $NOAAlocation;
$i = 0;
foreach($rawForecasts as $ptr => $FCST) {
// we'll be setting up:
// $forecasticons[x] x = 0 thru 13 This is the icon and text around it
// $forecasttemp[x] x= 0 thru 13 This is forecast temperature with styling
// $forecasttitles[x] x = 0 thru 13 This is the title word for the text forecast time period
// $forecasttext[x] x = 0 thru 13 This is the detail text for the text forecast time period
// $Status .= "<!-- FCST[$i]
".print_r($FCST,true)." -->
";
if (strlen($FCST['name']) < 5) { // sometimes have a left-over entry w/o a name.. skip it
continue;
}
$forecastcond[$i] = $FCST['shortForecast'];
$forecasticon[$i] = convert_to_local_icon($FCST['icon']);
if ($FCST['isDaytime']) {
$color = '#FF0000';
$tHL = 'Hi';
}
else {
$color = '#0000FF';
$tHL = 'Lo';
}
$tTrend = '';
if (!is_null($FCST['temperatureTrend'])) {
if ($FCST['temperatureTrend'] == 'rising') {
$tTrend = ' ↑';
}
if ($FCST['temperatureTrend'] == 'falling') {
$tTrend = ' ↓';
}
}
$forecasttemp[$i] = "$tHL <span style=\"color: $color;\">" . round($FCST['temperature'], 0) . " °F$tTrend</span>";
$forecasttitles[$i] = $FCST['name'];
$forecasttext[$i] = str_replace("
", ' ', $FCST['detailedForecast']); // sub blank for embedded NL
$forecasticons[$i] = make_local_icon($forecasticon[$i], $forecasttitles[$i], $forecastcond[$i], $forecasttemp[$i], $forecasttext[$i]);
$forecasticons[$i] = preg_replace('/&/', '&', $forecasticons[$i]);
if ($doDebug) {
$Status.= "<!-- i=$i name='" . $forecasttitles[$i] . "' " . "cond='" . $forecastcond[$i] . "' " . "temp='" . $forecasttemp[$i] . "' " . "
rawicon='" . $FCST['icon'] . "' " . "
cvticon='" . $forecasticon[$i] . "' " . "
detail='" . $forecasttext[$i] . "' " . "
icon='" . $forecasticons[$i] . "' -->
";
}
$i++;
} // end foreach FCST point processing
}
elseif (isset($FCSTJSON['periods'])) { // got a ZONE forecast
// $Status .= "<!-- FCSTJSON
".print_r($FCSTJSON,true)." -->
";
// -------------- ZONE forecast process -----------------
$isZone = true;
$Status.= "<!-- ZONE forecast processing -->
";
$rawForecasts = $FCSTJSON['periods'];
$rawUpdated = $FCSTJSON['updated'];
if (isset($META['timeZone'])) {
date_default_timezone_set($META['timeZone']);
}
$forecastupdated = date($timeFormat, strtotime($rawUpdated));
$forecastlatlong = $NOAAZone;
$forecastcity = $NOAAlocation;
$usingFile = "(Zone forecast)";
$Conditions = array(); // prepare for parsing the icon based on the text forecast
load_cond_data(); // initialize the conditions to look for
/*
<!-- FCSTJSON
Array
(
[periods] => Array
(
[0] => Array
(
[number] => 1
[name] => Today
[detailedForecast] => Partly sunny this morning
)
[1] => Array
(
[number] => 2
[name] => Tonight
[detailedForecast] => Partly cloudy. Lows around 40. North winds 5 to 10 mph
with gusts up to 20 mph.
)
[2] => Array
(
[number] => 3
[name] => Saturday
[detailedForecast] => Partly sunny in the morning
)
[3] => Array
(
[number] => 4
[name] => Saturday Night
[detailedForecast] => Mostly cloudy with a 40 percent chance of
showers. Lows in the lower 40s. Northeast winds 5 to 10 mph
)
*/
// Breakup multi-day forecasts if needed
$i = 0;
// foreach ($headers[1] as $j => $period) {
foreach($FCSTJSON['periods'] as $j => $FCST) {
$period = $FCST['name'];
$fcsttext = $FCST['detailedForecast'];
// $Status .= "<!-- raw $j='$period' '$fcsttext' -->
";
$fcsttext = str_replace("
", ' ', $fcsttext); // remove embedded new-line characters
$fcsttext = preg_replace('|\s+|is',' ',$fcsttext); // remove extra spaces
if (strpos($fcsttext, '&&') !== false) {
$fcsttext = substr($fcsttext, 0, strpos($fcsttext, '&&') - 1);
}
$fcsttext = trim($fcsttext);
$Status.= "<!-- adj FCSTJSON['periods'][$j]='$period' '$fcsttext' -->
";
if (preg_match('/^(.*) (Through|And) (.*)/i', $period, $mtemp)) { // got period1 thru period2
list($fcstLow, $fcstHigh) = explode(" ", split_fcst($fcsttext));
$startPeriod = $mtemp[1];
$periodType = $mtemp[2];
$endPeriod = $mtemp[3];
$startIndex = 0;
$endIndex = 0;
$Status.= "<!-- splitting $periodType '$period'='$fcsttext' -->
";
for ($k = 0; $k < count($fcstPeriods); $k++) { // find Starting and ending period indices
if (!$startIndex and $startPeriod == $fcstPeriods[$k]) {
$startIndex = $k;
}
if ($startIndex and !$endIndex and $endPeriod == $fcstPeriods[$k]) {
$endIndex = $k;
break;
}
}
for ($k = $startIndex; $k <= $endIndex; $k++) { // now generate the period names and appropriate fcst
if (preg_match('|night|i', $fcstPeriods[$k])) {
$forecasttext[$i] = $fcstLow;
}
else {
$forecasttext[$i] = $fcstHigh;
}
$forecasttitles[$i] = $fcstPeriods[$k];
$Status.= "<!-- $periodType $j, $i, '" . $forecasttitles[$i] . "'='" . $forecasttext[$i] . "' -->
";
$i++;
}
$Status.= "<!-- end splitting -->
";
continue;
}
$forecasttitles[$i] = $period;
$forecasttext[$i] = strip_tags($fcsttext);
$Status.= "<!-- normal $j, $i, '" . $forecasttitles[$i] . "'='" . $forecasttext[$i] . "' -->
";
$i++;
} // end of multi-day forecast split
$nfcsts = $i - 1;
for ($i = 0; $i <= $nfcsts; $i++) { // interpret the text for icons, summary, temp, PoP
list($forecasticons[$i], $forecasttemp[$i], $forecastpop[$i]) = explode(" ", make_zone_icon($forecasttitles[$i], $forecasttext[$i]));
$forecasticons[$i] = preg_replace('/&/', '&', $forecasticons[$i]);
if ($doDebug) {
$Status.= "<!-- i=$i name='" . $forecasttitles[$i] . "' " . "temp='" . $forecasttemp[$i] . "' " . "
detail='" . $forecasttext[$i] . "' " . "
icon='" . $forecasticons[$i] . "' -->
";
}
} // end interpret text for icons
// end forecast ZONE processing
}
else { // Oops.. got neither point nor zone forecast.
$forecasttitles = array();
$forecasttext = array();
$forecasticons = array();
$forecasttemp = array();
$PrintMode = true;
if (isset($doPrintNWS) && !$doPrintNWS) {
$PrintMode = false;;
}
if (isset($_REQUEST['inc']) && strtolower($_REQUEST['inc']) == 'noprint') {
$PrintMode = false;
}
if ($PrintMode) { ?>
<table style="width: 640px; border: none;">
<tr style="text-align: center;">
<td><b>National Weather Service Forecast for: </b><span style="color: green;">
<?php
echo $NOAAlocation; ?></span>
</td>
</tr>
<?php
echo $ddMenu ?>
</table>
<?php
}
if ($PrintMode) {
print "<p>Sorry.. the forecast for $NOAAlocation is not available at this time.</p>
";
}
print $Status;
if ($PrintMode and strlen($headers) > 0) {
print "<p>NWS server $lastURL has an error.</p>
";
print "<p>View the source of this page for additional information in HTML comments.</p>
";
}
$forecastValid = false;
return; // back to the calling program (if any)
} // end zone/point/nada forecast processing
$forecastValid = true;
$Status.= "<!-- forecast updated '$rawUpdated' ($forecastupdated) for lat,long or zone '$forecastlatlong' -->
";
// -- now get an alert for the zone (if any)
$forecastwarnings = '';
$alertCacheName = str_replace('.txt', '-alerts.txt', $cacheName);
$alerthtml = '';
if ($Force == 1 or $Force == 2 or !file_exists($alertCacheName) or (file_exists($alertCacheName) and filemtime($alertCacheName) + 300 < time())) {
$alerthtml = ADV_fetchUrlWithoutHanging($alertURL);
$fSize = strlen($alerthtml);
$Status.= "<!-- loaded alert for $NOAAZone from $alertURL - $fSize bytes -->
";
if (preg_match('/Location: (.*)
/Uis', $alerthtml, $matches)) {
$newLoc = $matches[1];
$newurl = APIURL . $newLoc;
$alerthtml = ADV_fetchUrlWithoutHanging($newurl);
$fSize = strlen($alerthtml);
$Status.= "<!-- loaded alert from redirect $newurl - $fSize bytes -->
";
}
if (strpos($alerthtml, '[') !== false) { // got a file.. save it
$fp = fopen($alertCacheName, "w");
if ($fp) {
$write = fputs($fp, $alerthtml);
fclose($fp);
$Status.= "<!-- wrote cache file $alertCacheName -->
";
}
else {
$Status.= "<!-- unable to write cache file $alertCacheName -->
";
}
}
}
elseif (file_exists($alertCacheName)) {
$alerthtml = file_get_contents($alertCacheName);
$Status.= "<!-- alerts loaded from $alertCacheName -->
";
}
else {
$Status.= "<!-- alerts information not available -->
";
}
list($alertHeaders, $alertContents) = explode("
", $alerthtml . "
");
if (strlen($alertContents) > 1) { // got some alerts.. process
$ALERTJSON = json_decode($alertContents, true);
if($doDebug) {$Status.= "<!-- ALERTJSON
" . print_r($ALERTJSON, true) . " -->
";}
/*
<!-- ALERTJSON
Array
(
[0] => Array
(
[id] => NWS-IDP-PROD-2135097-1993381
[event] => Wind Advisory
[product_issued] => 2016-11-19T11:29:39+00:00
[product_expires] => 2016-11-19T18:00:00+00:00
[event_starts] => 2016-11-19T11:29:00+00:00
[event_ends] => 2016-11-20T02:00:00+00:00
[headline] => Wind Advisory issued November 19 at 3:29AM PST expiring November 19 at 6:00PM PST by NWS San Francisco CA
[urgency] => Expected
[severity] => Moderate
[certainty] => Likely
[description] => * TIMING...TO 6 PM SATURDAY EVENING.
* LOCATION...SAN FRANCISCO...SAN FRANCISCO BAY SHORELINE...SAN
FRANCISCO PENINSULA COAST...SANTA CLARA VALLEY...EAST BAY
HILLS...SANTA CRUZ MOUNTAINS.
*/
// use the API alerts feed
if (is_array($ALERTJSON) and isset($ALERTJSON['features'][0])) {
$Status.= "<!-- preparing " . count($ALERTJSON['features']) . " warning links -->
";
$Status.= "<!-- now " . date($timeFormat) . " (" . gmdate($timeFormat) . ") -->
";
foreach($ALERTJSON['features'] as $i => $rawALERT) {
$ALERT = $rawALERT['properties']; // geo+json format
$expireUTC = strtotime($ALERT['expires']);
$Status.= "<!-- alert expires " . date($timeFormat, $expireUTC) . " (" . $ALERT['expires'] . ") -->
";
if (
(time() < $expireUTC) && // V5.15 unexpired alerts w/in one of our zones - thanks to Jasiu!
(
(in_array($META["forecastZone"], $ALERT["geocode"]["UGC"]) ) ||
(in_array($META["fireWeatherZone"], $ALERT["geocode"]["UGC"]) ) ||
(in_array($META["countyZone"], $ALERT["geocode"]["UGC"]) )
)
)
{
$forecastwarnings.= '<a href="' . ALERTURL . $ALERT['id'] . '"' . ' title="' . $ALERT['headline'] . "
---
" . $ALERT['description'] . '" target="_blank">' . '<strong><span style="color: red">' . $ALERT['event'] . "</span></strong></a><br/>
";
}
else {
$Status.= "<!-- alert " . $ALERT['id'] . " " . $ALERT['headline'] . " expired - " . $ALERT['expires'] . " -->
";
}
}
}
else {
$Status.= "<!-- no current hazard alerts for $NOAAZone -->
";
}
} // end API feed processing
/*
if(!file_put_contents($URLcacheFile,serialize($URLcache))) {
$Status .= "<!-- Error: unable to save URLcache $URLcacheFile -->
";
} else {
$Status .= "<!-- URL cache saved to $URLcacheFile with ".count($URLcache). " entries. -->
";
}
*/
$forecastoffice = '';
if (isset($META['WFOname'])) {
$forecastoffice = "National Weather Service " . $META['WFOname'];
}
if (isset($META['city']) and isset($META['state'])) {
$forecastcity = $META['city'] . ', ' . $META['state'];
}
$IncludeMode = false;
$PrintMode = true;
if (isset($doPrintNWS) && !$doPrintNWS) {
print $Status; // <------ print before return
return;
}
if (isset($_REQUEST['inc']) && strtolower($_REQUEST['inc']) == 'noprint') {
print $Status; // <------ print before return
return;
}
if (isset($_REQUEST['inc']) && strtolower($_REQUEST['inc']) == 'y') {
$IncludeMode = true;
}
if (isset($doIncludeNWS)) {
$IncludeMode = $doIncludeNWS;
}
// finally, we can print the results if desired
if (!$IncludeMode and $PrintMode) { ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>NWS Forecast for <?php
echo $forecastcity; ?></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body style="font-family:Verdana, Arial, Helvetica, sans-serif; font-size:12px; background-color:#FFFFFF">
<?php
}
print $Status;
// if the forecast text is blank, prompt the visitor to force an update
if (strlen($forecasttext[0]) < 2 and $PrintMode) {
if (!isset($PHP_SELF)) {
$PHP_SELF = $_SERVER['PHP_SELF'];
}
echo '<br/><br/>Forecast blank? <a href="' . $PHP_SELF . '?force=1">Force Update</a><br/><br/>';
}
if ($PrintMode) {
$tw = ($iconWidth + 8) * (1 + count($forecasticons) / 2);
if ($tw < 620) {
$tw = 620;
}
$wdth = $iconWidth + 10 . 'px';
?>
<table style="width: <?php
echo $tw; ?>px; border: none;">
<tr style="text-align: center;">
<td><b>National Weather Service Forecast for: </b><span style="color: green;">
<?php
echo $forecastcity; ?></span><br />
Issued by: <?php
echo $forecastoffice; ?>
</td>
</tr>
<tr>
<td style="text-align: center">Updated: <?php
echo $forecastupdated; ?>
</td><!--end forecastupdated-->
</tr>
<?php
echo $ddMenu ?>
<tr>
<td style="text-align: center; font-size: 18px; margin: 0px auto;"><b><?php
echo $NOAAlocation; ?></b>
<?php
if ($showZoneWarning and $isZone <> '') {
print "<br/><div style=\"border: 1px solid red; padding: 5px; font-size: 12px; margin: 3px;\">";
print "<small>The detailed point forecast weather data is not currently available.<br/>";
print "The zone forecast data for $NOAAZone (" . $META['zoneName'] . ") ";
print "will be displayed<br/>until the point forecast data is again available.";
if(isset($META['WFOphone']) and isset($META['WFOemail'])) {
$t = parse_url($META['forecastGridDataURL']);
print "<br/>If this persists, contact the NWS ".$META['WFOname']." WFO <br/> at ".
$META['WFOphone'] . " or email at ". $META['WFOemail'].
"<br/> to have them update the point forecast for " . $t['path'] .
" on " . $t['host'];
}
print "</small></div>
";
}
?></td>
</tr>
<tr>
<td align="center">
<table style="width: <?php
echo $tw; ?>px; border: none; border-collapse: collapse; border-spacing: 2px;">
<?php
if ($showTwoIconRows) { // show all icons in two rows
// now loop over the $forecasticons array to build the two table rows with icons
if (stripos($forecasttitles[0], 'night') !== false) {
$iStart = - 1;
}
else {
$iStart = 0;
}
print "<tr>
";
for ($i = 0; $i <= count($forecasticons); $i = $i + 2) { // day icon+cond
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasticons[$i + $iStart])) {
print $forecasticons[$i + $iStart];
}
else {
print " ";
}
print "</td>
";
}
print "</tr>
";
print "<tr>
";
for ($i = 0; $i <= count($forecasticons); $i = $i + 2) { // day temperatures
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasttemp[$i + $iStart])) {
print $forecasttemp[$i + $iStart];
}
else {
print " ";
}
print "</td>
";
}
print "</tr>
";
print "<tr><td colspan=\"8\"> </td></tr>
";
print "<tr>
";
for ($i = 1; $i <= count($forecasticons) + 1; $i = $i + 2) { // night icons+conds
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasticons[$i + $iStart])) {
print $forecasticons[$i + $iStart];
}
else {
print " ";
}
print "</td>
";
}
print "</tr>
";
print "<tr>
";
for ($i = 1; $i <= count($forecasticons) + 1; $i = $i + 2) { // night temperatures
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasttemp[$i + $iStart])) {
print $forecasttemp[$i + $iStart];
}
else {
print " ";
}
print "</td>
";
}
print "</tr>
";
print "</table>
";
print "</td>
</tr>
";
}
else { // show only first 9 icons (old style)
?>
<tr valign ="top" align="center">
<?php
for ($i = 0; $i < 9; $i++) {
print "<td style=\"width: 11%;\"><span style=\"font-size: 8pt;\">$forecasticons[$i]</span></td>
";
}
?>
</tr>
<tr valign ="top" align="center">
<?php
for ($i = 0; $i < 9; $i++) {
print "<td style=\"width: 11%;\">$forecasttemp[$i]</td>
";
}
?>
</tr>
</table>
</td>
</tr>
<?php
} // end show only first 9 icons (old style)
?>
</table>
<p><?php
if ($forecastwarnings <> '') {
print $forecastwarnings;
}
?> </p>
<table style="width: <?php
echo $tw; ?>px; border: none; border-collapse: collapse; border-spacing: 2px;">
<?php
for ($i = 0; $i < count($forecasttitles); $i++) {
print "<tr>
";
print "<td style=\"width: 20%; text-align: left; vertical-align: top;\"><b>$forecasttitles[$i]</b><br /> <br /></td>
";
print "<td style=\"width: 80%; text-align: left; vertical-align: top;\">$forecasttext[$i]</td>
";
print "</tr>
";
}
?>
</table>
<p> </p>
<p>Forecast from <a href="<?php
if (strlen($usingFile) > 0) {
echo htmlspecialchars('https://forecast.weather.gov/MapClick.php?zoneid='.$META['forecastZone'].'&zflg=1');
}
else {
list($lat,$lon) = explode(',',$META['point']);
echo htmlspecialchars("https://forecast.weather.gov/MapClick.php?lat=$lat&lon=$lon&unit=0&lg=english");
// echo htmlspecialchars( FCSTURL."/point/".$META['point']);
} ?>">NOAA-NWS</a>
for <?php
echo $forecastcity; ?>. <?php
if ($isZone) {
echo "(Zone forecast for " . $META['zoneName'] . ")";
}
else {
echo $usingFile;
}
if ($iconType == '.gif') {
print "<br/>Animated forecast icons courtesy of <a href=\"http://www.meteotreviglio.com/\">www.meteotreviglio.com</a>.";
}
?>
</p>
<?php
} // end printmode
if (!$IncludeMode and $PrintMode) { ?>
</body>
</html>
<?php
}
// end mainline code -- used functions are below
// ------------------------------------------------------------------------------------------
// FUNCTIONS
function ADV_fetchUrlWithoutHanging($inurl)
{
// get contents from one URL and return as string
global $Status, $needCookie,$doDebug,$doLogging,$curlStatus,$Version /*, $URLcache */;
$useFopen = false;
$overall_start = time();
$curlStatus = '';
if (!$useFopen) {
// Set maximum number of seconds (can have floating-point) to wait for feed before displaying page without feed
$numberOfSeconds = 6;
$url = $inurl;
// Thanks to Curly from ricksturf.com for the cURL fetch functions
$data = '';
$domain = parse_url($url, PHP_URL_HOST);
$theURL = str_replace('nocache', '?' . $overall_start, $url); // add cache-buster to URL if needed
$curlStatus.= "<!-- curl fetching '$theURL' -->
";
$ch = curl_init(); // initialize a cURL session
curl_setopt($ch, CURLOPT_URL, $theURL); // connect to provided URL
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // don't verify peer certificate
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (advforecast2.php (JSON) - saratoga-weather.org)');
// curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, // request LD-JSON format
array(
"Accept: application/geo+json",
"Cache-control: no-cache",
"Pragma: akamai-x-cache-on, akamai-x-get-request-id"
));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $numberOfSeconds); // connection timeout
curl_setopt($ch, CURLOPT_TIMEOUT, $numberOfSeconds); // data timeout
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return the data transfer
curl_setopt($ch, CURLOPT_NOBODY, false); // set nobody
curl_setopt($ch, CURLOPT_HEADER, true); // include header information
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // follow Location: redirect
curl_setopt($ch, CURLOPT_MAXREDIRS, 1); // but only one time
if (isset($needCookie[$domain])) {
curl_setopt($ch, $needCookie[$domain]); // set the cookie for this request
curl_setopt($ch, CURLOPT_COOKIESESSION, true); // and ignore prior cookies
$Status.= "<!-- cookie used '" . $needCookie[$domain] . "' for GET to $domain -->
";
}
$data = curl_exec($ch); // execute session
if (curl_error($ch) <> '') { // IF there is an error
$curlStatus.= "<!-- curl Error: " . curl_error($ch) . " -->
"; // display error notice
}
$cinfo = curl_getinfo($ch); // get info on curl exec.
/*
curl info sample
Array
(
[url] => http://saratoga-weather.net/clientraw.txt
[content_type] => text/plain
[http_code] => 200
[header_size] => 266
[request_size] => 141
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 0.125
[namelookup_time] => 0.016
[connect_time] => 0.063
[pretransfer_time] => 0.063
[size_upload] => 0
[size_download] => 758
[speed_download] => 6064
[speed_upload] => 0
[download_content_length] => 758
[upload_content_length] => -1
[starttransfer_time] => 0.125
[redirect_time] => 0
[redirect_url] =>
[primary_ip] => 74.208.149.102
[certinfo] => Array
(
)
[primary_port] => 80
[local_ip] => 192.168.1.104
[local_port] => 54156
)
*/
if($url !== $cinfo['url'] and $cinfo['http_code'] == 200 and
strpos($url,'/points/') > 0 and strpos($cinfo['url'],'/gridpoints/') > 0) {
# only cache point forecast->gridpoint forecast successful redirects
$curlStatus .= "<!-- note: fetched '".$cinfo['url']."' after redirect was followed. -->
";
//$URLcache[$inurl] = $cinfo['url'];
//$curlStatus .= "<!-- $inurl added to URLcache -->
";
}
$curlStatus.= "<!-- HTTP stats: " . " RC=" . $cinfo['http_code'];
if (isset($cinfo['primary_ip'])) {
$curlStatus .= " dest=" . $cinfo['primary_ip'];
}
if (isset($cinfo['primary_port'])) {
$curlStatus .= " port=" . $cinfo['primary_port'];
}
if (isset($cinfo['local_ip'])) {
$curlStatus.= " (from sce=" . $cinfo['local_ip'] . ")";
}
$curlStatus.= "
Times:" .
" dns=" . sprintf("%01.3f", round($cinfo['namelookup_time'], 3)) .
" conn=" . sprintf("%01.3f", round($cinfo['connect_time'], 3)) .
" pxfer=" . sprintf("%01.3f", round($cinfo['pretransfer_time'], 3));
if ($cinfo['total_time'] - $cinfo['pretransfer_time'] > 0.0000) {
$curlStatus.= " get=" . sprintf("%01.3f", round($cinfo['total_time'] - $cinfo['pretransfer_time'], 3));
}
$curlStatus.= " total=" . sprintf("%01.3f", round($cinfo['total_time'], 3)) . " secs -->
";
// $curlStatus .= "<!-- curl info
".print_r($cinfo,true)." -->
";
curl_close($ch); // close the cURL session
// $curlStatus .= "<!-- raw data
".$data."
-->
";
$stuff = explode("
",$data); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if ($cinfo['http_code'] <> '200') {
$curlStatus.= "<!-- headers returned:
" . $headers . "
-->
";
if($doLogging) {ADV_log('Fetch fail',$curlStatus); }
}
$Status .= $curlStatus;
return $data; // return headers+contents
}
else {
// print "<!-- using file_get_contents function -->
";
$STRopts = array(
'http' => array(
'method' => "GET",
'protocol_version' => 1.1,
'header' => "Cache-Control: no-cache, must-revalidate
" .
"Cache-control: max-age=0
" .
"Connection: close
" .
"User-agent: Mozilla/5.0 (advforecast2.php - saratoga-weather.org)
" .
"Accept: application/geo+json
"
) ,
'ssl' => array(
'method' => "GET",
'protocol_version' => 1.1,
'verify_peer' => false,
'header' => "Cache-Control: no-cache, must-revalidate
" .
"Cache-control: max-age=0
" .
"Connection: close
" .
"User-agent: Mozilla/5.0 (advforecast2.php - saratoga-weather.org)
" .
"Accept: application/geo+json
"
)
);
$STRcontext = stream_context_create($STRopts);
$T_start = ADV_fetch_microtime();
$xml = file_get_contents($inurl, false, $STRcontext);
$T_close = ADV_fetch_microtime();
$headerarray = get_headers($url, 0);
$theaders = join("
", $headerarray);
$xml = $theaders . "
" . $xml;
$ms_total = sprintf("%01.3f", round($T_close - $T_start, 3));
$curlStatus.= "<!-- file_get_contents() stats: total=$ms_total secs -->
";
$curlStatus.= "<-- get_headers returns
" . $theaders . "
-->
";
// print " file() stats: total=$ms_total secs.
";
$overall_end = time();
$overall_elapsed = $overall_end - $overall_start;
$curlStatus.= "<!-- fetch function elapsed= $overall_elapsed secs. -->
";
// print "fetch function elapsed= $overall_elapsed secs.
";
$Status .= $curlStatus;
return ($xml);
}
} // end ADV_fetchUrlWithoutHanging
// ------------------------------------------------------------------
function ADV_fetch_microtime()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
// ------------------------------------------------------------------
function ADV_log($msg,$details)
{
global $doLogging,$doLoggingDetail,$Version,$cacheFileDir,$Status,$META;
if(!$doLogging and !$doLoggingDetail) {return; }
$text = "---------------------------------------------
";
$text .= gmdate('c').' (local: '.date('r').") - $Version
";
$text .= "$msg
";
$text .= "$details
";
$fname = $cacheFileDir.'advforecast2-log-'.date('Y-m-d').'.txt';
if(isset($doLoggingDetail) and $doLoggingDetail) {
file_put_contents($fname,$text,FILE_APPEND);
$Status .= "<!-- logged '$msg' to '$fname' -->
";
}
if(!$doLogging) { return; }
/*
<!-- curl fetching 'https://api.weather.gov/gridpoints/OTX/132,134/forecast' -->
<!-- HTTP stats: RC=500 dest=184.27.36.18 port=443 (from sce=192.168.1.104)
Times: dns=0.002 conn=0.015 pxfer=0.060 get=1.182 total=1.242 secs -->
<!-- headers returned:
HTTP/2 500
server: nginx/1.16.1
content-type: application/problem+json
access-control-allow-origin: *
access-control-allow-headers: Feature-Flags
x-request-id: a81c0162-6bc5-414e-aaa3-948837fd9487
x-correlation-id: a81c0162-6bc5-414e-aaa3-948837fd9487
x-server-id: vm-bldr-nids-apiapp2.ncep.noaa.gov
pragma: no-cache
content-length: 325
cache-control: private, must-revalidate, max-age=883
expires: Wed, 08 Jul 2020 19:09:06 GMT
date: Wed, 08 Jul 2020 18:54:23 GMT
vary: Accept,Feature-Flags,Accept-Language
strict-transport-security: max-age=31536000 ; includeSubDomains ; preload
-->
*/
preg_match('!curl fetching \'(\S+)\' !Uis',$details,$m);
if(isset($m[1])) {$url = trim($m[1]);} else {$url = '';}
preg_match('!RC=(\S+) dest=(\S+) .*from sce=(\S+)\)!Uis',$details,$m);
if(isset($m[2])) {
$RC = $m[1];
$IP = $m[2];
$SIP = $m[3];
} else {
$RC = '';
$IP = '';
$SIP = '';
}
if(preg_match('!Stale Age=(\S+) !Uis',$details,$m)) {
$RC = 'stale ' . trim($m[1]);
if($url == '' and isset($META['forecastURL'])) {$url = $META['forecastURL']; }
}
if($RC == '0' and preg_match('!timed out after (\S+) !Uis',$details,$m)) {
$RC = 'timeout ' . trim($m[1]).'ms';
}
preg_match('!x-server-id: (.+)
!Uis',$details,$m);
if(isset($m[1])) { $svrid = trim($m[1]); } else {$svrid= '';}
preg_match('!x-request-id: (.+)
!Uis',$details,$m);
if(isset($m[1])) { $reqid = trim($m[1]); } else {$reqid= '';}
preg_match('!x-cache: (.+)
!Uis',$details,$m);
if(isset($m[1])) { $cache = trim($m[1]); } else {$cache= '';}
preg_match('!date: (.+)
!Uis',$details,$m);
if(isset($m[1])) { $cdate = trim($m[1]); } else {$cdate= '';}
preg_match('!expires: (.+)
!Uis',$details,$m);
if(isset($m[1])) { $edate = trim($m[1]); } else {$edate= '';}
$text = join(" ",array(date('c'),$url,$RC,$IP,$svrid,$reqid,$cache,$cdate,$edate,$SIP))."
";
$fname = $cacheFileDir.'advforecast2-log-'.date('Y-m-d').'.csv';
if(!file_exists($fname)) {
$text = join(" ",array('Date','URL','RC','IP','SRV-ID','REQ-ID','X-Cache','Hdr-Date','Expires','FromIP'))."
".$text;
}
file_put_contents($fname,$text,FILE_APPEND);
return;
}
// ------------------------------------------------------------------------------------------
// split off Low and High from multiday forecast
function split_fcst($fcst)
{
global $Status;
$f = explode(". ", $fcst . ' ');
$lowpart = 0;
$highpart = 0;
foreach($f as $n => $part) { // find the Low and High sentences
if (preg_match('/Low/i', $part)) {
$lowpart = $n;
}
if (preg_match('/High/i', $part)) {
$highpart = $n;
}
}
$f[$lowpart] = preg_replace('|(\d+) below|s', "-$1", $f[$lowpart]);
$f[$lowpart] = preg_replace('/( above| below| zero)/s', '', $f[$lowpart]);
$f[$lowpart].= '.';
$f[$highpart] = preg_replace('|(\d+) below|s', "-$1", $f[$highpart]);
$f[$highpart] = preg_replace('/( above| below| zero)/s', '', $f[$highpart]);
$f[$highpart].= '.';
$replpart = min($lowpart, $highpart) - 1;
$fcststr = '';
for ($i = 0; $i <= $replpart; $i++) {
$fcststr.= $f[$i] . '. ';
} // generate static fcst text
$fcstLow = $fcststr . ' ' . $f[$lowpart];
$fcstHigh = $fcststr . ' ' . $f[$highpart];
return ("$fcstLow $fcstHigh");
}
// ------------------------------------------------------------------------------------------
// function make_zone_icon: parse text and find suitable icon from zone forecast text for period
function make_zone_icon($day, $textforecast)
{
global $Conditions, $Status, $iconDir, $iconType, $doDebug, $iconHeight, $iconWidth;
$icon = "<strong>";
if (strpos($day, ' ') !== false) {
$icon.= wordwrap($day, 10, "<br/>", false) . '<br/>';
}
else {
$icon.= $day . '<br/><br/>';
}
$icon.= '</strong>';
$temperature = 'n/a';
$pop = '';
$iconimage = 'na.jpg';
$condition = 'N/A';
if (preg_match('|(\S+) (\d+) percent|', $textforecast, $mtemp)) { // found a PoP
$pop = $mtemp[2];
}
if (preg_match('|Chance of precipitation is (\d+)\s*%|', $textforecast, $mtemp)) { // found a zone pop
$pop = $mtemp[1];
if ($doDebug) {
$Status.= "<!-- pop='$pop' found -->
";
}
}
// handle negative temperatures in zone forecast
$textforecast = preg_replace('/([\d|-]+) below/i', "-$1", $textforecast);
$textforecast = preg_replace('/zero/', '0', $textforecast);
if (preg_match('/(High[s]{0,1}|Low[s]{0,1}|Temperatures nearly steady|Temperatures falling to|Temperatures rising to|Near steady temperature|with a high|with a low) (in the upper|in the lower|in the mid|in the low to mid|in the lower to mid|in the mid to upper|in the|around|near|nearly|above|below|from) ([\d|-]+)[s]{0,1}/i', $textforecast, $mtemp)) { // found temp
if ($doDebug) {
$Status.= "<!-- mtemp " . print_r($mtemp, true) . " -->
";
}
if (isset($mtemp[1]) and preg_match('|with a |', $mtemp[1])) {
$mtemp[1] = ucfirst(preg_replace('|with a |', '', $mtemp[1]));
if ($doDebug) {
$Status.= "<!-- mtemp modded to " . print_r($mtemp, true) . " -->
";
}
}
if (substr($mtemp[1], 0, 1) == 'T' or substr($mtemp[1], 0, 1) == 'N') { // use day for highs/night for lows if 'Temperatures nearly steady'
$mtemp[1] = 'Highs';
if (preg_match('|night|i', $day)) {
$mtemp[1] = 'Lows';
}
}
$tcolor = '#FF0000';
if (strtoupper(substr($mtemp[1], 0, 1)) == 'L') {
$tcolor = '#0000FF';
}
$temperature = ucfirst(substr($mtemp[1], 0, 2) . ' <span style="color: ' . $tcolor . '">');
$t = $mtemp[3]; // the raw temp
if (preg_match('/(low to mid|lower to mid|mid to upper|upper|lower|mid)/', $mtemp[2], $ttemp)) {
if ($doDebug) {
$Status.= "<!-- ttemp " . print_r($ttemp, true) . " -->
";
}
$t = $t + 5;
if ($ttemp[1] == 'upper') {
$temperature.= '>' . $t;
}
if ($ttemp[1] == 'lower') {
$temperature.= '<' . $t;
}
if ($ttemp[1] == 'mid') {
$temperature.= '≈' . $t;
}
if ($ttemp[1] == 'low to mid' or $ttemp[1] == 'lower to mid') {
$t = $t - 2;
$temperature.= '≈' . $t;
}
if ($ttemp[1] == 'mid to upper') {
$t = $t + 2;
$temperature.= '≈' . $t;
}
}
if (!isset($ttemp[1]) and !preg_match('/(near|around)/', $mtemp[2])) {
// special case '[Highs|Lows] in the dds.'
$t = $t + 5;
$temperature.= '≈' . $t;
}
elseif (preg_match('/(near|around)/', $mtemp[2], $ttemp)) {
$temperature.= '≈' . $mtemp[3];
}
$temperature.= '°F</span>';
}
if (preg_match('/(Highs|Lows) ([\d|-]+) to ([\d|-]+)/i', $textforecast, $mtemp)) { // temp range forecast
$tcolor = '#FF0000';
if (strtoupper(substr($mtemp[1], 0, 1)) == 'L') {
$tcolor = '#0000FF';
}
$temperature = ucfirst(substr($mtemp[1], 0, 2) . ' <span style="color: ' . $tcolor . '">');
$tavg = sprintf("%0d", round(($mtemp[3] + $mtemp[2]) / 2, 0));
$temperature.= '≈' . $tavg . '°F</span>';
}
if ($temperature == 'n/a') {
if (preg_match('|night|i', $day)) {
$temperature = 'Lo ' . $temperature;
}
else {
$temperature = 'Hi ' . $temperature;
}
}
// now look for harshest conditions first.. (in order in -data file
reset($Conditions); // Do search in load order
foreach($Conditions as $cond => $condrec) { // look for matching condition
if (preg_match("!$cond!i", $textforecast, $mtemp)) {
list($dayicon, $nighticon, $condition) = explode(" ", $condrec);
if (preg_match('|night|i', $day)) {
$iconimage = $nighticon . $pop . $iconType;
}
else {
$iconimage = $dayicon . $pop . $iconType;
}
break;
}
} // end of conditions search
$iconimage = preg_replace('|skc\d+|', 'skc', $iconimage); // handle funky SKC+a POP in forecast.
$shortCond = str_replace('hunderstorm', "-Storm", $condition);
$icon.= '<img src="' . $iconDir . $iconimage .
'" height="' . $iconHeight . '" width="' . $iconWidth . '" ' .
'alt="' . $condition . '" title="' . $condition . '" /><br/>' . $shortCond;
return ("$icon $temperature $pop");
} // end make_zone_icon function
// ------------------------------------------------------------------------------------------
// load the $Conditions array for icon selection based on key phrases
function load_cond_data()
{
global $Conditions, $Status;
$Condstring = '
#
cond|tornado|nsvrtsra|nsvrtsra|Severe storm|
cond|showery or intermittent. Some thunder|scttsra|nscttsra|Showers storms|
cond|thunder possible|scttsra|nscttsra|Showers storms|
cond|thunder|tsra|ntsra|Thunder storm|
cond|rain and sleet|raip|nraip|Rain Sleet|
cond|freezing rain and snow|fzra_sn|nfzra_sn|FrzgRn Snow|
cond|snow and freezing rain|fzra_sn|nfzra_sn|FrzgRn Snow|
cond|chance of snow and rain|rasn|nrasn|Chance Snow/Rain|
cond|chance of rain and snow|rasn|nrasn|Chance Snow/Rain|
cond|rain and snow|rasn|nrasn|Rain and Snow|
cond|rain or snow|rasn|nrasn|Rain or Snow|
cond|freezing rain|fzra|fzra|Freezing Rain|
cond|rain likely|ra|nra|Rain likely|
cond|snow showers|sn|nsn|Snow showers|
cond|showers likely|shra|nshra|Showers likely|
cond|chance showers|shra|nshra|Chance showers|
cond|isolated showers|shra|nshra|Isolated showers|
cond|scattered showers|shra|nshra|Scattered showers|
cond|chance of rain|ra|nra|Chance rain|
cond|rain|ra|nra|Rain|
cond|mix|rasn|rasn|Mix|
cond|sleet|ip|nip|Sleet|
cond|snow|sn|nsn|Snow|
cond|fog in the morning|sctfg|nbknfg|Fog a.m.|
cond|fog after midnight|sctfg|nbknfg|Fog late|
cond|fog|fg|nfg|Fog|
cond|wind chill down to -|cold|cold|Very Cold|
cond|heat index up to 1|hot|hot|Very Hot|
cond|hot|hot|hot|Hot|
cond|overcast|ovc|novc|Overcast|
cond|mostly cloudy|bkn|nbkn|Mostly Cloudy|
cond|partly cloudy|sct|nsct|Partly Cloudy|
cond|cloudy|cloudy|ncloudy|Cloudy|
cond|partly sunny and windy|wind_sct|nwind_sct|Partly Sunny|
cond|mostly sunny and windy|wind_few|nwind_few|Mostly Sunny|
cond|partly sunny and breezy|wind_sct|nwind_sct|Partly Sunny|
cond|mostly sunny and breezy|wind_few|nwind_few|Mostly Sunny|
cond|partly sunny|sct|nsct|Partly Sunny|
cond|mostly sunny|few|nfew|Mostly Sunny|
cond|mostly clear|few|nfew|Mostly Clear|
cond|sunny|skc|nskc|Sunny|
cond|clear|skc|nskc|Clear|
cond|fair|few|nfew|Fair|
cond|cloud|bkn|nbkn|Variable Clouds|
#
';
$config = explode("
", $Condstring);
foreach($config as $key => $rec) { // load the parser condition strings
$recin = trim($rec);
if ($recin and substr($recin, 0, 1) <> '#') { // got a non comment record
list($type, $keyword, $dayicon, $nighticon, $condition) = explode('|', $recin . '|||||');
if (isset($type) and strtolower($type) == 'cond' and isset($condition)) {
$Conditions["$keyword"] = "$dayicon $nighticon $condition";
}
} // end if not comment or blank
} // end loading of loop over config recs
} // end of load_cond_data function
// ------------------------------------------------------------------------------------------
function load_lookups()
{
// static lookup arrays
global $fcstPeriods, $NWSICONLIST;
$fcstPeriods = array( // for filling in the '<period> Through <period>' zone forecasts.
'Monday',
'Monday Night',
'Tuesday',
'Tuesday Night',
'Wednesday',
'Wednesday Night',
'Thursday',
'Thursday Night',
'Friday',
'Friday Night',
'Saturday',
'Saturday Night',
'Sunday',
'Sunday Night',
'Monday',
'Monday Night',
'Tuesday',
'Tuesday Night',
'Wednesday',
'Wednesday Night',
'Thursday',
'Thursday Night',
'Friday',
'Friday Night',
'Saturday',
'Saturday Night',
'Sunday',
'Sunday Night'
);
/* original list:
$NWSICONLIST = array(
# index is new icon name
# imagename is original image name (for which we have icons available)
# PoP flag controls if PoP is written on output or not.
# ScePosition controls if image is pulled from Left, Middle or Right of source image
# Text Name - optional, just a reminder of what it means
# imgname | PoP?=[YN] | ScePosition=[LMR] | Text Name (optional)
'bkn' => 'bkn|N|L|Broken Clouds',
'night/bkn' => 'nbkn|N|L|Night Broken Clouds',
'blizzard' => 'blizzard|Y|L|Blizzard',
'night/blizzard' => 'nblizzard|Y|L|Night Blizzard',
'cold' => 'cold|Y|L|Cold',
'night/cold' => 'cold|Y|L|Cold',
# 'cloudy' => 'cloudy|Y|L|Overcast (old cloudy)',
'dust' => 'du|N|M|Dust',
'night/dust' => 'ndu|N|M|Night Dust',
# 'fc' => 'fc|N|L|Funnel Cloud',
# 'nsvrtsra' => 'nsvrtsra|N|L|Funnel Cloud (old)',
'few' => 'few|Y|L|Few Clouds',
'night/few' => 'nfew|Y|L|Night Few Clouds',
# 'fg' => 'fg|N|R|Fog',
# 'br' => 'br|Y|R|Fog / mist old',
# 'fu' => 'fu|N|L|Smoke',
'fog' => 'nfg|N|R|Night Fog',
'night/fog' => 'nfg|N|R|Night Fog',
'fzra' => 'fzra|Y|L|Freezing rain',
'night/fzra' => 'nfzra|Y|L|Night Freezing Rain',
# 'fzrara' => 'fzrara|Y|L|Rain/Freezing Rain (old)',
'snow_fzra' => 'fzra_sn|Y|L|Freezing Rain/Snow',
'night/snow_fzra' => 'nfzra_sn|Y|L|Night Freezing Rain/Snow',
# 'mix' => 'mix|Y|L|Freezing Rain/Snow',
# 'hi_bkn' => 'hi_bkn|Y|L|Broken Clouds (old)',
# 'hi_few' => 'hi_few|Y|L|Few Clouds (old)',
# 'hi_sct' => 'hi_sct|N|L|Scattered Clouds (old)',
# 'hi_skc' => 'hi_skc|N|L|Clear Sky (old)',
# 'hi_nbkn' => 'hi_nbkn|Y|L|Night Broken Clouds (old)',
# 'hi_nfew' => 'hi_nfew|Y|L|Night Few Clouds (old)',
# 'hi_nsct' => 'hi_nsct|N|L|Night Scattered Clouds (old)',
# 'hi_nskc' => 'hi_nskc|N|L|Night Clear Sky (old)',
# 'hi_nshwrs' => 'hi_nshwrs|Y|R|Night Showers',
# 'hi_ntsra' => 'hi_ntsra|Y|L|Night Thunderstorm',
# 'hi_shwrs' => 'hi_shwrs|Y|R|Showers',
# 'hi_tsra' => 'hi_tsra|Y|L|Thunderstorm',
'hur_warn' => 'hur_warn|N|L|Hurrican Warning',
'night/hur_warn' => 'hur_warn|N|L|Hurrican Warning',
'hur_watch' => 'hur_watch|N|L|Hurricane Watch',
'night/hur_watch' => 'hur_watch|N|L|Hurricane Watch',
# 'hurr' => 'hurr|N|L|Hurrican Warning old',
# 'hurr-noh' => 'hurr-noh|N|L|Hurricane Watch old',
'hazy' => 'hz|N|L|Haze',
'night/hazy' => 'hz|N|L|Haze',
# 'hazy' => 'hazy|N|L|Haze old',
'hot' => 'hot|N|R|Hot',
'night/hot' => 'hot|N|R|Hot',
'sleet' => 'ip|Y|L|Ice Pellets',
'night/sleet' => 'nip|Y|L|Night Ice Pellets',
# 'minus_ra' => 'minus_ra|Y|L|Stopped Raining',
# 'ra1' => 'ra1|N|L|Stopped Raining (old)',
# 'mist' => 'mist|N|R|Mist (fog) (old)',
# 'ncloudy' => 'ncloudy|Y|L|Overcast night(old ncloudy)',
# 'ndu' => 'ndu|N|M|Night Dust',
# 'nfc' => 'nfc|N|L|Night Funnel Cloud',
# 'nbr' => 'nbr|Y|R|Night Fog/mist (old)',
# 'nfu' => 'nfu|N|L|Night Smoke',
# 'nmix' => 'nmix|Y|30|Night Freezing Rain/Snow (old)',
# 'nrasn' => 'nrasn|Y|M|Night Snow (old)',
# 'pcloudyn' => 'pcloudyn|Y|L|Night Partly Cloudy (old)',
# 'nscttsra' => 'nscttsra|Y|M|Night Scattered Thunderstorm',
# 'nsn_ip' => 'nsn_ip|Y|L|Night Snow/Ice Pellets (old)',
# 'nwind' => 'nwind|N|5|Night Windy/Clear (old)',
'ovc' => 'ovc|N|L|Overcast',
'night/ovc' => 'novc|N|L|Night Overcast',
'rain' => 'ra|Y|30|Rain',
'night/rain' => 'nra|Y|30|Night Rain',
'rain_sleet' => 'raip|Y|M|Rain/Ice Pellets',
'night/rain_sleet' => 'nraip|Y|M|Night Rain/Ice Pellets',
'rain_fzra' => 'ra_fzra|Y|30|Rain/Freezing Rain',
'night/rain_fzra' => 'nra_fzra|Y|30|Night Freezing Rain',
'rain_snow' => 'ra_sn|Y|M|Rain/Snow',
'night/rain_snow' => 'nra_sn|Y|M|Night Rain/Snow',
# 'rasn' => 'rasn|Y|M|Rain/Snow (old)',
'sct' => 'sct|N|L|name',
'night/sct' => 'nsct|N|L|Night Scattered Clouds',
# 'pcloudy' => 'pcloudy|Y|L|Partly Cloudy (old)',
# 'scttsra' => 'scttsra|Y|M|name',
'rain_showers' => 'shra|Y|10|Rain Showers',
'night/rain_showers' => 'nshra|Y|8|Night Rain Showers',
# 'shra2' => 'shra2|N|10|Rain Showers (old)',
'skc' => 'skc|N|L|Clear',
'night/skc' => 'nskc|N|L|Night Clear',
'snow' => 'sn|Y|L|Snow',
'night/snow' => 'nsn|Y|L|Night Snow',
'snow_sleet' => 'snip|Y|L|Snow/Ice Pellets',
'night/snow_sleet' => 'nsnip|Y|L|Night Snow/Ice Pellets',
'smoke' => 'smoke|N|L|Smoke',
'night/smoke' => 'nsmoke|N|L|Smoke', // NEW - Nov-2016
# 'sn_ip' => 'sn_ip|Y|L|Snow/Ice Pellets (old)',
# 'tcu' => 'tcu|N|L|Towering Cumulus (old)',
'tornado' => 'tor|N|L|Tornado',
'night/tornado' => 'ntor|N|L|Night Tornado',
'tsra' => 'tsra|Y|10|Thunderstorm',
'night/tsra' => 'ntsra|Y|8|Night Thunderstorm',
# 'tstormn' => 'tstormn|N|L|Thunderstorm night (old)',
# 'ts_nowarn' => 'ts_nowarn|N|L|Tropical Storm',
'ts_warn' => 'ts_warn|N|L|Tropical Storm Warning',
'night/ts_warn' => 'ts_warn|N|L|Tropical Storm Warning',
# 'tropstorm-noh' => 'tropstorm-noh|N|L|Tropical Storm old',
# 'tropstorm' => 'tropstorm|N|L|Tropical Storm Warning old',
'ts_watch' => 'ts_watch|N|L|Tropical Storm Watch',
'night/ts_watch' => 'ts_watch|N|L|Tropical Storm Watch',
# 'ts_hur_flags' => 'ts_hur_flags|Y|L|Hurrican Warning old',
# 'ts_no_flag' => 'ts_no_flag|Y|L|Tropical Storm old',
'wind_bkn' => 'wind_bkn|N|8|Windy/Broken Clouds',
'night/wind_bkn' => 'nwind_bkn|N|5|Night Windy/Broken Clouds',
'wind_few' => 'wind_few|N|8|Windy/Few Clouds',
'night/wind_few' => 'nwind_few|N|5|Night Windy/Few Clouds',
'wind_ovc' => 'wind_ovc|N|8|Windy/Overcast',
'night/wind_ovc' => 'nwind_ovc|N|5|Night Windy/Overcast',
'wind_sct' => 'wind_sct|N|8|Windy/Scattered Clouds',
'night/wind_sct' => 'nwind_sct|N|5|Night Windy/Scattered Clouds',
'wind_skc' => 'wind_skc|N|8|Windy/Clear',
'night/wind_skc' => 'nwind_skc|N|5|Night Windy/Clear',
# 'wind' => 'wind|N|L|Windy/Clear (old)',
'na' => 'N|L|Not Available',
);
*/
$NWSICONLIST = array(
// index is new icon name
// imagename is original image name (for which we have icons available)
// PoP flag controls if PoP is written on output or not.
// ScePosition controls if image is pulled from Left, Middle or Right of source image
// Text Name - optional, just a reminder of what it means
// imgname | PoP?=[YN] | ScePosition=[LMR] | Text Name (optional)
'bkn' => 'bkn|N|L|Broken Clouds',
'night/bkn' => 'nbkn|N|L|Night Broken Clouds',
'blizzard' => 'blizzard|Y|L|Blizzard',
'night/blizzard' => 'nblizzard|Y|L|Night Blizzard',
'cold' => 'cold|Y|L|Cold',
'night/cold' => 'cold|Y|L|Cold',
// 'cloudy' => 'cloudy|Y|L|Overcast (old cloudy)',
'dust' => 'du|N|22|Dust',
'night/dust' => 'ndu|N|22|Night Dust',
// 'fc' => 'fc|N|L|Funnel Cloud',
// 'nsvrtsra' => 'nsvrtsra|N|L|Funnel Cloud (old)',
'few' => 'few|Y|L|Few Clouds',
'night/few' => 'nfew|Y|L|Night Few Clouds',
// 'fg' => 'fg|N|R|Fog',
// 'br' => 'br|Y|R|Fog / mist old',
// 'fu' => 'fu|N|L|Smoke',
'fog' => 'fg|N|R|Fog',
'night/fog' => 'nfg|N|R|Night Fog',
'fzra' => 'fzra|Y|L|Freezing rain',
'night/fzra' => 'nfzra|Y|L|Night Freezing Rain',
// 'fzrara' => 'fzrara|Y|L|Rain/Freezing Rain (old)',
'snow_fzra' => 'fzra_sn|Y|L|Freezing Rain/Snow',
'night/snow_fzra' => 'nfzra_sn|Y|L|Night Freezing Rain/Snow',
// 'mix' => 'mix|Y|L|Freezing Rain/Snow',
// 'hi_bkn' => 'hi_bkn|Y|L|Broken Clouds (old)',
// 'hi_few' => 'hi_few|Y|L|Few Clouds (old)',
// 'hi_sct' => 'hi_sct|N|L|Scattered Clouds (old)',
// 'hi_skc' => 'hi_skc|N|L|Clear Sky (old)',
// 'hi_nbkn' => 'hi_nbkn|Y|L|Night Broken Clouds (old)',
// 'hi_nfew' => 'hi_nfew|Y|L|Night Few Clouds (old)',
// 'hi_nsct' => 'hi_nsct|N|L|Night Scattered Clouds (old)',
// 'hi_nskc' => 'hi_nskc|N|L|Night Clear Sky (old)',
// 'hi_nshwrs' => 'hi_nshwrs|Y|R|Night Showers',
// 'hi_ntsra' => 'hi_ntsra|Y|L|Night Thunderstorm',
// 'hi_shwrs' => 'hi_shwrs|Y|R|Showers',
// 'hi_tsra' => 'hi_tsra|Y|L|Thunderstorm',
'hur_warn' => 'hur_warn|N|L|Hurrican Warning',
'night/hur_warn' => 'hur_warn|N|L|Hurrican Warning',
"hurr_warn" => "hurr|N|L|Hurricane warning", // New NWS list
"night/hurr_warn" => "hurr|N|L|Night Hurricane warning", // New NWS list
'hur_watch' => 'hur_watch|N|L|Hurricane Watch',
'night/hur_watch' => 'hur_watch|N|L|Hurricane Watch',
"hurr_watch" => "hurr-noh|N|L|Hurricane watch", // New NWS list
"night/hurr_watch" => "hurr-noh|N|L|Night Hurricane watch", // New NWS list
'hurricane' => 'hurr-noh|Y|L|Hurricane',
'night/hurricane' => 'hurr-noh|Y|L|Hurricane',
// 'hurr' => 'hurr|N|L|Hurrican Warning old',
// 'hurr-noh' => 'hurr-noh|N|L|Hurricane Watch old',
'hazy' => 'hz|N|L|Haze',
'night/hazy' => 'hz|N|L|Haze',
"haze" => "hz|N|L|Haze", // New NWS list
"night/haze" => "hz|N|L|Night Haze", // New NWS list
// 'hazy' => 'hazy|N|L|Haze old',
'hot' => 'hot|N|R|Hot',
'night/hot' => 'hot|N|R|Hot',
'sleet' => 'ip|Y|L|Ice Pellets',
'night/sleet' => 'nip|Y|L|Night Ice Pellets',
// 'minus_ra' => 'minus_ra|Y|L|Stopped Raining',
// 'ra1' => 'ra1|N|L|Stopped Raining (old)',
// 'mist' => 'mist|N|R|Mist (fog) (old)',
// 'ncloudy' => 'ncloudy|Y|L|Overcast night(old ncloudy)',
// 'ndu' => 'ndu|N|M|Night Dust',
// 'nfc' => 'nfc|N|L|Night Funnel Cloud',
// 'nbr' => 'nbr|Y|R|Night Fog/mist (old)',
// 'nfu' => 'nfu|N|L|Night Smoke',
// 'nmix' => 'nmix|Y|30|Night Freezing Rain/Snow (old)',
// 'nrasn' => 'nrasn|Y|M|Night Snow (old)',
// 'pcloudyn' => 'pcloudyn|Y|L|Night Partly Cloudy (old)',
// 'nscttsra' => 'nscttsra|Y|M|Night Scattered Thunderstorm',
// 'nsn_ip' => 'nsn_ip|Y|L|Night Snow/Ice Pellets (old)',
// 'nwind' => 'nwind|N|5|Night Windy/Clear (old)',
'ovc' => 'ovc|N|L|Overcast',
'night/ovc' => 'novc|N|L|Night Overcast',
'rain' => 'ra|Y|30|Rain',
'night/rain' => 'nra|Y|30|Night Rain',
'rain_showers' => 'shra|Y|12|Rain Showers',
'night/rain_showers' => 'nshra|Y|12|Night Rain Showers',
"rain_showers_hi" => "hi_shwrs|Y|45|Rain showers (low cloud cover)", // New NWS list
"night/rain_showers_hi" => "hi_nshwrs|Y|45|Night Rain showers (low cloud cover)", // New NWS list
'rain_sleet' => 'raip|Y|M|Rain/Ice Pellets',
'night/rain_sleet' => 'nraip|Y|M|Night Rain/Ice Pellets',
'rain_fzra' => 'ra_fzra|Y|30|Rain/Freezing Rain',
'night/rain_fzra' => 'nra_fzra|Y|30|Night Freezing Rain',
'rain_snow' => 'ra_sn|Y|22|Rain/Snow',
'night/rain_snow' => 'nra_sn|Y|22|Night Rain/Snow',
// 'rasn' => 'rasn|Y|M|Rain/Snow (old)',
'sct' => 'sct|N|L|name',
'night/sct' => 'nsct|N|L|Night Scattered Clouds',
// 'pcloudy' => 'pcloudy|Y|L|Partly Cloudy (old)',
// 'scttsra' => 'scttsra|Y|M|name',
// 'shra2' => 'shra2|N|10|Rain Showers (old)',
'skc' => 'skc|N|L|Clear',
'night/skc' => 'nskc|N|L|Night Clear',
'snow' => 'sn|Y|L|Snow',
'night/snow' => 'nsn|Y|L|Night Snow',
'snow_sleet' => 'sn_ip|Y|L|Snow/Ice Pellets',
'night/snow_sleet' => 'nsn_ip|Y|L|Night Snow/Ice Pellets',
'smoke' => 'fu|N|L|Smoke',
'night/smoke' => 'nfu|N|L|Smoke', // NEW - Nov-2016
// 'sn_ip' => 'sn_ip|Y|L|Snow/Ice Pellets (old)',
// 'tcu' => 'tcu|N|L|Towering Cumulus (old)',
'tornado' => 'tor|N|L|Tornado',
'night/tornado' => 'ntor|N|L|Night Tornado',
'tsra' => 'tsra|Y|10|Thunderstorm',
'night/tsra' => 'ntsra|Y|10|Night Thunderstorm',
"tsra_sct" => "scttsra|Y|20|Thunderstorm (medium cloud cover)", // New NWS list
"night/tsra_sct" => "nscttsra|Y|20|Night Thunderstorm (medium cloud cover)", // New NWS list
"tsra_hi" => "hi_tsra|Y|L|Thunderstorm (low cloud cover)", // New NWS list
"night/tsra_hi" => "hi_ntsra|Y|L|Night Thunderstorm (low cloud cover)", // New NWS list
// 'tstormn' => 'tstormn|N|L|Thunderstorm night (old)',
// 'ts_nowarn' => 'ts_nowarn|N|L|Tropical Storm',
"ts_hurr_warn" => "ts_hur_flags|N|L|Tropical storm with hurricane warning in effect", // New NWS list
"night/ts_hurr_warn" => "ts_hur_flags|N|L|Night Tropical storm with hurricane warning in effect", // New NWS list
'tropical_storm' => 'tropstorm-noh|Y|L|Tropical Storm Warning',
'night/tropical_storm' => 'tropstorm-noh|Y|L|Tropical Storm Warning',
'ts_warn' => 'tropstorm|Y|L|Tropical Storm Warning',
'night/ts_warn' => 'tropstorm|Y|L|Tropical Storm Warning',
// 'tropstorm-noh' => 'tropstorm-noh|N|L|Tropical Storm old',
// 'tropstorm' => 'tropstorm|N|L|Tropical Storm Warning old',
'ts_watch' => 'tropstorm-noh|Y|L|Tropical Storm Watch',
'night/ts_watch' => 'tropstorm-noh|Y|L|Tropical Storm Watch',
// 'ts_hur_flags' => 'ts_hur_flags|Y|L|Hurrican Warning old',
// 'ts_no_flag' => 'ts_no_flag|Y|L|Tropical Storm old',
'wind_bkn' => 'wind_bkn|N|7|Windy/Broken Clouds',
'night/wind_bkn' => 'nwind_bkn|N|7|Night Windy/Broken Clouds',
'wind_few' => 'wind_few|N|7|Windy/Few Clouds',
'night/wind_few' => 'nwind_few|N|7|Night Windy/Few Clouds',
'wind_ovc' => 'wind_ovc|N|7|Windy/Overcast',
'night/wind_ovc' => 'nwind_ovc|N|7|Night Windy/Overcast',
'wind_sct' => 'wind_sct|N|7|Windy/Scattered Clouds',
'night/wind_sct' => 'nwind_sct|N|7|Night Windy/Scattered Clouds',
'wind_skc' => 'wind_skc|N|7|Windy/Clear',
'night/wind_skc' => 'nwind_skc|N|7|Night Windy/Clear',
// 'wind' => 'wind|N|L|Windy/Clear (old)',
'na' => 'na|N|L|Not Available',
);
} // end load_lookups function
// ------------------------------------------------------------------------------------------
function convert_to_local_icon($icon)
{
// input: https://api-v1.weather.gov/icons/land/day/rain_showers,20/sct,20?size=medium
// https://api-v1.weather.gov/icons/land/night/sct?size=medium
// output: {iconDir}{icon}.{$iconType} or
// DualImage.php?i={lefticon}&j={righticon}&ip={leftpop}&jp={rightpop}
global $Status, $NWSICONLIST, $iconDir, $iconType, $iconHeight, $iconWidth, $DualImageAvailable;
$newicon = $icon; // for testing
$uparts = parse_url($icon);
$iparts = array_slice(explode('/', $uparts['path']) , 3); //get day|night/icon[/icon]
// $Status .= "<!-- iparts
".print_r($iparts,true)." -->
";
$daynight = ($iparts[0] == 'day') ? '' : 'night/';
list($icon1, $pop1) = explode(',', $iparts[1] . ',');
$doDual = false;
if (isset($iparts[2])) {
$doDual = true;
list($icon2, $pop2) = explode(',', $iparts[2] . ',');
}
else {
$icon2 = '';
$pop2 = '';
}
// convert new API icon names to old image names
if (isset($NWSICONLIST["{$daynight}{$icon1}"])) {
list($nicon1, $rest) = explode('|', $NWSICONLIST["{$daynight}{$icon1}"]);
$icon1 = $nicon1;
}
else {
$Status.= "<!-- icon1='$icon1' not found - na used instead -->
";
$icon1 = 'na';
}
if ($icon2 <> '') {
if (isset($NWSICONLIST["{$daynight}{$icon2}"])) {
list($nicon2, $rest) = explode('|', $NWSICONLIST["{$daynight}{$icon2}"]);
$icon2 = $nicon2;
}
else {
$Status.= "<!-- icon2='$icon2' not found - na used instead -->
";
$icon2 = 'na';
}
}
// $Status .= "<!-- doDual='$doDual' DualImageAvailable='$DualImageAvailable' icon1=$icon1 icon2=$icon2 -->
";
if ($doDual and $DualImageAvailable) { // generate the DualImage.php script calling sequence for image
$newicon = "DualImage.php?";
$newicon.= "i=$icon1";
if ($pop1 <> '') {
$newicon.= "&ip=$pop1";
}
$newicon.= "&j=$icon2";
if ($pop2 <> '') {
$newicon.= "&jp=$pop2";
}
$Status.= "<!-- dual image '$newicon' used-->
";
}
elseif (file_exists("{$iconDir}{$icon1}{$pop1}{$iconType}")) { // use the image as-is
$newicon = "{$iconDir}{$icon1}{$pop1}{$iconType}";
/* } elseif ( $DualImageAvailable ) { // oops... pop icon doesn't exist but we can generate it
$newicon = "DualImage.php?";
$newicon .= "i=$icon1";
if($pop1 <> '') { $newicon .= "&ip=$pop1"; }
$Status .= "<!-- missing icon '{$iconDir}{$icon1}{$pop1}{$iconType}' .. using '$newicon' instead -->
";
*/
}
elseif (file_exists("{$iconDir}{$icon1}{$iconType}")) { // oops... pop icon doesn't exist
$newicon = "{$iconDir}{$icon1}{$iconType}";
$Status.= "<!-- missing icon '{$iconDir}{$icon1}{$pop1}{$iconType}' .. " . "using '{$iconDir}{$icon1}{$iconType}' instead -->
";
}
else {
$newicon = "{$iconDir}na{$iconType}";
$Status.= "<!-- missing icon '{$iconDir}{$icon1}{$pop1}{$iconType}' .. " . "using '{$iconDir}na{$iconType}' instead -->
";
}
return ($newicon);
} // end convert_to_local_icon
// ------------------------------------------------------------------------------------------
function make_local_icon($icon, $period, $cond, $temperature, $details)
{
// assemble the full icon to use
$ticon = '<strong>';
if (strpos($period, ' ') !== false) {
$ticon.= wordwrap($period, 10, "<br/>", false) . '<br/>';
// $ticon = str_replace(' ','<br/>',$ticon) . '<br/>';
}
else {
$ticon.= $period . '<br/><br/>';
}
$ticon.= '</strong>';
$shortCond = str_replace('hunderstorm', "-Storm", $cond);
$ticon.= '<img src="' . $icon . '" ' . "alt=\"$period: $cond\" " . "title=\"$period: $details\" /><br/>" . "$shortCond <br/>";
$ticon = str_replace('size=medium', 'size=small', $ticon);
return ($ticon);
} // end make_local_icon
// ------------------------------------------------------------------------------------------
function get_meta_info($mainCache, $pointURL, $zoneURL)
{
//
// this function saves up-to 3 accesses to the API site for metadata regarding the
// point, zone and WFO info. The three accesses are done, the JSON parsed and
// saved into a -json-meta.txt file as straight JSON for use in the $META array
// in the main script.
// Note: the logic below assumes that the JSON-LD format is returned by the API.
//
global $Status, $doDebug;
$compass = array(
'N',
'NNE',
'NE',
'ENE',
'E',
'ESE',
'SE',
'SSE',
'S',
'SSW',
'SW',
'WSW',
'W',
'WNW',
'NW',
'NNW'
);
// get and cache the meta information from the point, zone and WFO entries for the 'point'.
// use a new JSON cache file to store the meta information we need
//
$metaCache = str_replace('-geojson.txt', '-geojson-meta.txt', $mainCache);
// make the point-forecast URL into a metadata request URL
$metaPointURL = str_replace('/forecast', '', $pointURL);
$ourPoint = '';
if (preg_match('|/([^/]+)/forecast|i', $pointURL, $matches)) {
$ourPoint = $matches[1];
}
// make the zone forecast URL into a metadata request URL
$metaZoneURL = $zoneURL;
$metaZoneURL = preg_replace('!/forecast$!is', '', $metaZoneURL);
//$metaZoneURL = str_replace('JSON-LD', 'forecast', $metaZoneURL);
$ourZone = '';
if (preg_match('|/([^/]+)/forecast|i', $zoneURL, $matches)) {
$ourZone = $matches[1];
}
$Status.= "<!-- meta info re: point='$ourPoint' zone='$ourZone' metacache= '$metaCache' -->
";
$Status.= "<!-- metaZoneURL='$metaZoneURL' -->
";
$META = array();
// First.. see if we've already cached this meta info.. it won't change once discovered for a point
if(file_exists($metaCache)) {
$age = time() - filemtime($metaCache);
$Status.= "<!-- meta cache $metaCache age=".sec2hmsADV($age)." h:m:s -->
";
}
if (file_exists($metaCache) and filemtime($metaCache) + 24*60*60 > time() ) {
$recs = file_get_contents($metaCache);
$age = time() - filemtime($metaCache);
$META = json_decode($recs, true);
$Status.= "<!-- loaded meta info from $metaCache age=".sec2hmsADV($age)." h:m:s -->
";
if (isset($META['point']) and $META['point'] !== $ourPoint) {
// oops... changed the lat/long for the point .. reload the meta data
$Status.= "<!-- point '" . $ourPoint . "' changed from '" . $META['point'] . "' - reloading meta data -->
";
unset($META);
$META = array();
}
}
else { // JSON ERROR
$Status.= "<!-- Meta cache file not found or more than 24hrs old .. reloading meta data from NWS site -->
";
}
$saveNew = false;
if (!isset($META['city']) or !isset($META['forecastURL'])) { // no city data or fcstURL in the saved metadata so load it
$Status.= "<!-- getting metadata for point from $metaPointURL -->
";
$rawhtml = ADV_fetchUrlWithoutHanging($metaPointURL);
$stuff = explode("
",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
$rawPJSON = json_decode($content, true); // parse the JSON into an associative array
$PJSON = $rawPJSON['properties']; // geojson format
if (json_last_error() == JSON_ERROR_NONE) { // got the point METADATA.. stuff it away
if ($doDebug) {
$Status.= "<!-- PJSON raw data
" . var_export($PJSON, true) . " -->
";
}
if (isset($PJSON['relativeLocation']['properties']['city'])) {
$META['city'] = $PJSON['relativeLocation']['properties']['city'];
$META['state'] = $PJSON['relativeLocation']['properties']['state'];
$distance = $PJSON['relativeLocation']['properties']['distance']['value'];
$distance = floor(0.000621371 * $distance); // truncate to nearest whole mile
$Status.= "<!-- distance=$distance from " . $META['city'] . " -->
";
if ($distance >= 2) {
$angle = $PJSON['relativeLocation']['properties']['bearing']['value'];
$direction = $compass[round($angle / 22.5) % 16];
$t = $distance . ' ';
$t.= ($distance > 1) ? "Miles" : "Mile";
$t.= " $direction ";
$META['city'] = $t . $META['city'];
}
$META['point'] = $ourPoint;
$META['forecastOfficeURL'] = $PJSON['forecastOffice'];
$META['forecastURL'] = $PJSON['forecast'];
$META['forecastZoneURL'] = $PJSON['forecastZone'];
$META['forecastZone'] = substr($META['forecastZoneURL'], strrpos($META['forecastZoneURL'], '/') + 1);
$META['forecastHourlyURL'] = $PJSON['forecastHourly'];
$META['forecastGridDataURL'] = $PJSON['forecastGridData'];
$META['observationStationsURL'] = $PJSON['observationStations'];
$META['countyZoneURL'] = $PJSON['county'];
$META['countyZone'] = substr($META['countyZoneURL'], strrpos($META['countyZoneURL'], '/') + 1);
$META['fireWeatherZoneURL'] = $PJSON['fireWeatherZone'];
$META['fireWeatherZone'] = substr($META['fireWeatherZoneURL'], strrpos($META['fireWeatherZoneURL'], '/') + 1);
$META['timeZone'] = $PJSON['timeZone'];
$META['radarStation'] = $PJSON['radarStation'];
$saveNew = true;
if ($doDebug) {
$Status.= "<!-- META data
" . print_r($META, true) . " -->
";
}
}
}
else { // JSON ERROR
$Status.= "<!-- JSON ERROR with Point metadata content='" . print_r($content, true) . " -->
";
}
}
if (!isset($META['zoneName'])) { // no zone data.. load it
$Status.= "<!-- getting metadata for forecast zone from $metaZoneURL -->
";
$rawhtml = ADV_fetchUrlWithoutHanging($metaZoneURL);
$stuff = explode("
",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
$rawPJSON = json_decode($content, true); // parse the JSON into an associative array
$PJSON = $rawPJSON['properties']; // geojson format
if (json_last_error() == JSON_ERROR_NONE) { // got the point METADATA.. stuff it away
if ($doDebug) {
$Status.= "<!-- zone raw data
" . print_r($PJSON, true) . " -->
";
}
if (isset($PJSON['name'])) {
$META['zoneName'] = $PJSON['name'];
$saveNew = true;
}
}
else { // JSON ERROR
$Status.= "<!-- JSON ERROR with Zone metadata content='" . print_r($content, true) . " -->
";
}
}
if (!isset($META['WFOemail']) and isset($META['forecastOfficeURL'])) {
// GET the WFO data
$WFOmetaURL = $META['forecastOfficeURL'];
$WFOmetaURL = str_replace('http://', 'https://', $WFOmetaURL);
$Status.= "<!-- getting metadata for forecast office from $WFOmetaURL -->
";
$rawhtml = ADV_fetchUrlWithoutHanging($WFOmetaURL);
$stuff = explode("
",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
$PJSON = json_decode($content, true); // parse the JSON into an associative array
if (json_last_error() == JSON_ERROR_NONE) { // got the point METADATA.. stuff it away
if ($doDebug) {
$Status.= "<!-- WFO raw data
" . var_export($PJSON, true) . " -->
";
}
if (isset($PJSON['name'])) {
$META['WFOname'] = $PJSON['name'];
if (isset($PJSON['address']['addressLocality']) and stripos($META['WFOname'], $PJSON['address']['addressLocality']) === false) {
$META['WFOname'] = str_replace(',', '/' . $PJSON['address']['addressLocality'] . ',', $META['WFOname']);
}
if (isset($PJSON['telephone'])) {
$META['WFOphone'] = trim($PJSON['telephone']);
}
if (isset($PJSON['email'])) {
$META['WFOemail'] = trim($PJSON['email']);
}
$saveNew = true;
}
}
else { // JSON ERROR
$Status.= "<!-- JSON ERROR with WFO metadata content='" . print_r($content, true) . " -->
";
}
}
if ($doDebug) {
$Status.= "<!-- final META data
" . var_export($META, true) . "
saveNew=$saveNew -->
";
}
if ($saveNew) { // save the file if we changed anything
$outJSON = json_encode($META);
$fp = fopen($metaCache, "w");
if ($fp) {
$write = fputs($fp, $outJSON);
fclose($fp);
$Status.= "<!-- wrote meta cache file $metaCache -->
";
}
else {
$Status.= "<!-- unable to write meta cache file $metaCache -->
";
}
}
$Status.= "<!-- META
" . var_export($META, true) . " -->
";
return $META;
}
// ------------------------------------------------------------------------------------------
function get_gridpoint_data($mainCache, $gridpointURL, $forceFlag, $refreshTime)
{
//
//
global $Status, $doDebug;
// get and cache the gridpoint information
//
$gpCache = str_replace('-geojson.txt', '-geojson-gridpoint.txt', $mainCache);
$gpURL = str_replace('http://', 'https://', $gridpointURL);
$Status.= "<!-- gridpoint data cache= '$gpCache' from '$gpURL' -->
";
if ($forceFlag > 0 or !file_exists($gpCache) or (file_exists($gpCache) and filemtime($gpCache) + $refreshTime < time())) {
$Status.= "<!-- getting gridpoint data -->
";
$rawhtml = ADV_fetchUrlWithoutHanging($gpURL);
$stuff = explode("
",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if (strlen($content) > 500) {
$fp = fopen($gpCache, "w");
if ($fp) {
$write = fputs($fp, $content);
fclose($fp);
$Status.= "<!-- wrote " . strlen($content) . " bytes to gridpoint data cache file $gpCache -->
";
}
else {
$Status.= "<!-- unable to write gridpoint data cache file $gpCache -->
";
}
}
else {
$Status.= "<!-- unable to obtain gridpoint data .. headers:
" . print_r($headers, true) . "
-->
";
} // end got some content
}
else {
$gpAge = time() - filemtime($gpCache);
$Status.= "<!-- gridpoint data cache is current (".sec2hmsADV($gpAge)." h:m:s old). -->
";
} // end reload gridpoint data
} // end get_gridpoint_data
// ------------------------------------------------------------------------------------------
function get_hourly_data($mainCache, $gridpointURL, $forceFlag, $refreshTime)
{
//
//
global $Status, $doDebug;
// get and cache the gridpoint information
//
$gpCache = str_replace('-geojson.txt', '-geojson-hourly.txt', $mainCache);
$gpURL = str_replace('http://', 'https://', $gridpointURL);
$Status.= "<!-- hourly data cache= '$gpCache' from '$gpURL' -->
";
if ($forceFlag > 0 or !file_exists($gpCache) or (file_exists($gpCache) and filemtime($gpCache) + $refreshTime < time())) {
$Status.= "<!-- getting hourly -->
";
$rawhtml = ADV_fetchUrlWithoutHanging($gpURL);
$stuff = explode("
",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if (strlen($content) > 500) {
$fp = fopen($gpCache, "w");
if ($fp) {
$write = fputs($fp, $content);
fclose($fp);
$Status.= "<!-- wrote " . strlen($content) . " bytes to hourly data cache file $gpCache -->
";
}
else {
$Status.= "<!-- unable to write hourly data cache file $gpCache -->
";
}
}
else {
$Status.= "<!-- unable to obtain hourly data .. headers:
" . print_r($headers, true) . "
-->
";
} // end got some content
}
else {
$gpAge = time() - filemtime($gpCache);
$Status.= "<!-- hourly data cache is current ($gpAge secs old). -->
";
} // end reload hourly data
} // end get_hourly_data
// ------------------------------------------------------------------------------------------
function convert_filename($inURL, $NOAAZone)
{
global $Status, $metaURL, $zoneURL, $alertURL;
$fileName = $inURL;
// handle OLD formats of NWS URLS
// autocorrect the point-forecast URL if need be
/*
// from: http://forecast.weather.gov/MapClick.php?CityName=Rathdrum&state=ID&site=MTR&textField1=47.828&textField2=-116.842&e=0&TextType=2
// to:
https://api.weather.gov/points/47.82761,-116.842/forecast
NOTE: the lat,long must be decimal numbers with up-to 4 decimal places and no trailing zeroes
that's why the funky code:
$t = sprintf("%01.4f",$matches[1]); // forces 4 decimal places on number
$t = (float)$t; // trims trailing zeroes by casting to float type.
is used to enforce those API limits.
*/
if (preg_match('|textField1=|i', $fileName)) {
$newlatlong = '';
preg_match('|textField1=([\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= $t;
}
preg_match('|textField2=([-\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= ",$t";
}
$newurl = APIURL . '/points/' . $newlatlong . '/forecast';
$metaURL = APIURL . '/points/' . $newlatlong;
$pointURL = FCSTURL . '/point/' . $newlatlong;
$zoneURL = FCSTURL . '/zone/' . $NOAAZone;
// $alertURL = ALERTAPIURL.$NOAAZone;
$alertURL = ALERTAPIURL . $newlatlong;
return (array(
$newurl,
$pointURL
));
}
/*
// from:
http://forecast.weather.gov/MapClick.php?lat=38.36818&lon=-75.5976&unit=0&lg=english&FcstType=text&TextType=2
// to:
https://api.weather.gov/points/38.36818,-75.5976/forecast
*/
if (preg_match('|lat=|i', $fileName)) {
$newlatlong = '';
preg_match('|lat=([\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= $t;
}
preg_match('|lon=([-\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= ",$t";
}
$newurl = APIURL . '/points/' . $newlatlong . '/forecast';
$metaURL = APIURL . '/points/' . $newlatlong;
$pointURL = FCSTURL . '/point/' . $newlatlong;
$zoneURL = FCSTURL . '/zone/' . $NOAAZone;
// $alertURL = ALERTAPIURL.$NOAAZone;
$alertURL = ALERTAPIURL . $newlatlong;
return (array(
$newurl,
$pointURL
));
}
// handle NEW format of point URL
if (preg_match('|/point/([\d\.]+),([\-\d\.]+)|i', $fileName, $matches)) {
$newlatlong = '';
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= $t;
}
if (isset($matches[2])) {
$t = sprintf("%01.4f", $matches[2]);
$t = (float)$t;
$newlatlong.= ",$t";
}
$newurl = APIURL . '/points/' . $newlatlong . '/forecast';
$metaURL = APIURL . '/points/' . $newlatlong;
$pointURL = FCSTURL . '/point/' . $newlatlong;
$zoneURL = FCSTURL . '/zone/' . $NOAAZone;
// $alertURL = ALERTAPIURL.$NOAAZone;
$alertURL = ALERTAPIURL . $newlatlong;
return (array(
$newurl,
$pointURL
));
}
return (array(
'unk',
$inURL
));
}
// ------------------------------------------------------------------------------------------
function sec2hmsADV($sec, $padHours = false)
{
$hms = "";
if (!is_numeric($sec)) {
return ($sec);
}
// there are 3600 seconds in an hour, so if we
// divide total seconds by 3600 and throw away
// the remainder, we've got the number of hours
$hours = intval(intval($sec) / 3600);
// add to $hms, with a leading 0 if asked for
$hms.= ($padHours) ? str_pad($hours, 2, "0", STR_PAD_LEFT) . ':' : $hours . ':';
// dividing the total seconds by 60 will give us
// the number of minutes, but we're interested in
// minutes past the hour: to get that, we need to
// divide by 60 again and keep the remainder
$minutes = intval(fmod($sec / 60, 60));
// then add to $hms (with a leading 0 if needed)
$hms.= str_pad($minutes, 2, "0", STR_PAD_LEFT) . ':';
// seconds are simple - just divide the total
// seconds by 60 and keep the remainder
$seconds = intval(fmod($sec , 60));
// add to $hms, again with a leading 0 if needed
$hms.= str_pad($seconds, 2, "0", STR_PAD_LEFT);
// done!
return $hms;
} // end sec2hmsADV function
// ------------------------------------------------------------------------------------------
function gen_new_settings()
{
global $fileName, $NOAAZone, $NWSforecasts, $SITE, $Version, $showTwoIconRows, $showZoneWarning;
list($newAPI, $newFileName) = convert_filename($fileName, $NOAAZone);
$newNWSforecasts = array();
if (!isset($NWSforecasts)) {
$NWSforecasts = array();
$newZone = $NOAAZone;
}
foreach($NWSforecasts as $m => $rec) { // for each locations
list($Nzone, $Nlocation, $Nname) = explode('|', $NWSforecasts[$m] . '|||');
list($newAPI, $newName) = convert_filename($Nname, $Nzone);
if ($m == 0) {
$newFileName = $newName;
$newZone = $Nzone;
}
$newNWSforecasts[$m] = "$Nzone|$Nlocation|$newName";
}
header('Content-type: text/plain,charset=ISO-8859-1');
print "// $Version
";
print "// settings converted to new point-forecast request URLs.
";
print "// Put these in ";
print isset($SITE['noaazone']) ? 'Settings.php' : 'advforecast2.php';
print " to update the settings there.
";
print "
";
print "// -- start of converted settings for advforecast2.php JSON version.
";
print isset($SITE['NWSforecasts']) ? "\$SITE['NWSforecasts']" : "\$NWSforecasts";
print " = array(
";
print "// the entries below are for testing use.. replace them with your own entries if using the script
";
print "// outside the Saratoga AJAX/PHP templates.
";
print "// ZONE|Location|point-forecast-URL (separated by | characters
";
foreach($newNWSforecasts as $m => $rec) {
print " '$rec',
";
}
print "
);
";
print "//
";
print isset($SITE['noaazone']) ? "\$SITE['noaazone']" : "\$NOAAZone";
print " = '$newZone'; // change this line to your NOAA warning zone.
";
print "//
";
print "// set ";
print isset($SITE['fcsturlNWS']) ? "\$SITE['fcsturlNWS']" : "\$fileName";
print " to the URL for the point-printable forecast for your area
";
print "// NOTE: this value (and ";
print isset($SITE['noaazone']) ? "\$SITE['noaazone']" : "\$NOAAZone";
print ") will be overridden by the first entry in ";
print isset($SITE['NWSforecasts']) ? "\$SITE['NWSforecasts']" : "\$NWSforecasts";
print " if it exists.
";
print "//
";
print isset($SITE['fcsturlNWS']) ? "\$SITE['fcsturlNWS']" : "\$fileName";
print " = '$newFileName';
";
print "//
";
print isset($SITE['noaazone']) ? "\$SITE['showTwoIconRows']" : "\$showTwoIconRows";
print " = ";
print $showTwoIconRows ? 'true;' : 'false;';
print " // =true; show all icons, =false; show 9 icons in one row (new V5.00)
";
print "//
";
print isset($SITE['noaazone']) ? "\$SITE['showZoneWarning']" : "\$showZoneWarning";
print " = ";
print $showZoneWarning ? 'true;' : 'false;';
print " // =true; show note when Zone forecast used. =false; suppress Zone warning (new V5.00)
";
print "//
";
print "// -- end of converted settings for advforecast2.php JSON version.
";
} // end gen_new_settings function
// end of advforecast2.php script ?>
Did this file decode correctly?
Original Code
<?php
// error_reporting(E_ALL);
// This is a more advanced version of the forecast script
// It uses file caching and feed failure to better handle when NOAA is down
//
// Version 2.00 - 15-Jan-2007 - modified Tom's script for XHTML 1.0-Strict
// Version 2.01 - 14-Feb-2007 - modified for ERH->CRH.noaa.gov redirects (no point forecasts)
// Version 2.02 - 02-Mar-2007 - added auto-failover to CRH and better include in page.
// Version 2.03 - 29-Apr-2007 - modified for /images/wtf -> /forecast/images change
// Version 2.04 - 05-Jun-2007 - improvement to auto-failover
// Version 2.05 - 29-Jun-2007 - additional check for alternative no-icon forecast, then failover.
// Version 2.06 - 24-Nov-2007 - rewrite for zone forecast, intrepret icons from text-only forecast
// Version 2.07 - 24-Nov-2007 - support new zone forecast with different temp formats
// Version 2.08 - 25-Nov-2007 - fix zone forecast icons, new temp formats supported
// Version 2.09 - 26-Nov-2007 - add support for new temperature phrases and below zero temps
// Version 2.10 - 17-Dec-2007 - added safety features from Mike Challis http://www.carmosaic.com/weather/
// Version 2.11 - 20-Dec-2007 - added cache-refresh on request, fixed rising/falling temp arrow
// Version 2.12 - 31-Dec-2007 - fixed New Year"s to New Year's display problem
// Version 2.13 - 01-Jan-2008 - added integration features for carterlake/WD/PHP/AJAX template set
// Version 2.14 - 14-Jan-2009 - corrected Zone forecast parsing for below zero temperatures
// Version 2.15 - 28-Feb-2010 - updated Zone forecast parsing for new phrases
// Version 2.16 - 14-Dec-2010 - added support for warning messages from NWS and $forecastwarnings string
// Version 2.17 - 08-Jan-2011 - fixed validation issue when NWS uses '&' in condition
// Version 2.18 - 27-Feb-2011 - added support for common cache directory
// Version 3.00 - 12-Mar-2011 - support for multi-forecast added by Curly at http://www.ricksturf.com/
// Version 3.01 - 01-Oct-2011 - added support for alternative animated icon set from http://www.meteotreviglio.com/
// Version 3.02 - 05-Oct-2011 - corrected warning links to forecast.weather.gov
// Version 3.03 - 02-Jul-2012 - added fixes for NWS website changes
// Version 3.04 - 03-Jul-2012 - added fixes for W3C validation issues
// Version 3.05 - 05-Jul-2012 - added fixes for Zone forecast use with new NWS website design
// Version 3.06 - 07-Jul-2012 - fixed validation issue for Rising/Falling temp arrows with new NWS website design
// Version 3.07 - 09-Aug-2012 - fixed failover to Zone forecast with new NWS website design
// Version 3.08 - 23-Nov-2012 - fixed issue with Zone forecast parsing due to NWS website changes
// Version 3.09 - 28-Jun-2013 - added fixes for Zone forecast parsing due to NWS website changes
// Version 3.10 - 18-Nov-2013 - fixed issue with Zone forecast URL due to NWS website changes
// Version 3.11 - 13-Mar-2014 - fixed point forecast text non-display due to NWS website changes
// Version 3.12 - 15-Mar-2014 - fixes for Zone forecast, warnings and auto-correct old URLs
// Version 3.13 - 17-Mar-2015 - fixes for Zone forecast w/new NWS site design
// Version 3.14 - 31-Mar-2015 - fixes for NWS site changes
// Version 3.15 - 13-May-2015 - fix for Zone forecast w/NWS website change
// Version 3.16 - 15-May-2015 - fixes for different Zone forecast format in failover
// Version 4.00 - 06-Jul-2015 - added support for DualImage processing
// Version 4.01 - 07-Jul-2015 - fixed HTML for $forecasticons for new NWS website
// Version 4.02 - 07-Nov-2015 - fixed Zone forecast when using .gif icons issue
//
// Version 5.00 - 22-Apr-2017 - complete rewrite to use api.weather.gov JSON feeds for forecasts and alerts
// Version 5.01 - 26-Apr-2017 - switch to use test NWS site for data waiting for June 19, 2017 prod cutover
// Version 5.02 - 22-May-2017 - added temperature trend indicator
// Version 5.03 - 16-Jul-2017 - if point forecast HTTP return code > 400, force zone forecast fetch
// Version 5.04 - 11-Oct-2017 - fix for stale point-forecast via API to failover to Zone forecast
// Version 5.05 - 27-Feb-2018 - more fixes for point-forecast/refetch fail->Zone failover
// Version 5.06 - 12-Apr-2018 - fix PHP warning when no alerts available
// Version 5.07 - 14-Apr-2018 - add caching for point->gridpoint forecast URLs + WFO info on Zone fcst
// Version 5.08 - 25-May-2018 - fixes for point/zone JSON changes from api.weather.gov
// Version 5.09 - 26-May-2018 - added new NWS API icons for tropical storm/hurricane
// Version 5.10 - 13-Apr-2019 - fix for HTTP/2 responses from api.weather.gov
// Version 5.11 - 30-Apr-2019 - use new point->meta->gridpoint method for point forecast URL data
// Version 5.12 - 01-May-2019 - fix for link URL at bottom of page
// Version 5.13 - 08-Sep-2019 - fix for link URL with NWS discontinuation of forecast-v3.weather.gov site
// Version 5.14 - 10-May-2020 - fix for Alert link URL for alerts-v2.weather.gov (Thanks Jasiu!)
// Version 5.15 - 25-Jun-2020 - updated diagnostic info to help diagnose Akamai cache issues from api.weather.gov site
// Version 5.16 - 08-Jul-2020 - added diagnostic logging capability ($doLogging = true; to enable);
// Version 5.17 - 15-Jul-2020 - switch to geo+json queries from ld+json queries for api.weather.gov; cache file name changes too.
// Version 5.18 - 18-Jan-2022 - added fix for PHP8.1 Deprecated errata
// Version 5.18a - 07-Feb-2022 - use 'updateTime' instead of deprecated 'updated' for age of gridpoint forecast
// Version 5.19 - 27-Dec-2022 - fixes for PHP 8.2
//
$Version = 'advforecast2.php (JSON) - V5.19 - 27-Dec-2022';
//
// import NOAA Forecast info
// data ends up in four different arrays:
// $forecasticons[x] x = 0 thru 13 This is the icon and text around it
// $forecasttemp[x] x= 0 thru 13 This is forecast temperature with styling
// $forecasttitles[x] x = 0 thru 13 This is the title word for the text forecast time period
// $forecasttext[x] x = 0 thru 13 This is the detail text for the text forecast time period
//
// $forecastupdated This is the time of last update
// $forecastcity This is the city name for the forecast
// $forecastoffice This is the NWS Office providing the forecast
// $forecastwarnings This is the text/links to NWS Warnings, Watches, Advisories, Outlooks, Special Statements
//
// Also, in order for this to work correctly, you need the NOAA icons (or make your own...
// there are 750!).
//
// http://saratoga-weather.org/saratoga-icons2.zip
// unzip the above and upload to your site (preserving the directory structure):
//
// ./forecast/images (for the static images)
// ./forecast/icon-templates (for the dual-image icon master files)
// ./DualImage.php (script to create the dual-image icons as required by the forecast)
//
// The URL(s) below --MUST BE-- the Printable Point Forecast from the NOAA website
// Not every area of the US has a printable point forecast
//
// This script will ONLY WORK with a printable point forecast of either the OLD format like:
// http://forecast.weather.gov/MapClick.php?
// CityName=Saratoga&state=CA&site=MTR&textField1=37.2639&textField2=-122.022
// &e=1&TextType=2
//
// or the new format (after June 24, 2019 ) of
// https://forecast.weather.gov/point/37.2639,-122.022
//
// To find yours in your area:
//
// Go to www.weather.gov
// Put your city, state in the search box and press Search
// copy the URL from your browser into the $fileName variable below.
// Also put your NOAA Warning Zone (like ssZnnn) in caps in the $NOAAZone variable below.
// It is used for automatic backup in case the point printable forecast is not available.
//
// ----------------------SETTINGS---------------------------------------------
// V3.00 -- this following array can be used for multiple forecasts in *standalone* mode
// for Saratpga template use, add a $SITE['NWSforecasts'] entry in Settings.php to have these entries.
// To activate the definitions below, replace the /* with //* to uncomment the array definition
/*
$NWSforecasts = array(
// the entries below are for testing use.. replace them with your own entries if using the script
// outside the AJAX/PHP templates.
// ZONE|Location|point-forecast-URL (separated by | characters
"CAZ513|Saratoga, CA (WRH)|http://forecast.weather.gov/MapClick.php?CityName=Saratoga&state=CA&site=MTR&textField1=37.2639&textField2=-122.022&e=1&TextType=2",
"NEZ052|Omaha, NE (CRH)|http://forecast.weather.gov/MapClick.php?lat=41.2586&lon=-95.9378&unit=0&lg=english&FcstType=text&TextType=2",
"ALZ266|Gulf Shores, AL (SRH)|http://forecast.weather.gov/MapClick.php?CityName=Gulf+Shores&state=AL&site=MOB&textField1=30.27&textField2=-87.7015&e=0&TextType=2",
'MDZ022|Salisbury, MD (ERH)|http://forecast.weather.gov/MapClick.php?lat=38.36818&lon=-75.59761&unit=0&lg=english&FcstType=text&TextType=2',
'AKZ101|Anchorage, AK (ARH)|http://forecast.weather.gov/MapClick.php?lat=61.21806&lon=-149.90027780000003&unit=0&lg=english&FcstType=text&TextType=2',
'HIZ005|Honolulu, HI (HRH)|http://forecast.weather.gov/MapClick.php?lat=21.30694&lon=-157.85833330000003&unit=0&lg=english&FcstType=text&TextType=2',
'IAZ068|Riverdale, IA|http://forecast.weather.gov/MapClick.php?lat=41.5354&lon=-90.4671&unit=0&lg=english&FcstType=text&TextType=2',
'MEZ030|Bar Harbor, ME|http://forecast.weather.gov/MapClick.php?lat=44.76&lon=-67.5477&unit=0&lg=english&FcstType=text&TextType=2',
'TXZ147|Fairfield, TX|http://forecast.weather.gov/MapClick.php?&lat=31.7188&lon=-96.1655&lg=english&FcstType=text&TextType=2',
'SDZ021|Millbank, SD|http://forecast.weather.gov/MapClick.php?lat=45.194&lon=-96.6869&unit=0&lg=english&FcstType=text&TextType=2',
'MNZ034|Brainerd, MN|http://forecast.weather.gov/MapClick.php?lat=46.3544&lon=-94.1941&unit=0&lg=english&FcstType=text&TextType=2',
'COZ010|Vail, CO|http://forecast.weather.gov/MapClick.php?lat=39.5864&lon=-106.3822&unit=0&lg=english&FcstType=text&TextType=2',
'CAZ072|South Lake Tahoe, CA|http://forecast.weather.gov/MapClick.php?CityName=Tahoe Vista&state=CA&site=MTR&textField1=39.2425194&textField2=-120.0604858&e=1&TextType=2',
'WAZ037|Colville, WA|http://forecast.weather.gov/MapClick.php?lat=48.5433&lon=-117.8951&unit=0&lg=english&FcstType=text&TextType=2',
'ILZ103|Hoffman Estates, IL|http://forecast.weather.gov/MapClick.php?lat=42.03921&lon=-88.11001&unit=0&lg=english&FcstType=text&TextType=2',
'NDZ027|Grand Forks, ND|http://forecast.weather.gov/MapClick.php?lat=47.9169&lon=-97.072&unit=0&lg=english&FcstType=text&TextType=2',
'MTZ055|Bozeman, MT|http://forecast.weather.gov/MapClick.php?lat=45.6354&lon=-111.0633&unit=0&lg=english&FcstType=text&TextType=2',
"AZZ544|Phoenix|http://forecast.weather.gov/MapClick.php?CityName=Phoenix&state=AZ&site=PSR&textField1=33.646&textField2=-112.007&e=0&TextType=2",
'KSZ078|Dodge City, KS|http://forecast.weather.gov/MapClick.php?lat=37.7528&lon=-100.0171&unit=0&lg=english&FcstType=text&TextType=2',
'OKZ020|Stillwater, OK|http://forecast.weather.gov/MapClick.php?lat=36.1156&lon=-97.0584&unit=0&lg=english&FcstType=text&TextType=2',
'GAZ034|Lawrenceville, GA|http://forecast.weather.gov/MapClick.php?CityName=Lawrenceville&state=GA&site=FFC&lat=33.9495&lon=-83.9922&TextType=2',
'ARZ011|Rockhouse, AR|http://forecast.weather.gov/MapClick.php?lat=36.289&lon=-93.7258&unit=0&lg=english&FcstType=text&TextType=2',
'ARZ040|Mena, AR|http://forecast.weather.gov/MapClick.php?lat=34.6036&lon=-94.2631&unit=0&lg=english&FcstType=text&TextType=2',
'MOZ090|Springfield, MO|http://forecast.weather.gov/MapClick.php?lat=37.1962&lon=-93.2861&unit=0&lg=english&FcstType=text&TextType=2',
'MTZ019|Plentywood, MT|http://forecast.weather.gov/MapClick.php?lat=48.7747&lon=-104.5625&unit=0&lg=english&FcstType=text&TextType=2',
'ARZ044|Little Rock, AR|https://forecast.weather.gov/MapClick.php?lat=34.7224&lon=-92.3541&unit=0&lg=english&FcstType=text&TextType=2',
"NYZ040|Hessville, NY|https://forecast.weather.gov/MapClick.php?lat=42.8739&lon=-74.687&unit=0&lg=english&FcstType=text&TextType=2",
"NYZ049|Albany, NY|http://forecast.weather.gov/MapClick.php?CityName=Albany&state=NY&site=MTR&textField1=42.6525&textField2=-73.757&e=0&TextType=2",
"NYZ056|Binghamton, NY|http://forecast.weather.gov/MapClick.php?CityName=Binghamton&state=NY&site=MTR&textField1=42.0956&textField2=-75.910&e=0&TextType=2",
"MAZ017|Boston, MA|http://forecast.weather.gov/MapClick.php?CityName=Boston&state=MA&site=MTR&textField1=42.3586&textField2=-71.060&e=0&TextType=2",
"MNZ060|Robbinsdale, MN|http://forecast.weather.gov/MapClick.php?lat=45.02233988655459&lon=-93.34722518920898&site=mpx&unit=0&lg=en&FcstType=text&TextType=2",
"FLZ112|Upper Grand Lagoon, FL|http://forecast.weather.gov/MapClick.php?lat=30.1981&lon=-85.8077&unit=0&lg=english&FcstType=text&TextType=2",
"MSZ066|Ellisville, MS|https://forecast.weather.gov/MapClick.php?lat=31.62265&lon=-89.23384&unit=0&lg=english&FcstType=text&TextType=2",
"NEZ066|Lincoln, NE|https://forecast.weather.gov/MapClick.php?CityName=Lincoln&state=NE&site=OAX&textField1=40.8164&textField2=-96.6882&e=0&TextType=2",
"IAZ066|Clinton, IA|https://forecast-v3.weather.gov/point/41.839,-90.192",
'ALZ266|Gulf Shores, AL|https://forecast.weather.gov/MapClick.php?lat=30.2775&lon=-87.6831&unit=0&lg=english&FcstType=text&TextType=2',
'KSZ050|3 Miles SE Lyons KS|https://forecast.weather.gov/MapClick.php?lat=38.3134&lon=-98.1617&unit=0&lg=english&FcstType=text&TextType=2',
'ILZ029|Glasford IL|https://forecast.weather.gov/MapClick.php?lat=40.5723&lon=-89.8132&unit=0&lg=english&FcstType=text&TextType=2',
);
//*/
//*
//
$NOAAZone = 'CAZ513'; // change this line to your NOAA warning zone.
// set $fileName to the URL for the point-printable forecast for your area
// NOTE: this value (and $NOAAZone) will be overridden by the first entry in $NWSforecasts if it exists.
$fileName = "http://forecast.weather.gov/MapClick.php?CityName=Saratoga&state=CA&site=MTR&textField1=37.2639&textField2=-122.022&e=1&TextType=2";
//*/
$showTwoIconRows = true; // =true; show all icons, =false; show 9 icons in one row (new V5.00)
//
$showZoneWarning = true; // =true; show note when Zone forecast used. =false; suppress Zone warning (new V5.00)
// $iconDir = './forecast/imagesPNG-86x86/'; // testing only
// $iconDir = './forecast/imagesGIF-55x58/'; // testing only
$iconDir = './forecast/images/';
$iconType = '.jpg'; // default type='.jpg' -- use '.gif' for animated icons from http://www.meteotreviglio.com/
$cacheFileDir = './'; // default cache file directory
$iconHeight = 55; // default height of conditions icon (saratoga-icons.zip)
$iconWidth = 55; // default width of conditions icon (saratoga-icons.zip)
$refreshTime = 600; // default refresh of cache 600=10 minutes
//
$ourTZ = 'America/Los_Angeles'; // default timezone (new V5.00)
// $timeFormat = 'd-M-Y g:ia T'; // Fri, 31-Mar-2006 6:35pm TZone (new V5.00)
$timeFormat = 'g:i a T M d, Y'; // 3:17 am PST Jan 28, 2017 (new V5.00, like old forecast timestamp display)
# V5.16 logging capability for curl accesses to api.weather.gov
$doLogging = true; // =true to enable summary logging, =false for no summary log file
# log saved to $cacheFileDir/advforecast2-log-YYYY-MM-DD.csv (tab-delimited CSV file)
#
$doLoggingDetail = true; // =true to eanable detail logging, =false for no detail file
# log saved to $cacheFileDir/advforecast2-log-YYYY-MM-DD.txt
//
// ----------------------END OF SETTINGS--------------------------------------
// changing stuff below this may cause you issues when updates to the script are done.
// you change it, you own it :)
//
$useProdNWS = false; // =false, use preview V3 sites, =true, use production sites (after June 24, 2019)
//
$forceDualIconURL = false; // for TESTING prior to 7-Jul-2015 when new icons were used by NWS
// following is for possible FUTURE use
$getGridpointData = false; // =true; for getting gridpoint data, =false for not getting the data
//
// following is for possible FUTURE hourly forecast/graphics use
$getHourlyData = false; // =true; for getting hourly data, =false for not getting the data
if(file_exists('Settings.php')) { include_once('Settings.php'); }
// overrides from Settings.php if available
global $SITE;
if (isset($SITE['NWSforecasts'])) {$NWSforecasts = $SITE['NWSforecasts']; }
if (isset($SITE['cacheFileDir'])) {$cacheFileDir = $SITE['cacheFileDir']; }
if (isset($SITE['noaazone'])) {$NOAAZone = $SITE['noaazone'];}
if (isset($SITE['fcsturlNWS'])) {$fileName = $SITE['fcsturlNWS'];}
if (isset($SITE['fcsticonsdir'])) {$iconDir = $SITE['fcsticonsdir'];}
if (isset($SITE['fcsticonstype'])) {$iconType = $SITE['fcsticonstype'];}
if (isset($SITE['fcsticonsheight'])) {$iconHeight = $SITE['fcsticonsheight'];}
if (isset($SITE['fcsticonswidth'])) {$iconWidth = $SITE['fcsticonswidth'];}
if (isset($SITE['tz'])) {$ourTZ = $SITE['tz'];} // V5.00
if (isset($SITE['timeFormat'])) {$timeFormat = $SITE['timeFormat'];} // V5.00
if (isset($SITE['showTwoIconRows'])) {$showTwoIconRows = $SITE['showTwoIconRows'];} // V5.00
if (isset($SITE['showZoneWarning'])) {$showZoneWarning = $SITE['showZoneWarning'];} // V5.00
if (isset($SITE['advLogging'])) {$doLogging = $SITE['advLogging'];} // V5.16
if (isset($SITE['advLoggingDetail'])) {$doLoggingDetail = $SITE['advLoggingDetail'];} // V5.16
// end of overrides from Settings.php
$doDebug = (isset($_REQUEST['debug']) and preg_match('|y|i',$_REQUEST['debug']))?true:false;
if (isset($_REQUEST['rows'])) {
if ($_REQUEST['rows'] == '1') {
$showTwoIconRows = false;
}
if ($_REQUEST['rows'] == '2') {
$showTwoIconRows = true;
}
}
// hosts providing API and Forecasts and Alerts
if ($useProdNWS) {
// final production URLs
define('APIURL', "https://api.weather.gov");
define('FCSTURL', "https://forecast.weather.gov");
define('ALERTAPIURL', 'https://api.weather.gov/alerts?active=1&point=');
define('ALERTURL', 'https://alerts.weather.gov/products/');
}
else {
// pre-production/testing URLs
define('APIURL', "https://api.weather.gov");
define('FCSTURL', "https://forecast-v3.weather.gov");
define('ALERTAPIURL','https://api.weather.gov/alerts?active=1&point=');
define('ALERTURL', 'https://alerts-v2.weather.gov/#/?id='); // Jasiu fix 10-May-2020
}
// get the selected zone code
$haveZone = '0';
if (!empty($_GET['z']) && preg_match("/^[0-9]+$/i", htmlspecialchars($_GET['z']))) {
$haveZone = htmlspecialchars(strip_tags($_GET['z'])); // valid zone syntax from input
}
$DualImageAvailable = file_exists("./DualImage.php") ? true : false;
// $DualImageAvailable = false;
if (isset($_REQUEST['convert'])) { // display new URLs if requested
gen_new_settings();
exit;
}
if (!isset($NWSforecasts[0])) {
// print "<!-- making NWSforecasts array default -->\n";
$NWSforecasts = array(
"$NOAAZone||$fileName"
); // create default entry
}
// print "<!-- NWSforecasts\n".print_r($NWSforecasts,true). " -->\n";
// Set the default zone. The first entry in the $SITE['NWSforecasts'] array.
list($Nz, $Nl, $Nn) = explode('|', $NWSforecasts[0] . '|||');
$NOAAZone = $Nz;
$NOAAlocation = $Nl;
$fileName = $Nn;
$newFormat = false;
if (!isset($NWSforecasts[$haveZone])) {
$haveZone = 0;
}
// locations added to the drop down menu and set selected zone values
$dDownMenu = '';
for ($m = 0; $m < count($NWSforecasts); $m++) { // for each locations
list($Nzone, $Nlocation, $Nname) = explode('|', $NWSforecasts[$m] . '|||');
$dDownMenu.= " <option value=\"" . $m . "\">" . $Nlocation . "</option>\n";
if ($haveZone == $m) {
$NOAAZone = $Nzone;
$NOAAlocation = $Nlocation;
$fileName = $Nname;
}
}
// build the drop down menu
$ddMenu = '';
// create menu if at least two locations are listed in the array
if (isset($NWSforecasts[0]) and isset($NWSforecasts[1])) {
$ddMenu.= '<tr align="center">
<td style="font-size: 14px; font-family: Arial, Helvetica, sans-serif">
<script type="text/javascript">
<!--
function menu_goto( menuform ){
selecteditem = menuform.logfile.selectedIndex ;
logfile = menuform.logfile.options[ selecteditem ].value ;
if (logfile.length != 0) {
location.href = logfile ;
}
}
// -->
</script>
<form action="" method="get">
<p><select name="z" onchange="this.form.submit()">
<option value=""> - Select Forecast - </option>
' . $dDownMenu . $ddMenu . ' </select></p>
<div><noscript><pre><input name="submit" type="submit" value="Get Forecast" /></pre></noscript></div>
</form>
</td>
</tr>
';
}
// You can now force the cache to update by adding ?force=1 to the end of the URL
if (empty($_REQUEST['force'])) $_REQUEST['force'] = "0";
$Force = $_REQUEST['force'];
$forceBackup = false;
if ($Force > 1) {
$forceBackup = true;
}
$cacheName = $cacheFileDir . "forecast-" . $NOAAZone . "-$haveZone-geojson.txt";
// dont change the $backupfileName!
// new Zone URL with V5.00:
$Status = "<!-- $Version on PHP " . phpversion() . "-->\n<!-- RAW NWS URL: $fileName zone=$NOAAZone -->\n";
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,charset=ISO-8859-1");
header("Accept-Ranges: bytes");
header("Content-Length: $download_size");
header('Connection: close');
readfile($filenameReal);
exit;
}
global $fcstPeriods, $NWSICONLIST;
load_lookups();
$usingFile = "";
if (!function_exists('date_default_timezone_set')) {
if (!ini_get('safe_mode')) {
putenv("TZ=$ourTZ"); // set our timezone for 'as of' date on file
}
}
else {
date_default_timezone_set($ourTZ);
}
if (version_compare(PHP_VERSION, '5.3', '<')) {
print "<p>This script requires PHP V5.3+ to operate. You are running PHP " . PHP_VERSION . "<br/>";
print "Please update PHP to be able to run $Version script</p>\n";
$forecasttitles = array();
$forecasttext = array();
$forecasticons = array();
$forecasttemp = array();
print $Status;
exit;
}
// grab the meta info for point, zone and forecast office + gridpoint URLs to use
if (strpos($iconType, 'gif') !== false and $DualImageAvailable) {
$DualImageAvailable = false;
$Status.= "<!-- DualImage function is not available with $iconType icons; -->\n";
$Status.= "<!-- first image of any dual-image found will be displayed instead -->\n";
}
$oldFileName = $fileName;
$backupfileName = APIURL . "/zones/forecast/$NOAAZone/forecast";
list($fileName, $pointURL) = convert_filename($fileName, $NOAAZone); // correct the filename if necessary to API format
$META = array();
$META = get_meta_info($cacheName, $fileName, $backupfileName);
if (isset($META['forecastZone']) and $META['forecastZone'] !== $NOAAZone) {
$Status.= "<!-- WARNING: NOAAZone='$NOAAZone' is not correct. Will use '" . $META['forecastZone'] . "' for this point location per NWS. -->\n";
$NOAAZone = $META['forecastZone'];
}
// V5.11 - use the META data for the real gridpoint forecast and zone forecast URLs.
$pointURL = $META['forecastURL'];
$fileName = $META['forecastURL'];
$backupfileName = APIURL . "/zones/forecast/".$META['forecastZone']."/forecast";
if($fileName == '') {
$Status .= "<!-- pointURL is null. Using Zone URL instead -->\n";
$fileName = $backupfileName;
}
$Status .= "<!-- point forecastURL = '$pointURL' -->\n";
$Status .= "<!-- zone forecastURL = '$backupfileName' -->\n";
$html = '';
$lastURL = '';
if ($Force == 1 or
!file_exists($cacheName) or
(file_exists($cacheName) and filemtime($cacheName) + $refreshTime < time())) {
$html = ADV_fetchUrlWithoutHanging($fileName);
$stuff = explode("\r\n\r\n",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
preg_match('/HTTP\/\S+ (\d+)/', $headers, $m);
//$Status .= "<!-- m=".print_r($m,true)." -->\n";
//$Status .= "<!-- html=".print_r($html,true)." -->\n";
if(isset($m[1])) {$lastRC = (string)$m[1]; } else {$lastRC = '0'; }
if ($lastRC >= '400') {
$Force = 2;
$Status.= "<!-- Oops.. point forecast unavailable RC=" . $lastRC . " - using Zone instead -->\n";
} else {
$lastURL = $fileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded point-forecast $fileName - $fSize bytes -->\n";
if (strpos($content, '{') !== false and $lastRC == '200') { // got a file.. save it
$fp = fopen($cacheName, "w");
if ($fp) {
$write = fputs($fp, $html);
fclose($fp);
$Status.= "<!-- wrote cache file $cacheName -->\n";
} else {
$Status.= "<!-- unable to write cache file $cacheName -->\n";
}
}
}
}
if ($Force == 2) {
$usingFile = "(Zone forecast)";
$html = ADV_fetchUrlWithoutHanging($backupfileName);
$lastURL = $backupfileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded $usingFile $backupfileName - $fSize bytes -->\n";
$stuff = explode("\r\n\r\n",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
preg_match('/HTTP\/\S+ (\d+)/', $headers, $m);
//$Status .= "<!-- m=".print_r($m,true)." -->\n";
//$Status .= "<!-- html=".print_r($html,true)." -->\n";
if(isset($m[1])) {$lastRC = (string)$m[1];} else {$lastRC = '0';}
if (strpos($html, '{') !== false and $lastRC == '200') { // got a file.. save it
$fp = fopen($cacheName, "w");
if ($fp) {
$write = fputs($fp, $html);
fclose($fp);
$Status.= "<!-- wrote cache file $cacheName -->\n";
}
else {
$Status.= "<!-- unable to write cache file $cacheName -->\n";
}
}
}
if (strlen($html) < 50 and file_exists($cacheName)) { // haven't loaded it by fetch.. load from cache
$html = file_get_contents($cacheName);
$fSize = strlen($html);
$Status.= "<!-- loaded cache file $cacheName - $fSize bytes -->\n";
$stuff = explode("\r\n\r\n",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if (preg_match('/Temporary|Location:|defaulting to|window\.location\.href\=/Uis', $headers)) {
$usingFile = "(Zone forecast)";
$html = ADV_fetchUrlWithoutHanging($backupfileName);
$lastURL = $backupfileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded $usingFile $backupfileName - $fSize bytes -->\n";
}
}
if ($Force != 2) {
// check point-forecast age and load zone forecast if too old
preg_match('!"updateTime":\s*"([^"]+)"!is', $html, $matches);
if (isset($matches[1])) {
$age = time() - strtotime($matches[1]);
$ts = $matches[1];
if ($age > 18 * 60 * 60) {
$agehms = sec2hmsADV($age);
$Status.= "<!-- point forecast more than 18hrs old (age h:m:s is $agehms) updated:'$ts' .. use Zone forecast instead -->\n";
list($headers,$content) = explode("\r\n\r\n",$html);
$Status .= "<!-- headers from the gridpoint request\n".$headers."\n -->\n";
ADV_log('Stale',"Forecast Stale Age=$agehms from gridpoint - use Zone forecast instead\n".$curlStatus."\n".$headers,"\n");
$Force = 2;
$usingFile = "(Zone forecast)";
$html = ADV_fetchUrlWithoutHanging($backupfileName);
$lastURL = $backupfileName; // remember if error encountered
$fSize = strlen($html);
$Status.= "<!-- loaded $usingFile $backupfileName - $fSize bytes -->\n";
if (strpos($html, '{') !== false) { // got a file.. save it
$fp = fopen($cacheName, "w");
if ($fp) {
$write = fputs($fp, $html);
fclose($fp);
$Status.= "<!-- wrote cache file $cacheName -->\n";
}
else {
$Status.= "<!-- unable to write cache file $cacheName -->\n";
}
}
}
}
}
// now split off the headers from the contents of the return
$stuff = explode("\r\n\r\n",$html); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
// check age of forecast
preg_match('!"updateTime":\s*"([^"]+)"!is', $html, $matches);
if(!isset($matches[1])) {
preg_match('!"updated":\s*"([^"]+)"!is', $html, $matches);
}
if (isset($matches[1])) {
$age = time() - strtotime($matches[1]);
$ts = $matches[1];
if ($age > 18 * 60 * 60) {
$agehms = sec2hmsADV($age);
$Status.= "<!-- forecast more than 18hrs old (age h:m:s is $agehms) updated:'$ts' -->\n";
list($headers,$content) = explode("\r\n\r\n",$html);
$Status .= "<!-- headers from the $usingFile request\n".$headers."\n -->\n";
}
}
$rawJSON = json_decode($content, true); // parse the JSON into an associative array
$FCSTJSON = $rawJSON['properties']; // geoJSON format
if (strlen($content > 10) and function_exists('json_last_error')) { // report status, php >= 5.3.0 only
switch (json_last_error()) {
case JSON_ERROR_NONE:
$JSONerror = '- No errors';
break;
case JSON_ERROR_DEPTH:
$JSONerror = '- Maximum stack depth exceeded';
break;
case JSON_ERROR_STATE_MISMATCH:
$JSONerror = '- Underflow or the modes mismatch';
break;
case JSON_ERROR_CTRL_CHAR:
$JSONerror = '- Unexpected control character found';
break;
case JSON_ERROR_SYNTAX:
$JSONerror = '- Syntax error, malformed JSON';
break;
case JSON_ERROR_UTF8:
$JSONerror = '- Malformed UTF-8 characters, possibly incorrectly encoded';
break;
default:
$JSONerror = '- Unknown error';
break;
}
$Status.= "<!-- JSON decode $JSONerror -->\n";
if (json_last_error() !== JSON_ERROR_NONE) {
$Status.= "<!-- content='" . print_r($content, true) . "' -->\n";
}
}
/*
<!-- FCSTJSON
Array
{
"periods": [
{
"number": 1,
"name": "Today",
"startTime": "2017-05-01T07:00:00-07:00",
"endTime": "2017-05-01T18:00:00-07:00",
"isDaytime": true,
"temperature": 83,
"temperatureUnit": "F",
"temperatureTrend": null,
"windSpeed": "8 to 17 mph",
"windDirection": "NW",
"icon": "https://api.weather.gov/icons/land/day/skc?size=medium",
"shortForecast": "Sunny",
"detailedForecast": "Sunny, with a high near 83. Northwest wind 8 to 17 mph, with gusts as high as 23 mph."
},
{
"number": 2,
"name": "Tonight",
"startTime": "2017-05-01T18:00:00-07:00",
"endTime": "2017-05-02T06:00:00-07:00",
"isDaytime": false,
"temperature": 53,
"temperatureUnit": "F",
"temperatureTrend": "rising",
"windSpeed": "3 to 17 mph",
"windDirection": "NW",
"icon": "https://api.weather.gov/icons/land/night/skc?size=medium",
"shortForecast": "Clear",
"detailedForecast": "Clear. Low around 53, with temperatures rising to around 55 overnight. Northwest wind 3 to 17 mph, with gusts as high as 23 mph."
},
*/
if ($getGridpointData and isset($META['forecastGridDataURL'])) {
// grab the gridpoint JSON if need be
get_gridpoint_data($cacheName, $META['forecastGridDataURL'], $Force, 3600);
}
if ($getHourlyData and isset($META['forecastHourlyURL'])) {
// grab the Hourly JSON if need be
get_hourly_data($cacheName, $META['forecastHourlyURL'], $Force, 3600);
}
// now process the point or zone forecast
if (isset($FCSTJSON['periods'][0]['icon']) and
strpos($FCSTJSON['periods'][0]['icon'], 'icons') !== false) { // got a point forecast
// -------------- POINT forecast process -----------------
$isZone = false;
$rawForecasts = $FCSTJSON['periods'];
$Status.= "<!-- point forecast processing -->\n\n";
/*
<!-- rawForecasts
Array
(
[0] => Array
(
[number] => 1
[name] => Today
[startTime] => 2016-11-16T08:00:00-08:00
[endTime] => 2016-11-16T18:00:00-08:00
[isDaytime] => 1
[temperature] => 66
[windSpeed] => 15 mph
[windDirection] => NW
[icon] => https://api-v1.weather.gov/icons/land/day/rain_showers,20/sct,20?size=medium
[shortForecast] => Slight Chance Rain Showers then Mostly Sunny
[detailedForecast] => A slight chance of rain showers, mainly before 10am. Mostly sunny, with a high near 66. Northwest wind around 15 mph, with gusts as high as 20 mph. Chance of precipitation is 20%. New rainfall amounts less than a tenth of an inch possible.
)
[1] => Array
(
[number] => 2
[name] => Tonight
[startTime] => 2016-11-16T18:00:00-08:00
[endTime] => 2016-11-17T06:00:00-08:00
[isDaytime] =>
[temperature] => 45
[windSpeed] => 6 to 15 mph
[windDirection] => NW
[icon] => https://api-v1.weather.gov/icons/land/night/sct?size=medium
[shortForecast] => Partly Cloudy
[detailedForecast] => Partly cloudy, with a low around 45. Northwest wind 6 to 15 mph, with gusts as high as 20 mph.
)
*/
$rawUpdated = $FCSTJSON['updateTime'];
if (isset($META['timeZone'])) {
date_default_timezone_set($META['timeZone']);
}
$forecastupdated = date($timeFormat, strtotime($rawUpdated));
$ourPoint = '';
if (preg_match('|/([^/]+)/forecast|i', $fileName, $matches)) {
$ourPoint = $matches[1];
}
$forecastlatlong = $ourPoint;
$forecastcity = $NOAAlocation;
$i = 0;
foreach($rawForecasts as $ptr => $FCST) {
// we'll be setting up:
// $forecasticons[x] x = 0 thru 13 This is the icon and text around it
// $forecasttemp[x] x= 0 thru 13 This is forecast temperature with styling
// $forecasttitles[x] x = 0 thru 13 This is the title word for the text forecast time period
// $forecasttext[x] x = 0 thru 13 This is the detail text for the text forecast time period
// $Status .= "<!-- FCST[$i]\n".print_r($FCST,true)." -->\n";
if (strlen($FCST['name']) < 5) { // sometimes have a left-over entry w/o a name.. skip it
continue;
}
$forecastcond[$i] = $FCST['shortForecast'];
$forecasticon[$i] = convert_to_local_icon($FCST['icon']);
if ($FCST['isDaytime']) {
$color = '#FF0000';
$tHL = 'Hi';
}
else {
$color = '#0000FF';
$tHL = 'Lo';
}
$tTrend = '';
if (!is_null($FCST['temperatureTrend'])) {
if ($FCST['temperatureTrend'] == 'rising') {
$tTrend = ' ↑';
}
if ($FCST['temperatureTrend'] == 'falling') {
$tTrend = ' ↓';
}
}
$forecasttemp[$i] = "$tHL <span style=\"color: $color;\">" . round($FCST['temperature'], 0) . " °F$tTrend</span>";
$forecasttitles[$i] = $FCST['name'];
$forecasttext[$i] = str_replace("\n", ' ', $FCST['detailedForecast']); // sub blank for embedded NL
$forecasticons[$i] = make_local_icon($forecasticon[$i], $forecasttitles[$i], $forecastcond[$i], $forecasttemp[$i], $forecasttext[$i]);
$forecasticons[$i] = preg_replace('/&/', '&', $forecasticons[$i]);
if ($doDebug) {
$Status.= "<!-- i=$i name='" . $forecasttitles[$i] . "' " . "cond='" . $forecastcond[$i] . "' " . "temp='" . $forecasttemp[$i] . "' " . "\nrawicon='" . $FCST['icon'] . "' " . "\ncvticon='" . $forecasticon[$i] . "' " . "\ndetail='" . $forecasttext[$i] . "' " . "\nicon='" . $forecasticons[$i] . "' -->\n\n";
}
$i++;
} // end foreach FCST point processing
}
elseif (isset($FCSTJSON['periods'])) { // got a ZONE forecast
// $Status .= "<!-- FCSTJSON \n".print_r($FCSTJSON,true)." -->\n";
// -------------- ZONE forecast process -----------------
$isZone = true;
$Status.= "<!-- ZONE forecast processing -->\n\n";
$rawForecasts = $FCSTJSON['periods'];
$rawUpdated = $FCSTJSON['updated'];
if (isset($META['timeZone'])) {
date_default_timezone_set($META['timeZone']);
}
$forecastupdated = date($timeFormat, strtotime($rawUpdated));
$forecastlatlong = $NOAAZone;
$forecastcity = $NOAAlocation;
$usingFile = "(Zone forecast)";
$Conditions = array(); // prepare for parsing the icon based on the text forecast
load_cond_data(); // initialize the conditions to look for
/*
<!-- FCSTJSON
Array
(
[periods] => Array
(
[0] => Array
(
[number] => 1
[name] => Today
[detailedForecast] => Partly sunny this morning
)
[1] => Array
(
[number] => 2
[name] => Tonight
[detailedForecast] => Partly cloudy. Lows around 40. North winds 5 to 10 mph
with gusts up to 20 mph.
)
[2] => Array
(
[number] => 3
[name] => Saturday
[detailedForecast] => Partly sunny in the morning
)
[3] => Array
(
[number] => 4
[name] => Saturday Night
[detailedForecast] => Mostly cloudy with a 40 percent chance of
showers. Lows in the lower 40s. Northeast winds 5 to 10 mph
)
*/
// Breakup multi-day forecasts if needed
$i = 0;
// foreach ($headers[1] as $j => $period) {
foreach($FCSTJSON['periods'] as $j => $FCST) {
$period = $FCST['name'];
$fcsttext = $FCST['detailedForecast'];
// $Status .= "<!-- raw $j='$period' '$fcsttext' -->\n";
$fcsttext = str_replace("\n", ' ', $fcsttext); // remove embedded new-line characters
$fcsttext = preg_replace('|\s+|is',' ',$fcsttext); // remove extra spaces
if (strpos($fcsttext, '&&') !== false) {
$fcsttext = substr($fcsttext, 0, strpos($fcsttext, '&&') - 1);
}
$fcsttext = trim($fcsttext);
$Status.= "<!-- adj FCSTJSON['periods'][$j]='$period' '$fcsttext' -->\n";
if (preg_match('/^(.*) (Through|And) (.*)/i', $period, $mtemp)) { // got period1 thru period2
list($fcstLow, $fcstHigh) = explode("\t", split_fcst($fcsttext));
$startPeriod = $mtemp[1];
$periodType = $mtemp[2];
$endPeriod = $mtemp[3];
$startIndex = 0;
$endIndex = 0;
$Status.= "<!-- splitting $periodType '$period'='$fcsttext' -->\n";
for ($k = 0; $k < count($fcstPeriods); $k++) { // find Starting and ending period indices
if (!$startIndex and $startPeriod == $fcstPeriods[$k]) {
$startIndex = $k;
}
if ($startIndex and !$endIndex and $endPeriod == $fcstPeriods[$k]) {
$endIndex = $k;
break;
}
}
for ($k = $startIndex; $k <= $endIndex; $k++) { // now generate the period names and appropriate fcst
if (preg_match('|night|i', $fcstPeriods[$k])) {
$forecasttext[$i] = $fcstLow;
}
else {
$forecasttext[$i] = $fcstHigh;
}
$forecasttitles[$i] = $fcstPeriods[$k];
$Status.= "<!-- $periodType $j, $i, '" . $forecasttitles[$i] . "'='" . $forecasttext[$i] . "' -->\n";
$i++;
}
$Status.= "<!-- end splitting -->\n\n";
continue;
}
$forecasttitles[$i] = $period;
$forecasttext[$i] = strip_tags($fcsttext);
$Status.= "<!-- normal $j, $i, '" . $forecasttitles[$i] . "'='" . $forecasttext[$i] . "' -->\n\n";
$i++;
} // end of multi-day forecast split
$nfcsts = $i - 1;
for ($i = 0; $i <= $nfcsts; $i++) { // interpret the text for icons, summary, temp, PoP
list($forecasticons[$i], $forecasttemp[$i], $forecastpop[$i]) = explode("\t", make_zone_icon($forecasttitles[$i], $forecasttext[$i]));
$forecasticons[$i] = preg_replace('/&/', '&', $forecasticons[$i]);
if ($doDebug) {
$Status.= "<!-- i=$i name='" . $forecasttitles[$i] . "' " . "temp='" . $forecasttemp[$i] . "' " . "\ndetail='" . $forecasttext[$i] . "' " . "\nicon='" . $forecasticons[$i] . "' -->\n\n";
}
} // end interpret text for icons
// end forecast ZONE processing
}
else { // Oops.. got neither point nor zone forecast.
$forecasttitles = array();
$forecasttext = array();
$forecasticons = array();
$forecasttemp = array();
$PrintMode = true;
if (isset($doPrintNWS) && !$doPrintNWS) {
$PrintMode = false;;
}
if (isset($_REQUEST['inc']) && strtolower($_REQUEST['inc']) == 'noprint') {
$PrintMode = false;
}
if ($PrintMode) { ?>
<table style="width: 640px; border: none;">
<tr style="text-align: center;">
<td><b>National Weather Service Forecast for: </b><span style="color: green;">
<?php
echo $NOAAlocation; ?></span>
</td>
</tr>
<?php
echo $ddMenu ?>
</table>
<?php
}
if ($PrintMode) {
print "<p>Sorry.. the forecast for $NOAAlocation is not available at this time.</p>\n";
}
print $Status;
if ($PrintMode and strlen($headers) > 0) {
print "<p>NWS server $lastURL has an error.</p>\n";
print "<p>View the source of this page for additional information in HTML comments.</p>\n";
}
$forecastValid = false;
return; // back to the calling program (if any)
} // end zone/point/nada forecast processing
$forecastValid = true;
$Status.= "<!-- forecast updated '$rawUpdated' ($forecastupdated) for lat,long or zone '$forecastlatlong' -->\n";
// -- now get an alert for the zone (if any)
$forecastwarnings = '';
$alertCacheName = str_replace('.txt', '-alerts.txt', $cacheName);
$alerthtml = '';
if ($Force == 1 or $Force == 2 or !file_exists($alertCacheName) or (file_exists($alertCacheName) and filemtime($alertCacheName) + 300 < time())) {
$alerthtml = ADV_fetchUrlWithoutHanging($alertURL);
$fSize = strlen($alerthtml);
$Status.= "<!-- loaded alert for $NOAAZone from $alertURL - $fSize bytes -->\n";
if (preg_match('/Location: (.*)\r\n/Uis', $alerthtml, $matches)) {
$newLoc = $matches[1];
$newurl = APIURL . $newLoc;
$alerthtml = ADV_fetchUrlWithoutHanging($newurl);
$fSize = strlen($alerthtml);
$Status.= "<!-- loaded alert from redirect $newurl - $fSize bytes -->\n";
}
if (strpos($alerthtml, '[') !== false) { // got a file.. save it
$fp = fopen($alertCacheName, "w");
if ($fp) {
$write = fputs($fp, $alerthtml);
fclose($fp);
$Status.= "<!-- wrote cache file $alertCacheName -->\n";
}
else {
$Status.= "<!-- unable to write cache file $alertCacheName -->\n";
}
}
}
elseif (file_exists($alertCacheName)) {
$alerthtml = file_get_contents($alertCacheName);
$Status.= "<!-- alerts loaded from $alertCacheName -->\n";
}
else {
$Status.= "<!-- alerts information not available -->\n";
}
list($alertHeaders, $alertContents) = explode("\r\n\r\n", $alerthtml . "\r\n\r\n");
if (strlen($alertContents) > 1) { // got some alerts.. process
$ALERTJSON = json_decode($alertContents, true);
if($doDebug) {$Status.= "<!-- ALERTJSON\n" . print_r($ALERTJSON, true) . " -->\n";}
/*
<!-- ALERTJSON
Array
(
[0] => Array
(
[id] => NWS-IDP-PROD-2135097-1993381
[event] => Wind Advisory
[product_issued] => 2016-11-19T11:29:39+00:00
[product_expires] => 2016-11-19T18:00:00+00:00
[event_starts] => 2016-11-19T11:29:00+00:00
[event_ends] => 2016-11-20T02:00:00+00:00
[headline] => Wind Advisory issued November 19 at 3:29AM PST expiring November 19 at 6:00PM PST by NWS San Francisco CA
[urgency] => Expected
[severity] => Moderate
[certainty] => Likely
[description] => * TIMING...TO 6 PM SATURDAY EVENING.
* LOCATION...SAN FRANCISCO...SAN FRANCISCO BAY SHORELINE...SAN
FRANCISCO PENINSULA COAST...SANTA CLARA VALLEY...EAST BAY
HILLS...SANTA CRUZ MOUNTAINS.
*/
// use the API alerts feed
if (is_array($ALERTJSON) and isset($ALERTJSON['features'][0])) {
$Status.= "<!-- preparing " . count($ALERTJSON['features']) . " warning links -->\n";
$Status.= "<!-- now " . date($timeFormat) . " (" . gmdate($timeFormat) . ") -->\n";
foreach($ALERTJSON['features'] as $i => $rawALERT) {
$ALERT = $rawALERT['properties']; // geo+json format
$expireUTC = strtotime($ALERT['expires']);
$Status.= "<!-- alert expires " . date($timeFormat, $expireUTC) . " (" . $ALERT['expires'] . ") -->\n";
if (
(time() < $expireUTC) && // V5.15 unexpired alerts w/in one of our zones - thanks to Jasiu!
(
(in_array($META["forecastZone"], $ALERT["geocode"]["UGC"]) ) ||
(in_array($META["fireWeatherZone"], $ALERT["geocode"]["UGC"]) ) ||
(in_array($META["countyZone"], $ALERT["geocode"]["UGC"]) )
)
)
{
$forecastwarnings.= '<a href="' . ALERTURL . $ALERT['id'] . '"' . ' title="' . $ALERT['headline'] . "\n---\n" . $ALERT['description'] . '" target="_blank">' . '<strong><span style="color: red">' . $ALERT['event'] . "</span></strong></a><br/>\n";
}
else {
$Status.= "<!-- alert " . $ALERT['id'] . " " . $ALERT['headline'] . " expired - " . $ALERT['expires'] . " -->\n";
}
}
}
else {
$Status.= "<!-- no current hazard alerts for $NOAAZone -->\n";
}
} // end API feed processing
/*
if(!file_put_contents($URLcacheFile,serialize($URLcache))) {
$Status .= "<!-- Error: unable to save URLcache $URLcacheFile -->\n";
} else {
$Status .= "<!-- URL cache saved to $URLcacheFile with ".count($URLcache). " entries. -->\n";
}
*/
$forecastoffice = '';
if (isset($META['WFOname'])) {
$forecastoffice = "National Weather Service " . $META['WFOname'];
}
if (isset($META['city']) and isset($META['state'])) {
$forecastcity = $META['city'] . ', ' . $META['state'];
}
$IncludeMode = false;
$PrintMode = true;
if (isset($doPrintNWS) && !$doPrintNWS) {
print $Status; // <------ print before return
return;
}
if (isset($_REQUEST['inc']) && strtolower($_REQUEST['inc']) == 'noprint') {
print $Status; // <------ print before return
return;
}
if (isset($_REQUEST['inc']) && strtolower($_REQUEST['inc']) == 'y') {
$IncludeMode = true;
}
if (isset($doIncludeNWS)) {
$IncludeMode = $doIncludeNWS;
}
// finally, we can print the results if desired
if (!$IncludeMode and $PrintMode) { ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>NWS Forecast for <?php
echo $forecastcity; ?></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body style="font-family:Verdana, Arial, Helvetica, sans-serif; font-size:12px; background-color:#FFFFFF">
<?php
}
print $Status;
// if the forecast text is blank, prompt the visitor to force an update
if (strlen($forecasttext[0]) < 2 and $PrintMode) {
if (!isset($PHP_SELF)) {
$PHP_SELF = $_SERVER['PHP_SELF'];
}
echo '<br/><br/>Forecast blank? <a href="' . $PHP_SELF . '?force=1">Force Update</a><br/><br/>';
}
if ($PrintMode) {
$tw = ($iconWidth + 8) * (1 + count($forecasticons) / 2);
if ($tw < 620) {
$tw = 620;
}
$wdth = $iconWidth + 10 . 'px';
?>
<table style="width: <?php
echo $tw; ?>px; border: none;">
<tr style="text-align: center;">
<td><b>National Weather Service Forecast for: </b><span style="color: green;">
<?php
echo $forecastcity; ?></span><br />
Issued by: <?php
echo $forecastoffice; ?>
</td>
</tr>
<tr>
<td style="text-align: center">Updated: <?php
echo $forecastupdated; ?>
</td><!--end forecastupdated-->
</tr>
<?php
echo $ddMenu ?>
<tr>
<td style="text-align: center; font-size: 18px; margin: 0px auto;"><b><?php
echo $NOAAlocation; ?></b>
<?php
if ($showZoneWarning and $isZone <> '') {
print "<br/><div style=\"border: 1px solid red; padding: 5px; font-size: 12px; margin: 3px;\">";
print "<small>The detailed point forecast weather data is not currently available.<br/>";
print "The zone forecast data for $NOAAZone (" . $META['zoneName'] . ") ";
print "will be displayed<br/>until the point forecast data is again available.";
if(isset($META['WFOphone']) and isset($META['WFOemail'])) {
$t = parse_url($META['forecastGridDataURL']);
print "<br/>If this persists, contact the NWS ".$META['WFOname']." WFO <br/> at ".
$META['WFOphone'] . " or email at ". $META['WFOemail'].
"<br/> to have them update the point forecast for " . $t['path'] .
" on " . $t['host'];
}
print "</small></div>\n";
}
?></td>
</tr>
<tr>
<td align="center">
<table style="width: <?php
echo $tw; ?>px; border: none; border-collapse: collapse; border-spacing: 2px;">
<?php
if ($showTwoIconRows) { // show all icons in two rows
// now loop over the $forecasticons array to build the two table rows with icons
if (stripos($forecasttitles[0], 'night') !== false) {
$iStart = - 1;
}
else {
$iStart = 0;
}
print "<tr>\n";
for ($i = 0; $i <= count($forecasticons); $i = $i + 2) { // day icon+cond
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasticons[$i + $iStart])) {
print $forecasticons[$i + $iStart];
}
else {
print " ";
}
print "</td>\n";
}
print "</tr>\n";
print "<tr>\n";
for ($i = 0; $i <= count($forecasticons); $i = $i + 2) { // day temperatures
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasttemp[$i + $iStart])) {
print $forecasttemp[$i + $iStart];
}
else {
print " ";
}
print "</td>\n";
}
print "</tr>\n";
print "<tr><td colspan=\"8\"> </td></tr>\n";
print "<tr>\n";
for ($i = 1; $i <= count($forecasticons) + 1; $i = $i + 2) { // night icons+conds
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasticons[$i + $iStart])) {
print $forecasticons[$i + $iStart];
}
else {
print " ";
}
print "</td>\n";
}
print "</tr>\n";
print "<tr>\n";
for ($i = 1; $i <= count($forecasticons) + 1; $i = $i + 2) { // night temperatures
print '<td style="width: ' . $wdth . '; text-align: center; vertical-align: top;">';
if (isset($forecasttemp[$i + $iStart])) {
print $forecasttemp[$i + $iStart];
}
else {
print " ";
}
print "</td>\n";
}
print "</tr>\n";
print "</table>\n\n";
print "</td>\n</tr>\n";
}
else { // show only first 9 icons (old style)
?>
<tr valign ="top" align="center">
<?php
for ($i = 0; $i < 9; $i++) {
print "<td style=\"width: 11%;\"><span style=\"font-size: 8pt;\">$forecasticons[$i]</span></td>\n";
}
?>
</tr>
<tr valign ="top" align="center">
<?php
for ($i = 0; $i < 9; $i++) {
print "<td style=\"width: 11%;\">$forecasttemp[$i]</td>\n";
}
?>
</tr>
</table>
</td>
</tr>
<?php
} // end show only first 9 icons (old style)
?>
</table>
<p><?php
if ($forecastwarnings <> '') {
print $forecastwarnings;
}
?> </p>
<table style="width: <?php
echo $tw; ?>px; border: none; border-collapse: collapse; border-spacing: 2px;">
<?php
for ($i = 0; $i < count($forecasttitles); $i++) {
print "<tr>\n";
print "<td style=\"width: 20%; text-align: left; vertical-align: top;\"><b>$forecasttitles[$i]</b><br /> <br /></td>\n";
print "<td style=\"width: 80%; text-align: left; vertical-align: top;\">$forecasttext[$i]</td>\n";
print "</tr>\n";
}
?>
</table>
<p> </p>
<p>Forecast from <a href="<?php
if (strlen($usingFile) > 0) {
echo htmlspecialchars('https://forecast.weather.gov/MapClick.php?zoneid='.$META['forecastZone'].'&zflg=1');
}
else {
list($lat,$lon) = explode(',',$META['point']);
echo htmlspecialchars("https://forecast.weather.gov/MapClick.php?lat=$lat&lon=$lon&unit=0&lg=english");
// echo htmlspecialchars( FCSTURL."/point/".$META['point']);
} ?>">NOAA-NWS</a>
for <?php
echo $forecastcity; ?>. <?php
if ($isZone) {
echo "(Zone forecast for " . $META['zoneName'] . ")";
}
else {
echo $usingFile;
}
if ($iconType == '.gif') {
print "<br/>Animated forecast icons courtesy of <a href=\"http://www.meteotreviglio.com/\">www.meteotreviglio.com</a>.";
}
?>
</p>
<?php
} // end printmode
if (!$IncludeMode and $PrintMode) { ?>
</body>
</html>
<?php
}
// end mainline code -- used functions are below
// ------------------------------------------------------------------------------------------
// FUNCTIONS
function ADV_fetchUrlWithoutHanging($inurl)
{
// get contents from one URL and return as string
global $Status, $needCookie,$doDebug,$doLogging,$curlStatus,$Version /*, $URLcache */;
$useFopen = false;
$overall_start = time();
$curlStatus = '';
if (!$useFopen) {
// Set maximum number of seconds (can have floating-point) to wait for feed before displaying page without feed
$numberOfSeconds = 6;
$url = $inurl;
// Thanks to Curly from ricksturf.com for the cURL fetch functions
$data = '';
$domain = parse_url($url, PHP_URL_HOST);
$theURL = str_replace('nocache', '?' . $overall_start, $url); // add cache-buster to URL if needed
$curlStatus.= "<!-- curl fetching '$theURL' -->\n";
$ch = curl_init(); // initialize a cURL session
curl_setopt($ch, CURLOPT_URL, $theURL); // connect to provided URL
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); // don't verify peer certificate
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (advforecast2.php (JSON) - saratoga-weather.org)');
// curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, // request LD-JSON format
array(
"Accept: application/geo+json",
"Cache-control: no-cache",
"Pragma: akamai-x-cache-on, akamai-x-get-request-id"
));
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $numberOfSeconds); // connection timeout
curl_setopt($ch, CURLOPT_TIMEOUT, $numberOfSeconds); // data timeout
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return the data transfer
curl_setopt($ch, CURLOPT_NOBODY, false); // set nobody
curl_setopt($ch, CURLOPT_HEADER, true); // include header information
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // follow Location: redirect
curl_setopt($ch, CURLOPT_MAXREDIRS, 1); // but only one time
if (isset($needCookie[$domain])) {
curl_setopt($ch, $needCookie[$domain]); // set the cookie for this request
curl_setopt($ch, CURLOPT_COOKIESESSION, true); // and ignore prior cookies
$Status.= "<!-- cookie used '" . $needCookie[$domain] . "' for GET to $domain -->\n";
}
$data = curl_exec($ch); // execute session
if (curl_error($ch) <> '') { // IF there is an error
$curlStatus.= "<!-- curl Error: " . curl_error($ch) . " -->\n"; // display error notice
}
$cinfo = curl_getinfo($ch); // get info on curl exec.
/*
curl info sample
Array
(
[url] => http://saratoga-weather.net/clientraw.txt
[content_type] => text/plain
[http_code] => 200
[header_size] => 266
[request_size] => 141
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 0.125
[namelookup_time] => 0.016
[connect_time] => 0.063
[pretransfer_time] => 0.063
[size_upload] => 0
[size_download] => 758
[speed_download] => 6064
[speed_upload] => 0
[download_content_length] => 758
[upload_content_length] => -1
[starttransfer_time] => 0.125
[redirect_time] => 0
[redirect_url] =>
[primary_ip] => 74.208.149.102
[certinfo] => Array
(
)
[primary_port] => 80
[local_ip] => 192.168.1.104
[local_port] => 54156
)
*/
if($url !== $cinfo['url'] and $cinfo['http_code'] == 200 and
strpos($url,'/points/') > 0 and strpos($cinfo['url'],'/gridpoints/') > 0) {
# only cache point forecast->gridpoint forecast successful redirects
$curlStatus .= "<!-- note: fetched '".$cinfo['url']."' after redirect was followed. -->\n";
//$URLcache[$inurl] = $cinfo['url'];
//$curlStatus .= "<!-- $inurl added to URLcache -->\n";
}
$curlStatus.= "<!-- HTTP stats: " . " RC=" . $cinfo['http_code'];
if (isset($cinfo['primary_ip'])) {
$curlStatus .= " dest=" . $cinfo['primary_ip'];
}
if (isset($cinfo['primary_port'])) {
$curlStatus .= " port=" . $cinfo['primary_port'];
}
if (isset($cinfo['local_ip'])) {
$curlStatus.= " (from sce=" . $cinfo['local_ip'] . ")";
}
$curlStatus.= "\n Times:" .
" dns=" . sprintf("%01.3f", round($cinfo['namelookup_time'], 3)) .
" conn=" . sprintf("%01.3f", round($cinfo['connect_time'], 3)) .
" pxfer=" . sprintf("%01.3f", round($cinfo['pretransfer_time'], 3));
if ($cinfo['total_time'] - $cinfo['pretransfer_time'] > 0.0000) {
$curlStatus.= " get=" . sprintf("%01.3f", round($cinfo['total_time'] - $cinfo['pretransfer_time'], 3));
}
$curlStatus.= " total=" . sprintf("%01.3f", round($cinfo['total_time'], 3)) . " secs -->\n";
// $curlStatus .= "<!-- curl info\n".print_r($cinfo,true)." -->\n";
curl_close($ch); // close the cURL session
// $curlStatus .= "<!-- raw data\n".$data."\n -->\n";
$stuff = explode("\r\n\r\n",$data); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if ($cinfo['http_code'] <> '200') {
$curlStatus.= "<!-- headers returned:\n" . $headers . "\n -->\n";
if($doLogging) {ADV_log('Fetch fail',$curlStatus); }
}
$Status .= $curlStatus;
return $data; // return headers+contents
}
else {
// print "<!-- using file_get_contents function -->\n";
$STRopts = array(
'http' => array(
'method' => "GET",
'protocol_version' => 1.1,
'header' => "Cache-Control: no-cache, must-revalidate\r\n" .
"Cache-control: max-age=0\r\n" .
"Connection: close\r\n" .
"User-agent: Mozilla/5.0 (advforecast2.php - saratoga-weather.org)\r\n" .
"Accept: application/geo+json\r\n"
) ,
'ssl' => array(
'method' => "GET",
'protocol_version' => 1.1,
'verify_peer' => false,
'header' => "Cache-Control: no-cache, must-revalidate\r\n" .
"Cache-control: max-age=0\r\n" .
"Connection: close\r\n" .
"User-agent: Mozilla/5.0 (advforecast2.php - saratoga-weather.org)\r\n" .
"Accept: application/geo+json\r\n"
)
);
$STRcontext = stream_context_create($STRopts);
$T_start = ADV_fetch_microtime();
$xml = file_get_contents($inurl, false, $STRcontext);
$T_close = ADV_fetch_microtime();
$headerarray = get_headers($url, 0);
$theaders = join("\r\n", $headerarray);
$xml = $theaders . "\r\n\r\n" . $xml;
$ms_total = sprintf("%01.3f", round($T_close - $T_start, 3));
$curlStatus.= "<!-- file_get_contents() stats: total=$ms_total secs -->\n";
$curlStatus.= "<-- get_headers returns\n" . $theaders . "\n -->\n";
// print " file() stats: total=$ms_total secs.\n";
$overall_end = time();
$overall_elapsed = $overall_end - $overall_start;
$curlStatus.= "<!-- fetch function elapsed= $overall_elapsed secs. -->\n";
// print "fetch function elapsed= $overall_elapsed secs.\n";
$Status .= $curlStatus;
return ($xml);
}
} // end ADV_fetchUrlWithoutHanging
// ------------------------------------------------------------------
function ADV_fetch_microtime()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
// ------------------------------------------------------------------
function ADV_log($msg,$details)
{
global $doLogging,$doLoggingDetail,$Version,$cacheFileDir,$Status,$META;
if(!$doLogging and !$doLoggingDetail) {return; }
$text = "---------------------------------------------\n";
$text .= gmdate('c').' (local: '.date('r').") - $Version\n";
$text .= "$msg\n";
$text .= "$details\n";
$fname = $cacheFileDir.'advforecast2-log-'.date('Y-m-d').'.txt';
if(isset($doLoggingDetail) and $doLoggingDetail) {
file_put_contents($fname,$text,FILE_APPEND);
$Status .= "<!-- logged '$msg' to '$fname' -->\n";
}
if(!$doLogging) { return; }
/*
<!-- curl fetching 'https://api.weather.gov/gridpoints/OTX/132,134/forecast' -->
<!-- HTTP stats: RC=500 dest=184.27.36.18 port=443 (from sce=192.168.1.104)
Times: dns=0.002 conn=0.015 pxfer=0.060 get=1.182 total=1.242 secs -->
<!-- headers returned:
HTTP/2 500
server: nginx/1.16.1
content-type: application/problem+json
access-control-allow-origin: *
access-control-allow-headers: Feature-Flags
x-request-id: a81c0162-6bc5-414e-aaa3-948837fd9487
x-correlation-id: a81c0162-6bc5-414e-aaa3-948837fd9487
x-server-id: vm-bldr-nids-apiapp2.ncep.noaa.gov
pragma: no-cache
content-length: 325
cache-control: private, must-revalidate, max-age=883
expires: Wed, 08 Jul 2020 19:09:06 GMT
date: Wed, 08 Jul 2020 18:54:23 GMT
vary: Accept,Feature-Flags,Accept-Language
strict-transport-security: max-age=31536000 ; includeSubDomains ; preload
-->
*/
preg_match('!curl fetching \'(\S+)\' !Uis',$details,$m);
if(isset($m[1])) {$url = trim($m[1]);} else {$url = '';}
preg_match('!RC=(\S+) dest=(\S+) .*from sce=(\S+)\)!Uis',$details,$m);
if(isset($m[2])) {
$RC = $m[1];
$IP = $m[2];
$SIP = $m[3];
} else {
$RC = '';
$IP = '';
$SIP = '';
}
if(preg_match('!Stale Age=(\S+) !Uis',$details,$m)) {
$RC = 'stale ' . trim($m[1]);
if($url == '' and isset($META['forecastURL'])) {$url = $META['forecastURL']; }
}
if($RC == '0' and preg_match('!timed out after (\S+) !Uis',$details,$m)) {
$RC = 'timeout ' . trim($m[1]).'ms';
}
preg_match('!x-server-id: (.+)\n!Uis',$details,$m);
if(isset($m[1])) { $svrid = trim($m[1]); } else {$svrid= '';}
preg_match('!x-request-id: (.+)\n!Uis',$details,$m);
if(isset($m[1])) { $reqid = trim($m[1]); } else {$reqid= '';}
preg_match('!x-cache: (.+)\n!Uis',$details,$m);
if(isset($m[1])) { $cache = trim($m[1]); } else {$cache= '';}
preg_match('!date: (.+)\n!Uis',$details,$m);
if(isset($m[1])) { $cdate = trim($m[1]); } else {$cdate= '';}
preg_match('!expires: (.+)\n!Uis',$details,$m);
if(isset($m[1])) { $edate = trim($m[1]); } else {$edate= '';}
$text = join("\t",array(date('c'),$url,$RC,$IP,$svrid,$reqid,$cache,$cdate,$edate,$SIP))."\n";
$fname = $cacheFileDir.'advforecast2-log-'.date('Y-m-d').'.csv';
if(!file_exists($fname)) {
$text = join("\t",array('Date','URL','RC','IP','SRV-ID','REQ-ID','X-Cache','Hdr-Date','Expires','FromIP'))."\n".$text;
}
file_put_contents($fname,$text,FILE_APPEND);
return;
}
// ------------------------------------------------------------------------------------------
// split off Low and High from multiday forecast
function split_fcst($fcst)
{
global $Status;
$f = explode(". ", $fcst . ' ');
$lowpart = 0;
$highpart = 0;
foreach($f as $n => $part) { // find the Low and High sentences
if (preg_match('/Low/i', $part)) {
$lowpart = $n;
}
if (preg_match('/High/i', $part)) {
$highpart = $n;
}
}
$f[$lowpart] = preg_replace('|(\d+) below|s', "-$1", $f[$lowpart]);
$f[$lowpart] = preg_replace('/( above| below| zero)/s', '', $f[$lowpart]);
$f[$lowpart].= '.';
$f[$highpart] = preg_replace('|(\d+) below|s', "-$1", $f[$highpart]);
$f[$highpart] = preg_replace('/( above| below| zero)/s', '', $f[$highpart]);
$f[$highpart].= '.';
$replpart = min($lowpart, $highpart) - 1;
$fcststr = '';
for ($i = 0; $i <= $replpart; $i++) {
$fcststr.= $f[$i] . '. ';
} // generate static fcst text
$fcstLow = $fcststr . ' ' . $f[$lowpart];
$fcstHigh = $fcststr . ' ' . $f[$highpart];
return ("$fcstLow\t$fcstHigh");
}
// ------------------------------------------------------------------------------------------
// function make_zone_icon: parse text and find suitable icon from zone forecast text for period
function make_zone_icon($day, $textforecast)
{
global $Conditions, $Status, $iconDir, $iconType, $doDebug, $iconHeight, $iconWidth;
$icon = "<strong>";
if (strpos($day, ' ') !== false) {
$icon.= wordwrap($day, 10, "<br/>", false) . '<br/>';
}
else {
$icon.= $day . '<br/><br/>';
}
$icon.= '</strong>';
$temperature = 'n/a';
$pop = '';
$iconimage = 'na.jpg';
$condition = 'N/A';
if (preg_match('|(\S+) (\d+) percent|', $textforecast, $mtemp)) { // found a PoP
$pop = $mtemp[2];
}
if (preg_match('|Chance of precipitation is (\d+)\s*%|', $textforecast, $mtemp)) { // found a zone pop
$pop = $mtemp[1];
if ($doDebug) {
$Status.= "<!-- pop='$pop' found -->\n";
}
}
// handle negative temperatures in zone forecast
$textforecast = preg_replace('/([\d|-]+) below/i', "-$1", $textforecast);
$textforecast = preg_replace('/zero/', '0', $textforecast);
if (preg_match('/(High[s]{0,1}|Low[s]{0,1}|Temperatures nearly steady|Temperatures falling to|Temperatures rising to|Near steady temperature|with a high|with a low) (in the upper|in the lower|in the mid|in the low to mid|in the lower to mid|in the mid to upper|in the|around|near|nearly|above|below|from) ([\d|-]+)[s]{0,1}/i', $textforecast, $mtemp)) { // found temp
if ($doDebug) {
$Status.= "<!-- mtemp " . print_r($mtemp, true) . " -->\n";
}
if (isset($mtemp[1]) and preg_match('|with a |', $mtemp[1])) {
$mtemp[1] = ucfirst(preg_replace('|with a |', '', $mtemp[1]));
if ($doDebug) {
$Status.= "<!-- mtemp modded to " . print_r($mtemp, true) . " -->\n";
}
}
if (substr($mtemp[1], 0, 1) == 'T' or substr($mtemp[1], 0, 1) == 'N') { // use day for highs/night for lows if 'Temperatures nearly steady'
$mtemp[1] = 'Highs';
if (preg_match('|night|i', $day)) {
$mtemp[1] = 'Lows';
}
}
$tcolor = '#FF0000';
if (strtoupper(substr($mtemp[1], 0, 1)) == 'L') {
$tcolor = '#0000FF';
}
$temperature = ucfirst(substr($mtemp[1], 0, 2) . ' <span style="color: ' . $tcolor . '">');
$t = $mtemp[3]; // the raw temp
if (preg_match('/(low to mid|lower to mid|mid to upper|upper|lower|mid)/', $mtemp[2], $ttemp)) {
if ($doDebug) {
$Status.= "<!-- ttemp " . print_r($ttemp, true) . " -->\n";
}
$t = $t + 5;
if ($ttemp[1] == 'upper') {
$temperature.= '>' . $t;
}
if ($ttemp[1] == 'lower') {
$temperature.= '<' . $t;
}
if ($ttemp[1] == 'mid') {
$temperature.= '≈' . $t;
}
if ($ttemp[1] == 'low to mid' or $ttemp[1] == 'lower to mid') {
$t = $t - 2;
$temperature.= '≈' . $t;
}
if ($ttemp[1] == 'mid to upper') {
$t = $t + 2;
$temperature.= '≈' . $t;
}
}
if (!isset($ttemp[1]) and !preg_match('/(near|around)/', $mtemp[2])) {
// special case '[Highs|Lows] in the dds.'
$t = $t + 5;
$temperature.= '≈' . $t;
}
elseif (preg_match('/(near|around)/', $mtemp[2], $ttemp)) {
$temperature.= '≈' . $mtemp[3];
}
$temperature.= '°F</span>';
}
if (preg_match('/(Highs|Lows) ([\d|-]+) to ([\d|-]+)/i', $textforecast, $mtemp)) { // temp range forecast
$tcolor = '#FF0000';
if (strtoupper(substr($mtemp[1], 0, 1)) == 'L') {
$tcolor = '#0000FF';
}
$temperature = ucfirst(substr($mtemp[1], 0, 2) . ' <span style="color: ' . $tcolor . '">');
$tavg = sprintf("%0d", round(($mtemp[3] + $mtemp[2]) / 2, 0));
$temperature.= '≈' . $tavg . '°F</span>';
}
if ($temperature == 'n/a') {
if (preg_match('|night|i', $day)) {
$temperature = 'Lo ' . $temperature;
}
else {
$temperature = 'Hi ' . $temperature;
}
}
// now look for harshest conditions first.. (in order in -data file
reset($Conditions); // Do search in load order
foreach($Conditions as $cond => $condrec) { // look for matching condition
if (preg_match("!$cond!i", $textforecast, $mtemp)) {
list($dayicon, $nighticon, $condition) = explode("\t", $condrec);
if (preg_match('|night|i', $day)) {
$iconimage = $nighticon . $pop . $iconType;
}
else {
$iconimage = $dayicon . $pop . $iconType;
}
break;
}
} // end of conditions search
$iconimage = preg_replace('|skc\d+|', 'skc', $iconimage); // handle funky SKC+a POP in forecast.
$shortCond = str_replace('hunderstorm', "-Storm", $condition);
$icon.= '<img src="' . $iconDir . $iconimage .
'" height="' . $iconHeight . '" width="' . $iconWidth . '" ' .
'alt="' . $condition . '" title="' . $condition . '" /><br/>' . $shortCond;
return ("$icon\t$temperature\t$pop");
} // end make_zone_icon function
// ------------------------------------------------------------------------------------------
// load the $Conditions array for icon selection based on key phrases
function load_cond_data()
{
global $Conditions, $Status;
$Condstring = '
#
cond|tornado|nsvrtsra|nsvrtsra|Severe storm|
cond|showery or intermittent. Some thunder|scttsra|nscttsra|Showers storms|
cond|thunder possible|scttsra|nscttsra|Showers storms|
cond|thunder|tsra|ntsra|Thunder storm|
cond|rain and sleet|raip|nraip|Rain Sleet|
cond|freezing rain and snow|fzra_sn|nfzra_sn|FrzgRn Snow|
cond|snow and freezing rain|fzra_sn|nfzra_sn|FrzgRn Snow|
cond|chance of snow and rain|rasn|nrasn|Chance Snow/Rain|
cond|chance of rain and snow|rasn|nrasn|Chance Snow/Rain|
cond|rain and snow|rasn|nrasn|Rain and Snow|
cond|rain or snow|rasn|nrasn|Rain or Snow|
cond|freezing rain|fzra|fzra|Freezing Rain|
cond|rain likely|ra|nra|Rain likely|
cond|snow showers|sn|nsn|Snow showers|
cond|showers likely|shra|nshra|Showers likely|
cond|chance showers|shra|nshra|Chance showers|
cond|isolated showers|shra|nshra|Isolated showers|
cond|scattered showers|shra|nshra|Scattered showers|
cond|chance of rain|ra|nra|Chance rain|
cond|rain|ra|nra|Rain|
cond|mix|rasn|rasn|Mix|
cond|sleet|ip|nip|Sleet|
cond|snow|sn|nsn|Snow|
cond|fog in the morning|sctfg|nbknfg|Fog a.m.|
cond|fog after midnight|sctfg|nbknfg|Fog late|
cond|fog|fg|nfg|Fog|
cond|wind chill down to -|cold|cold|Very Cold|
cond|heat index up to 1|hot|hot|Very Hot|
cond|hot|hot|hot|Hot|
cond|overcast|ovc|novc|Overcast|
cond|mostly cloudy|bkn|nbkn|Mostly Cloudy|
cond|partly cloudy|sct|nsct|Partly Cloudy|
cond|cloudy|cloudy|ncloudy|Cloudy|
cond|partly sunny and windy|wind_sct|nwind_sct|Partly Sunny|
cond|mostly sunny and windy|wind_few|nwind_few|Mostly Sunny|
cond|partly sunny and breezy|wind_sct|nwind_sct|Partly Sunny|
cond|mostly sunny and breezy|wind_few|nwind_few|Mostly Sunny|
cond|partly sunny|sct|nsct|Partly Sunny|
cond|mostly sunny|few|nfew|Mostly Sunny|
cond|mostly clear|few|nfew|Mostly Clear|
cond|sunny|skc|nskc|Sunny|
cond|clear|skc|nskc|Clear|
cond|fair|few|nfew|Fair|
cond|cloud|bkn|nbkn|Variable Clouds|
#
';
$config = explode("\n", $Condstring);
foreach($config as $key => $rec) { // load the parser condition strings
$recin = trim($rec);
if ($recin and substr($recin, 0, 1) <> '#') { // got a non comment record
list($type, $keyword, $dayicon, $nighticon, $condition) = explode('|', $recin . '|||||');
if (isset($type) and strtolower($type) == 'cond' and isset($condition)) {
$Conditions["$keyword"] = "$dayicon\t$nighticon\t$condition";
}
} // end if not comment or blank
} // end loading of loop over config recs
} // end of load_cond_data function
// ------------------------------------------------------------------------------------------
function load_lookups()
{
// static lookup arrays
global $fcstPeriods, $NWSICONLIST;
$fcstPeriods = array( // for filling in the '<period> Through <period>' zone forecasts.
'Monday',
'Monday Night',
'Tuesday',
'Tuesday Night',
'Wednesday',
'Wednesday Night',
'Thursday',
'Thursday Night',
'Friday',
'Friday Night',
'Saturday',
'Saturday Night',
'Sunday',
'Sunday Night',
'Monday',
'Monday Night',
'Tuesday',
'Tuesday Night',
'Wednesday',
'Wednesday Night',
'Thursday',
'Thursday Night',
'Friday',
'Friday Night',
'Saturday',
'Saturday Night',
'Sunday',
'Sunday Night'
);
/* original list:
$NWSICONLIST = array(
# index is new icon name
# imagename is original image name (for which we have icons available)
# PoP flag controls if PoP is written on output or not.
# ScePosition controls if image is pulled from Left, Middle or Right of source image
# Text Name - optional, just a reminder of what it means
# imgname | PoP?=[YN] | ScePosition=[LMR] | Text Name (optional)
'bkn' => 'bkn|N|L|Broken Clouds',
'night/bkn' => 'nbkn|N|L|Night Broken Clouds',
'blizzard' => 'blizzard|Y|L|Blizzard',
'night/blizzard' => 'nblizzard|Y|L|Night Blizzard',
'cold' => 'cold|Y|L|Cold',
'night/cold' => 'cold|Y|L|Cold',
# 'cloudy' => 'cloudy|Y|L|Overcast (old cloudy)',
'dust' => 'du|N|M|Dust',
'night/dust' => 'ndu|N|M|Night Dust',
# 'fc' => 'fc|N|L|Funnel Cloud',
# 'nsvrtsra' => 'nsvrtsra|N|L|Funnel Cloud (old)',
'few' => 'few|Y|L|Few Clouds',
'night/few' => 'nfew|Y|L|Night Few Clouds',
# 'fg' => 'fg|N|R|Fog',
# 'br' => 'br|Y|R|Fog / mist old',
# 'fu' => 'fu|N|L|Smoke',
'fog' => 'nfg|N|R|Night Fog',
'night/fog' => 'nfg|N|R|Night Fog',
'fzra' => 'fzra|Y|L|Freezing rain',
'night/fzra' => 'nfzra|Y|L|Night Freezing Rain',
# 'fzrara' => 'fzrara|Y|L|Rain/Freezing Rain (old)',
'snow_fzra' => 'fzra_sn|Y|L|Freezing Rain/Snow',
'night/snow_fzra' => 'nfzra_sn|Y|L|Night Freezing Rain/Snow',
# 'mix' => 'mix|Y|L|Freezing Rain/Snow',
# 'hi_bkn' => 'hi_bkn|Y|L|Broken Clouds (old)',
# 'hi_few' => 'hi_few|Y|L|Few Clouds (old)',
# 'hi_sct' => 'hi_sct|N|L|Scattered Clouds (old)',
# 'hi_skc' => 'hi_skc|N|L|Clear Sky (old)',
# 'hi_nbkn' => 'hi_nbkn|Y|L|Night Broken Clouds (old)',
# 'hi_nfew' => 'hi_nfew|Y|L|Night Few Clouds (old)',
# 'hi_nsct' => 'hi_nsct|N|L|Night Scattered Clouds (old)',
# 'hi_nskc' => 'hi_nskc|N|L|Night Clear Sky (old)',
# 'hi_nshwrs' => 'hi_nshwrs|Y|R|Night Showers',
# 'hi_ntsra' => 'hi_ntsra|Y|L|Night Thunderstorm',
# 'hi_shwrs' => 'hi_shwrs|Y|R|Showers',
# 'hi_tsra' => 'hi_tsra|Y|L|Thunderstorm',
'hur_warn' => 'hur_warn|N|L|Hurrican Warning',
'night/hur_warn' => 'hur_warn|N|L|Hurrican Warning',
'hur_watch' => 'hur_watch|N|L|Hurricane Watch',
'night/hur_watch' => 'hur_watch|N|L|Hurricane Watch',
# 'hurr' => 'hurr|N|L|Hurrican Warning old',
# 'hurr-noh' => 'hurr-noh|N|L|Hurricane Watch old',
'hazy' => 'hz|N|L|Haze',
'night/hazy' => 'hz|N|L|Haze',
# 'hazy' => 'hazy|N|L|Haze old',
'hot' => 'hot|N|R|Hot',
'night/hot' => 'hot|N|R|Hot',
'sleet' => 'ip|Y|L|Ice Pellets',
'night/sleet' => 'nip|Y|L|Night Ice Pellets',
# 'minus_ra' => 'minus_ra|Y|L|Stopped Raining',
# 'ra1' => 'ra1|N|L|Stopped Raining (old)',
# 'mist' => 'mist|N|R|Mist (fog) (old)',
# 'ncloudy' => 'ncloudy|Y|L|Overcast night(old ncloudy)',
# 'ndu' => 'ndu|N|M|Night Dust',
# 'nfc' => 'nfc|N|L|Night Funnel Cloud',
# 'nbr' => 'nbr|Y|R|Night Fog/mist (old)',
# 'nfu' => 'nfu|N|L|Night Smoke',
# 'nmix' => 'nmix|Y|30|Night Freezing Rain/Snow (old)',
# 'nrasn' => 'nrasn|Y|M|Night Snow (old)',
# 'pcloudyn' => 'pcloudyn|Y|L|Night Partly Cloudy (old)',
# 'nscttsra' => 'nscttsra|Y|M|Night Scattered Thunderstorm',
# 'nsn_ip' => 'nsn_ip|Y|L|Night Snow/Ice Pellets (old)',
# 'nwind' => 'nwind|N|5|Night Windy/Clear (old)',
'ovc' => 'ovc|N|L|Overcast',
'night/ovc' => 'novc|N|L|Night Overcast',
'rain' => 'ra|Y|30|Rain',
'night/rain' => 'nra|Y|30|Night Rain',
'rain_sleet' => 'raip|Y|M|Rain/Ice Pellets',
'night/rain_sleet' => 'nraip|Y|M|Night Rain/Ice Pellets',
'rain_fzra' => 'ra_fzra|Y|30|Rain/Freezing Rain',
'night/rain_fzra' => 'nra_fzra|Y|30|Night Freezing Rain',
'rain_snow' => 'ra_sn|Y|M|Rain/Snow',
'night/rain_snow' => 'nra_sn|Y|M|Night Rain/Snow',
# 'rasn' => 'rasn|Y|M|Rain/Snow (old)',
'sct' => 'sct|N|L|name',
'night/sct' => 'nsct|N|L|Night Scattered Clouds',
# 'pcloudy' => 'pcloudy|Y|L|Partly Cloudy (old)',
# 'scttsra' => 'scttsra|Y|M|name',
'rain_showers' => 'shra|Y|10|Rain Showers',
'night/rain_showers' => 'nshra|Y|8|Night Rain Showers',
# 'shra2' => 'shra2|N|10|Rain Showers (old)',
'skc' => 'skc|N|L|Clear',
'night/skc' => 'nskc|N|L|Night Clear',
'snow' => 'sn|Y|L|Snow',
'night/snow' => 'nsn|Y|L|Night Snow',
'snow_sleet' => 'snip|Y|L|Snow/Ice Pellets',
'night/snow_sleet' => 'nsnip|Y|L|Night Snow/Ice Pellets',
'smoke' => 'smoke|N|L|Smoke',
'night/smoke' => 'nsmoke|N|L|Smoke', // NEW - Nov-2016
# 'sn_ip' => 'sn_ip|Y|L|Snow/Ice Pellets (old)',
# 'tcu' => 'tcu|N|L|Towering Cumulus (old)',
'tornado' => 'tor|N|L|Tornado',
'night/tornado' => 'ntor|N|L|Night Tornado',
'tsra' => 'tsra|Y|10|Thunderstorm',
'night/tsra' => 'ntsra|Y|8|Night Thunderstorm',
# 'tstormn' => 'tstormn|N|L|Thunderstorm night (old)',
# 'ts_nowarn' => 'ts_nowarn|N|L|Tropical Storm',
'ts_warn' => 'ts_warn|N|L|Tropical Storm Warning',
'night/ts_warn' => 'ts_warn|N|L|Tropical Storm Warning',
# 'tropstorm-noh' => 'tropstorm-noh|N|L|Tropical Storm old',
# 'tropstorm' => 'tropstorm|N|L|Tropical Storm Warning old',
'ts_watch' => 'ts_watch|N|L|Tropical Storm Watch',
'night/ts_watch' => 'ts_watch|N|L|Tropical Storm Watch',
# 'ts_hur_flags' => 'ts_hur_flags|Y|L|Hurrican Warning old',
# 'ts_no_flag' => 'ts_no_flag|Y|L|Tropical Storm old',
'wind_bkn' => 'wind_bkn|N|8|Windy/Broken Clouds',
'night/wind_bkn' => 'nwind_bkn|N|5|Night Windy/Broken Clouds',
'wind_few' => 'wind_few|N|8|Windy/Few Clouds',
'night/wind_few' => 'nwind_few|N|5|Night Windy/Few Clouds',
'wind_ovc' => 'wind_ovc|N|8|Windy/Overcast',
'night/wind_ovc' => 'nwind_ovc|N|5|Night Windy/Overcast',
'wind_sct' => 'wind_sct|N|8|Windy/Scattered Clouds',
'night/wind_sct' => 'nwind_sct|N|5|Night Windy/Scattered Clouds',
'wind_skc' => 'wind_skc|N|8|Windy/Clear',
'night/wind_skc' => 'nwind_skc|N|5|Night Windy/Clear',
# 'wind' => 'wind|N|L|Windy/Clear (old)',
'na' => 'N|L|Not Available',
);
*/
$NWSICONLIST = array(
// index is new icon name
// imagename is original image name (for which we have icons available)
// PoP flag controls if PoP is written on output or not.
// ScePosition controls if image is pulled from Left, Middle or Right of source image
// Text Name - optional, just a reminder of what it means
// imgname | PoP?=[YN] | ScePosition=[LMR] | Text Name (optional)
'bkn' => 'bkn|N|L|Broken Clouds',
'night/bkn' => 'nbkn|N|L|Night Broken Clouds',
'blizzard' => 'blizzard|Y|L|Blizzard',
'night/blizzard' => 'nblizzard|Y|L|Night Blizzard',
'cold' => 'cold|Y|L|Cold',
'night/cold' => 'cold|Y|L|Cold',
// 'cloudy' => 'cloudy|Y|L|Overcast (old cloudy)',
'dust' => 'du|N|22|Dust',
'night/dust' => 'ndu|N|22|Night Dust',
// 'fc' => 'fc|N|L|Funnel Cloud',
// 'nsvrtsra' => 'nsvrtsra|N|L|Funnel Cloud (old)',
'few' => 'few|Y|L|Few Clouds',
'night/few' => 'nfew|Y|L|Night Few Clouds',
// 'fg' => 'fg|N|R|Fog',
// 'br' => 'br|Y|R|Fog / mist old',
// 'fu' => 'fu|N|L|Smoke',
'fog' => 'fg|N|R|Fog',
'night/fog' => 'nfg|N|R|Night Fog',
'fzra' => 'fzra|Y|L|Freezing rain',
'night/fzra' => 'nfzra|Y|L|Night Freezing Rain',
// 'fzrara' => 'fzrara|Y|L|Rain/Freezing Rain (old)',
'snow_fzra' => 'fzra_sn|Y|L|Freezing Rain/Snow',
'night/snow_fzra' => 'nfzra_sn|Y|L|Night Freezing Rain/Snow',
// 'mix' => 'mix|Y|L|Freezing Rain/Snow',
// 'hi_bkn' => 'hi_bkn|Y|L|Broken Clouds (old)',
// 'hi_few' => 'hi_few|Y|L|Few Clouds (old)',
// 'hi_sct' => 'hi_sct|N|L|Scattered Clouds (old)',
// 'hi_skc' => 'hi_skc|N|L|Clear Sky (old)',
// 'hi_nbkn' => 'hi_nbkn|Y|L|Night Broken Clouds (old)',
// 'hi_nfew' => 'hi_nfew|Y|L|Night Few Clouds (old)',
// 'hi_nsct' => 'hi_nsct|N|L|Night Scattered Clouds (old)',
// 'hi_nskc' => 'hi_nskc|N|L|Night Clear Sky (old)',
// 'hi_nshwrs' => 'hi_nshwrs|Y|R|Night Showers',
// 'hi_ntsra' => 'hi_ntsra|Y|L|Night Thunderstorm',
// 'hi_shwrs' => 'hi_shwrs|Y|R|Showers',
// 'hi_tsra' => 'hi_tsra|Y|L|Thunderstorm',
'hur_warn' => 'hur_warn|N|L|Hurrican Warning',
'night/hur_warn' => 'hur_warn|N|L|Hurrican Warning',
"hurr_warn" => "hurr|N|L|Hurricane warning", // New NWS list
"night/hurr_warn" => "hurr|N|L|Night Hurricane warning", // New NWS list
'hur_watch' => 'hur_watch|N|L|Hurricane Watch',
'night/hur_watch' => 'hur_watch|N|L|Hurricane Watch',
"hurr_watch" => "hurr-noh|N|L|Hurricane watch", // New NWS list
"night/hurr_watch" => "hurr-noh|N|L|Night Hurricane watch", // New NWS list
'hurricane' => 'hurr-noh|Y|L|Hurricane',
'night/hurricane' => 'hurr-noh|Y|L|Hurricane',
// 'hurr' => 'hurr|N|L|Hurrican Warning old',
// 'hurr-noh' => 'hurr-noh|N|L|Hurricane Watch old',
'hazy' => 'hz|N|L|Haze',
'night/hazy' => 'hz|N|L|Haze',
"haze" => "hz|N|L|Haze", // New NWS list
"night/haze" => "hz|N|L|Night Haze", // New NWS list
// 'hazy' => 'hazy|N|L|Haze old',
'hot' => 'hot|N|R|Hot',
'night/hot' => 'hot|N|R|Hot',
'sleet' => 'ip|Y|L|Ice Pellets',
'night/sleet' => 'nip|Y|L|Night Ice Pellets',
// 'minus_ra' => 'minus_ra|Y|L|Stopped Raining',
// 'ra1' => 'ra1|N|L|Stopped Raining (old)',
// 'mist' => 'mist|N|R|Mist (fog) (old)',
// 'ncloudy' => 'ncloudy|Y|L|Overcast night(old ncloudy)',
// 'ndu' => 'ndu|N|M|Night Dust',
// 'nfc' => 'nfc|N|L|Night Funnel Cloud',
// 'nbr' => 'nbr|Y|R|Night Fog/mist (old)',
// 'nfu' => 'nfu|N|L|Night Smoke',
// 'nmix' => 'nmix|Y|30|Night Freezing Rain/Snow (old)',
// 'nrasn' => 'nrasn|Y|M|Night Snow (old)',
// 'pcloudyn' => 'pcloudyn|Y|L|Night Partly Cloudy (old)',
// 'nscttsra' => 'nscttsra|Y|M|Night Scattered Thunderstorm',
// 'nsn_ip' => 'nsn_ip|Y|L|Night Snow/Ice Pellets (old)',
// 'nwind' => 'nwind|N|5|Night Windy/Clear (old)',
'ovc' => 'ovc|N|L|Overcast',
'night/ovc' => 'novc|N|L|Night Overcast',
'rain' => 'ra|Y|30|Rain',
'night/rain' => 'nra|Y|30|Night Rain',
'rain_showers' => 'shra|Y|12|Rain Showers',
'night/rain_showers' => 'nshra|Y|12|Night Rain Showers',
"rain_showers_hi" => "hi_shwrs|Y|45|Rain showers (low cloud cover)", // New NWS list
"night/rain_showers_hi" => "hi_nshwrs|Y|45|Night Rain showers (low cloud cover)", // New NWS list
'rain_sleet' => 'raip|Y|M|Rain/Ice Pellets',
'night/rain_sleet' => 'nraip|Y|M|Night Rain/Ice Pellets',
'rain_fzra' => 'ra_fzra|Y|30|Rain/Freezing Rain',
'night/rain_fzra' => 'nra_fzra|Y|30|Night Freezing Rain',
'rain_snow' => 'ra_sn|Y|22|Rain/Snow',
'night/rain_snow' => 'nra_sn|Y|22|Night Rain/Snow',
// 'rasn' => 'rasn|Y|M|Rain/Snow (old)',
'sct' => 'sct|N|L|name',
'night/sct' => 'nsct|N|L|Night Scattered Clouds',
// 'pcloudy' => 'pcloudy|Y|L|Partly Cloudy (old)',
// 'scttsra' => 'scttsra|Y|M|name',
// 'shra2' => 'shra2|N|10|Rain Showers (old)',
'skc' => 'skc|N|L|Clear',
'night/skc' => 'nskc|N|L|Night Clear',
'snow' => 'sn|Y|L|Snow',
'night/snow' => 'nsn|Y|L|Night Snow',
'snow_sleet' => 'sn_ip|Y|L|Snow/Ice Pellets',
'night/snow_sleet' => 'nsn_ip|Y|L|Night Snow/Ice Pellets',
'smoke' => 'fu|N|L|Smoke',
'night/smoke' => 'nfu|N|L|Smoke', // NEW - Nov-2016
// 'sn_ip' => 'sn_ip|Y|L|Snow/Ice Pellets (old)',
// 'tcu' => 'tcu|N|L|Towering Cumulus (old)',
'tornado' => 'tor|N|L|Tornado',
'night/tornado' => 'ntor|N|L|Night Tornado',
'tsra' => 'tsra|Y|10|Thunderstorm',
'night/tsra' => 'ntsra|Y|10|Night Thunderstorm',
"tsra_sct" => "scttsra|Y|20|Thunderstorm (medium cloud cover)", // New NWS list
"night/tsra_sct" => "nscttsra|Y|20|Night Thunderstorm (medium cloud cover)", // New NWS list
"tsra_hi" => "hi_tsra|Y|L|Thunderstorm (low cloud cover)", // New NWS list
"night/tsra_hi" => "hi_ntsra|Y|L|Night Thunderstorm (low cloud cover)", // New NWS list
// 'tstormn' => 'tstormn|N|L|Thunderstorm night (old)',
// 'ts_nowarn' => 'ts_nowarn|N|L|Tropical Storm',
"ts_hurr_warn" => "ts_hur_flags|N|L|Tropical storm with hurricane warning in effect", // New NWS list
"night/ts_hurr_warn" => "ts_hur_flags|N|L|Night Tropical storm with hurricane warning in effect", // New NWS list
'tropical_storm' => 'tropstorm-noh|Y|L|Tropical Storm Warning',
'night/tropical_storm' => 'tropstorm-noh|Y|L|Tropical Storm Warning',
'ts_warn' => 'tropstorm|Y|L|Tropical Storm Warning',
'night/ts_warn' => 'tropstorm|Y|L|Tropical Storm Warning',
// 'tropstorm-noh' => 'tropstorm-noh|N|L|Tropical Storm old',
// 'tropstorm' => 'tropstorm|N|L|Tropical Storm Warning old',
'ts_watch' => 'tropstorm-noh|Y|L|Tropical Storm Watch',
'night/ts_watch' => 'tropstorm-noh|Y|L|Tropical Storm Watch',
// 'ts_hur_flags' => 'ts_hur_flags|Y|L|Hurrican Warning old',
// 'ts_no_flag' => 'ts_no_flag|Y|L|Tropical Storm old',
'wind_bkn' => 'wind_bkn|N|7|Windy/Broken Clouds',
'night/wind_bkn' => 'nwind_bkn|N|7|Night Windy/Broken Clouds',
'wind_few' => 'wind_few|N|7|Windy/Few Clouds',
'night/wind_few' => 'nwind_few|N|7|Night Windy/Few Clouds',
'wind_ovc' => 'wind_ovc|N|7|Windy/Overcast',
'night/wind_ovc' => 'nwind_ovc|N|7|Night Windy/Overcast',
'wind_sct' => 'wind_sct|N|7|Windy/Scattered Clouds',
'night/wind_sct' => 'nwind_sct|N|7|Night Windy/Scattered Clouds',
'wind_skc' => 'wind_skc|N|7|Windy/Clear',
'night/wind_skc' => 'nwind_skc|N|7|Night Windy/Clear',
// 'wind' => 'wind|N|L|Windy/Clear (old)',
'na' => 'na|N|L|Not Available',
);
} // end load_lookups function
// ------------------------------------------------------------------------------------------
function convert_to_local_icon($icon)
{
// input: https://api-v1.weather.gov/icons/land/day/rain_showers,20/sct,20?size=medium
// https://api-v1.weather.gov/icons/land/night/sct?size=medium
// output: {iconDir}{icon}.{$iconType} or
// DualImage.php?i={lefticon}&j={righticon}&ip={leftpop}&jp={rightpop}
global $Status, $NWSICONLIST, $iconDir, $iconType, $iconHeight, $iconWidth, $DualImageAvailable;
$newicon = $icon; // for testing
$uparts = parse_url($icon);
$iparts = array_slice(explode('/', $uparts['path']) , 3); //get day|night/icon[/icon]
// $Status .= "<!-- iparts \n".print_r($iparts,true)." -->\n";
$daynight = ($iparts[0] == 'day') ? '' : 'night/';
list($icon1, $pop1) = explode(',', $iparts[1] . ',');
$doDual = false;
if (isset($iparts[2])) {
$doDual = true;
list($icon2, $pop2) = explode(',', $iparts[2] . ',');
}
else {
$icon2 = '';
$pop2 = '';
}
// convert new API icon names to old image names
if (isset($NWSICONLIST["{$daynight}{$icon1}"])) {
list($nicon1, $rest) = explode('|', $NWSICONLIST["{$daynight}{$icon1}"]);
$icon1 = $nicon1;
}
else {
$Status.= "<!-- icon1='$icon1' not found - na used instead -->\n";
$icon1 = 'na';
}
if ($icon2 <> '') {
if (isset($NWSICONLIST["{$daynight}{$icon2}"])) {
list($nicon2, $rest) = explode('|', $NWSICONLIST["{$daynight}{$icon2}"]);
$icon2 = $nicon2;
}
else {
$Status.= "<!-- icon2='$icon2' not found - na used instead -->\n";
$icon2 = 'na';
}
}
// $Status .= "<!-- doDual='$doDual' DualImageAvailable='$DualImageAvailable' icon1=$icon1 icon2=$icon2 -->\n";
if ($doDual and $DualImageAvailable) { // generate the DualImage.php script calling sequence for image
$newicon = "DualImage.php?";
$newicon.= "i=$icon1";
if ($pop1 <> '') {
$newicon.= "&ip=$pop1";
}
$newicon.= "&j=$icon2";
if ($pop2 <> '') {
$newicon.= "&jp=$pop2";
}
$Status.= "<!-- dual image '$newicon' used-->\n";
}
elseif (file_exists("{$iconDir}{$icon1}{$pop1}{$iconType}")) { // use the image as-is
$newicon = "{$iconDir}{$icon1}{$pop1}{$iconType}";
/* } elseif ( $DualImageAvailable ) { // oops... pop icon doesn't exist but we can generate it
$newicon = "DualImage.php?";
$newicon .= "i=$icon1";
if($pop1 <> '') { $newicon .= "&ip=$pop1"; }
$Status .= "<!-- missing icon '{$iconDir}{$icon1}{$pop1}{$iconType}' .. using '$newicon' instead -->\n";
*/
}
elseif (file_exists("{$iconDir}{$icon1}{$iconType}")) { // oops... pop icon doesn't exist
$newicon = "{$iconDir}{$icon1}{$iconType}";
$Status.= "<!-- missing icon '{$iconDir}{$icon1}{$pop1}{$iconType}' .. " . "using '{$iconDir}{$icon1}{$iconType}' instead -->\n";
}
else {
$newicon = "{$iconDir}na{$iconType}";
$Status.= "<!-- missing icon '{$iconDir}{$icon1}{$pop1}{$iconType}' .. " . "using '{$iconDir}na{$iconType}' instead -->\n";
}
return ($newicon);
} // end convert_to_local_icon
// ------------------------------------------------------------------------------------------
function make_local_icon($icon, $period, $cond, $temperature, $details)
{
// assemble the full icon to use
$ticon = '<strong>';
if (strpos($period, ' ') !== false) {
$ticon.= wordwrap($period, 10, "<br/>", false) . '<br/>';
// $ticon = str_replace(' ','<br/>',$ticon) . '<br/>';
}
else {
$ticon.= $period . '<br/><br/>';
}
$ticon.= '</strong>';
$shortCond = str_replace('hunderstorm', "-Storm", $cond);
$ticon.= '<img src="' . $icon . '" ' . "alt=\"$period: $cond\" " . "title=\"$period: $details\" /><br/>" . "$shortCond <br/>";
$ticon = str_replace('size=medium', 'size=small', $ticon);
return ($ticon);
} // end make_local_icon
// ------------------------------------------------------------------------------------------
function get_meta_info($mainCache, $pointURL, $zoneURL)
{
//
// this function saves up-to 3 accesses to the API site for metadata regarding the
// point, zone and WFO info. The three accesses are done, the JSON parsed and
// saved into a -json-meta.txt file as straight JSON for use in the $META array
// in the main script.
// Note: the logic below assumes that the JSON-LD format is returned by the API.
//
global $Status, $doDebug;
$compass = array(
'N',
'NNE',
'NE',
'ENE',
'E',
'ESE',
'SE',
'SSE',
'S',
'SSW',
'SW',
'WSW',
'W',
'WNW',
'NW',
'NNW'
);
// get and cache the meta information from the point, zone and WFO entries for the 'point'.
// use a new JSON cache file to store the meta information we need
//
$metaCache = str_replace('-geojson.txt', '-geojson-meta.txt', $mainCache);
// make the point-forecast URL into a metadata request URL
$metaPointURL = str_replace('/forecast', '', $pointURL);
$ourPoint = '';
if (preg_match('|/([^/]+)/forecast|i', $pointURL, $matches)) {
$ourPoint = $matches[1];
}
// make the zone forecast URL into a metadata request URL
$metaZoneURL = $zoneURL;
$metaZoneURL = preg_replace('!/forecast$!is', '', $metaZoneURL);
//$metaZoneURL = str_replace('JSON-LD', 'forecast', $metaZoneURL);
$ourZone = '';
if (preg_match('|/([^/]+)/forecast|i', $zoneURL, $matches)) {
$ourZone = $matches[1];
}
$Status.= "<!-- meta info re: point='$ourPoint' zone='$ourZone' metacache= '$metaCache' -->\n";
$Status.= "<!-- metaZoneURL='$metaZoneURL' -->\n";
$META = array();
// First.. see if we've already cached this meta info.. it won't change once discovered for a point
if(file_exists($metaCache)) {
$age = time() - filemtime($metaCache);
$Status.= "<!-- meta cache $metaCache age=".sec2hmsADV($age)." h:m:s -->\n";
}
if (file_exists($metaCache) and filemtime($metaCache) + 24*60*60 > time() ) {
$recs = file_get_contents($metaCache);
$age = time() - filemtime($metaCache);
$META = json_decode($recs, true);
$Status.= "<!-- loaded meta info from $metaCache age=".sec2hmsADV($age)." h:m:s -->\n";
if (isset($META['point']) and $META['point'] !== $ourPoint) {
// oops... changed the lat/long for the point .. reload the meta data
$Status.= "<!-- point '" . $ourPoint . "' changed from '" . $META['point'] . "' - reloading meta data -->\n";
unset($META);
$META = array();
}
}
else { // JSON ERROR
$Status.= "<!-- Meta cache file not found or more than 24hrs old .. reloading meta data from NWS site -->\n";
}
$saveNew = false;
if (!isset($META['city']) or !isset($META['forecastURL'])) { // no city data or fcstURL in the saved metadata so load it
$Status.= "<!-- getting metadata for point from $metaPointURL -->\n";
$rawhtml = ADV_fetchUrlWithoutHanging($metaPointURL);
$stuff = explode("\r\n\r\n",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
$rawPJSON = json_decode($content, true); // parse the JSON into an associative array
$PJSON = $rawPJSON['properties']; // geojson format
if (json_last_error() == JSON_ERROR_NONE) { // got the point METADATA.. stuff it away
if ($doDebug) {
$Status.= "<!-- PJSON raw data\n" . var_export($PJSON, true) . " -->\n";
}
if (isset($PJSON['relativeLocation']['properties']['city'])) {
$META['city'] = $PJSON['relativeLocation']['properties']['city'];
$META['state'] = $PJSON['relativeLocation']['properties']['state'];
$distance = $PJSON['relativeLocation']['properties']['distance']['value'];
$distance = floor(0.000621371 * $distance); // truncate to nearest whole mile
$Status.= "<!-- distance=$distance from " . $META['city'] . " -->\n";
if ($distance >= 2) {
$angle = $PJSON['relativeLocation']['properties']['bearing']['value'];
$direction = $compass[round($angle / 22.5) % 16];
$t = $distance . ' ';
$t.= ($distance > 1) ? "Miles" : "Mile";
$t.= " $direction ";
$META['city'] = $t . $META['city'];
}
$META['point'] = $ourPoint;
$META['forecastOfficeURL'] = $PJSON['forecastOffice'];
$META['forecastURL'] = $PJSON['forecast'];
$META['forecastZoneURL'] = $PJSON['forecastZone'];
$META['forecastZone'] = substr($META['forecastZoneURL'], strrpos($META['forecastZoneURL'], '/') + 1);
$META['forecastHourlyURL'] = $PJSON['forecastHourly'];
$META['forecastGridDataURL'] = $PJSON['forecastGridData'];
$META['observationStationsURL'] = $PJSON['observationStations'];
$META['countyZoneURL'] = $PJSON['county'];
$META['countyZone'] = substr($META['countyZoneURL'], strrpos($META['countyZoneURL'], '/') + 1);
$META['fireWeatherZoneURL'] = $PJSON['fireWeatherZone'];
$META['fireWeatherZone'] = substr($META['fireWeatherZoneURL'], strrpos($META['fireWeatherZoneURL'], '/') + 1);
$META['timeZone'] = $PJSON['timeZone'];
$META['radarStation'] = $PJSON['radarStation'];
$saveNew = true;
if ($doDebug) {
$Status.= "<!-- META data\n" . print_r($META, true) . " -->\n";
}
}
}
else { // JSON ERROR
$Status.= "<!-- JSON ERROR with Point metadata content='" . print_r($content, true) . " -->\n";
}
}
if (!isset($META['zoneName'])) { // no zone data.. load it
$Status.= "<!-- getting metadata for forecast zone from $metaZoneURL -->\n";
$rawhtml = ADV_fetchUrlWithoutHanging($metaZoneURL);
$stuff = explode("\r\n\r\n",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
$rawPJSON = json_decode($content, true); // parse the JSON into an associative array
$PJSON = $rawPJSON['properties']; // geojson format
if (json_last_error() == JSON_ERROR_NONE) { // got the point METADATA.. stuff it away
if ($doDebug) {
$Status.= "<!-- zone raw data\n" . print_r($PJSON, true) . " -->\n";
}
if (isset($PJSON['name'])) {
$META['zoneName'] = $PJSON['name'];
$saveNew = true;
}
}
else { // JSON ERROR
$Status.= "<!-- JSON ERROR with Zone metadata content='" . print_r($content, true) . " -->\n";
}
}
if (!isset($META['WFOemail']) and isset($META['forecastOfficeURL'])) {
// GET the WFO data
$WFOmetaURL = $META['forecastOfficeURL'];
$WFOmetaURL = str_replace('http://', 'https://', $WFOmetaURL);
$Status.= "<!-- getting metadata for forecast office from $WFOmetaURL -->\n";
$rawhtml = ADV_fetchUrlWithoutHanging($WFOmetaURL);
$stuff = explode("\r\n\r\n",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
$PJSON = json_decode($content, true); // parse the JSON into an associative array
if (json_last_error() == JSON_ERROR_NONE) { // got the point METADATA.. stuff it away
if ($doDebug) {
$Status.= "<!-- WFO raw data\n" . var_export($PJSON, true) . " -->\n";
}
if (isset($PJSON['name'])) {
$META['WFOname'] = $PJSON['name'];
if (isset($PJSON['address']['addressLocality']) and stripos($META['WFOname'], $PJSON['address']['addressLocality']) === false) {
$META['WFOname'] = str_replace(',', '/' . $PJSON['address']['addressLocality'] . ',', $META['WFOname']);
}
if (isset($PJSON['telephone'])) {
$META['WFOphone'] = trim($PJSON['telephone']);
}
if (isset($PJSON['email'])) {
$META['WFOemail'] = trim($PJSON['email']);
}
$saveNew = true;
}
}
else { // JSON ERROR
$Status.= "<!-- JSON ERROR with WFO metadata content='" . print_r($content, true) . " -->\n";
}
}
if ($doDebug) {
$Status.= "<!-- final META data\n" . var_export($META, true) . "\n saveNew=$saveNew -->\n";
}
if ($saveNew) { // save the file if we changed anything
$outJSON = json_encode($META);
$fp = fopen($metaCache, "w");
if ($fp) {
$write = fputs($fp, $outJSON);
fclose($fp);
$Status.= "<!-- wrote meta cache file $metaCache -->\n";
}
else {
$Status.= "<!-- unable to write meta cache file $metaCache -->\n";
}
}
$Status.= "<!-- META\n" . var_export($META, true) . " -->\n";
return $META;
}
// ------------------------------------------------------------------------------------------
function get_gridpoint_data($mainCache, $gridpointURL, $forceFlag, $refreshTime)
{
//
//
global $Status, $doDebug;
// get and cache the gridpoint information
//
$gpCache = str_replace('-geojson.txt', '-geojson-gridpoint.txt', $mainCache);
$gpURL = str_replace('http://', 'https://', $gridpointURL);
$Status.= "<!-- gridpoint data cache= '$gpCache' from '$gpURL' -->\n";
if ($forceFlag > 0 or !file_exists($gpCache) or (file_exists($gpCache) and filemtime($gpCache) + $refreshTime < time())) {
$Status.= "<!-- getting gridpoint data -->\n";
$rawhtml = ADV_fetchUrlWithoutHanging($gpURL);
$stuff = explode("\r\n\r\n",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if (strlen($content) > 500) {
$fp = fopen($gpCache, "w");
if ($fp) {
$write = fputs($fp, $content);
fclose($fp);
$Status.= "<!-- wrote " . strlen($content) . " bytes to gridpoint data cache file $gpCache -->\n";
}
else {
$Status.= "<!-- unable to write gridpoint data cache file $gpCache -->\n";
}
}
else {
$Status.= "<!-- unable to obtain gridpoint data .. headers:\n" . print_r($headers, true) . "\n -->\n";
} // end got some content
}
else {
$gpAge = time() - filemtime($gpCache);
$Status.= "<!-- gridpoint data cache is current (".sec2hmsADV($gpAge)." h:m:s old). -->\n";
} // end reload gridpoint data
} // end get_gridpoint_data
// ------------------------------------------------------------------------------------------
function get_hourly_data($mainCache, $gridpointURL, $forceFlag, $refreshTime)
{
//
//
global $Status, $doDebug;
// get and cache the gridpoint information
//
$gpCache = str_replace('-geojson.txt', '-geojson-hourly.txt', $mainCache);
$gpURL = str_replace('http://', 'https://', $gridpointURL);
$Status.= "<!-- hourly data cache= '$gpCache' from '$gpURL' -->\n";
if ($forceFlag > 0 or !file_exists($gpCache) or (file_exists($gpCache) and filemtime($gpCache) + $refreshTime < time())) {
$Status.= "<!-- getting hourly -->\n";
$rawhtml = ADV_fetchUrlWithoutHanging($gpURL);
$stuff = explode("\r\n\r\n",$rawhtml); // maybe we have more than one header due to redirects.
$content = (string)array_pop($stuff); // last one is the content
$headers = (string)array_pop($stuff); // next-to-last-one is the headers
if (strlen($content) > 500) {
$fp = fopen($gpCache, "w");
if ($fp) {
$write = fputs($fp, $content);
fclose($fp);
$Status.= "<!-- wrote " . strlen($content) . " bytes to hourly data cache file $gpCache -->\n";
}
else {
$Status.= "<!-- unable to write hourly data cache file $gpCache -->\n";
}
}
else {
$Status.= "<!-- unable to obtain hourly data .. headers:\n" . print_r($headers, true) . "\n -->\n";
} // end got some content
}
else {
$gpAge = time() - filemtime($gpCache);
$Status.= "<!-- hourly data cache is current ($gpAge secs old). -->\n";
} // end reload hourly data
} // end get_hourly_data
// ------------------------------------------------------------------------------------------
function convert_filename($inURL, $NOAAZone)
{
global $Status, $metaURL, $zoneURL, $alertURL;
$fileName = $inURL;
// handle OLD formats of NWS URLS
// autocorrect the point-forecast URL if need be
/*
// from: http://forecast.weather.gov/MapClick.php?CityName=Rathdrum&state=ID&site=MTR&textField1=47.828&textField2=-116.842&e=0&TextType=2
// to:
https://api.weather.gov/points/47.82761,-116.842/forecast
NOTE: the lat,long must be decimal numbers with up-to 4 decimal places and no trailing zeroes
that's why the funky code:
$t = sprintf("%01.4f",$matches[1]); // forces 4 decimal places on number
$t = (float)$t; // trims trailing zeroes by casting to float type.
is used to enforce those API limits.
*/
if (preg_match('|textField1=|i', $fileName)) {
$newlatlong = '';
preg_match('|textField1=([\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= $t;
}
preg_match('|textField2=([-\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= ",$t";
}
$newurl = APIURL . '/points/' . $newlatlong . '/forecast';
$metaURL = APIURL . '/points/' . $newlatlong;
$pointURL = FCSTURL . '/point/' . $newlatlong;
$zoneURL = FCSTURL . '/zone/' . $NOAAZone;
// $alertURL = ALERTAPIURL.$NOAAZone;
$alertURL = ALERTAPIURL . $newlatlong;
return (array(
$newurl,
$pointURL
));
}
/*
// from:
http://forecast.weather.gov/MapClick.php?lat=38.36818&lon=-75.5976&unit=0&lg=english&FcstType=text&TextType=2
// to:
https://api.weather.gov/points/38.36818,-75.5976/forecast
*/
if (preg_match('|lat=|i', $fileName)) {
$newlatlong = '';
preg_match('|lat=([\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= $t;
}
preg_match('|lon=([-\d\.]+)|i', $fileName, $matches);
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= ",$t";
}
$newurl = APIURL . '/points/' . $newlatlong . '/forecast';
$metaURL = APIURL . '/points/' . $newlatlong;
$pointURL = FCSTURL . '/point/' . $newlatlong;
$zoneURL = FCSTURL . '/zone/' . $NOAAZone;
// $alertURL = ALERTAPIURL.$NOAAZone;
$alertURL = ALERTAPIURL . $newlatlong;
return (array(
$newurl,
$pointURL
));
}
// handle NEW format of point URL
if (preg_match('|/point/([\d\.]+),([\-\d\.]+)|i', $fileName, $matches)) {
$newlatlong = '';
if (isset($matches[1])) {
$t = sprintf("%01.4f", $matches[1]);
$t = (float)$t;
$newlatlong.= $t;
}
if (isset($matches[2])) {
$t = sprintf("%01.4f", $matches[2]);
$t = (float)$t;
$newlatlong.= ",$t";
}
$newurl = APIURL . '/points/' . $newlatlong . '/forecast';
$metaURL = APIURL . '/points/' . $newlatlong;
$pointURL = FCSTURL . '/point/' . $newlatlong;
$zoneURL = FCSTURL . '/zone/' . $NOAAZone;
// $alertURL = ALERTAPIURL.$NOAAZone;
$alertURL = ALERTAPIURL . $newlatlong;
return (array(
$newurl,
$pointURL
));
}
return (array(
'unk',
$inURL
));
}
// ------------------------------------------------------------------------------------------
function sec2hmsADV($sec, $padHours = false)
{
$hms = "";
if (!is_numeric($sec)) {
return ($sec);
}
// there are 3600 seconds in an hour, so if we
// divide total seconds by 3600 and throw away
// the remainder, we've got the number of hours
$hours = intval(intval($sec) / 3600);
// add to $hms, with a leading 0 if asked for
$hms.= ($padHours) ? str_pad($hours, 2, "0", STR_PAD_LEFT) . ':' : $hours . ':';
// dividing the total seconds by 60 will give us
// the number of minutes, but we're interested in
// minutes past the hour: to get that, we need to
// divide by 60 again and keep the remainder
$minutes = intval(fmod($sec / 60, 60));
// then add to $hms (with a leading 0 if needed)
$hms.= str_pad($minutes, 2, "0", STR_PAD_LEFT) . ':';
// seconds are simple - just divide the total
// seconds by 60 and keep the remainder
$seconds = intval(fmod($sec , 60));
// add to $hms, again with a leading 0 if needed
$hms.= str_pad($seconds, 2, "0", STR_PAD_LEFT);
// done!
return $hms;
} // end sec2hmsADV function
// ------------------------------------------------------------------------------------------
function gen_new_settings()
{
global $fileName, $NOAAZone, $NWSforecasts, $SITE, $Version, $showTwoIconRows, $showZoneWarning;
list($newAPI, $newFileName) = convert_filename($fileName, $NOAAZone);
$newNWSforecasts = array();
if (!isset($NWSforecasts)) {
$NWSforecasts = array();
$newZone = $NOAAZone;
}
foreach($NWSforecasts as $m => $rec) { // for each locations
list($Nzone, $Nlocation, $Nname) = explode('|', $NWSforecasts[$m] . '|||');
list($newAPI, $newName) = convert_filename($Nname, $Nzone);
if ($m == 0) {
$newFileName = $newName;
$newZone = $Nzone;
}
$newNWSforecasts[$m] = "$Nzone|$Nlocation|$newName";
}
header('Content-type: text/plain,charset=ISO-8859-1');
print "// $Version \n";
print "// settings converted to new point-forecast request URLs.\n";
print "// Put these in ";
print isset($SITE['noaazone']) ? 'Settings.php' : 'advforecast2.php';
print " to update the settings there.\n";
print "\n";
print "// -- start of converted settings for advforecast2.php JSON version.\n";
print isset($SITE['NWSforecasts']) ? "\$SITE['NWSforecasts']" : "\$NWSforecasts";
print " = array(\n";
print "// the entries below are for testing use.. replace them with your own entries if using the script\n";
print "// outside the Saratoga AJAX/PHP templates.\n";
print "// ZONE|Location|point-forecast-URL (separated by | characters\n";
foreach($newNWSforecasts as $m => $rec) {
print " '$rec',\n";
}
print "\n);\n";
print "//\n";
print isset($SITE['noaazone']) ? "\$SITE['noaazone']" : "\$NOAAZone";
print " = '$newZone'; // change this line to your NOAA warning zone.\n";
print "//\n";
print "// set ";
print isset($SITE['fcsturlNWS']) ? "\$SITE['fcsturlNWS']" : "\$fileName";
print " to the URL for the point-printable forecast for your area\n";
print "// NOTE: this value (and ";
print isset($SITE['noaazone']) ? "\$SITE['noaazone']" : "\$NOAAZone";
print ") will be overridden by the first entry in ";
print isset($SITE['NWSforecasts']) ? "\$SITE['NWSforecasts']" : "\$NWSforecasts";
print " if it exists.\n";
print "//\n";
print isset($SITE['fcsturlNWS']) ? "\$SITE['fcsturlNWS']" : "\$fileName";
print " = '$newFileName';\n";
print "//\n";
print isset($SITE['noaazone']) ? "\$SITE['showTwoIconRows']" : "\$showTwoIconRows";
print " = ";
print $showTwoIconRows ? 'true;' : 'false;';
print " // =true; show all icons, =false; show 9 icons in one row (new V5.00)\n";
print "//\n";
print isset($SITE['noaazone']) ? "\$SITE['showZoneWarning']" : "\$showZoneWarning";
print " = ";
print $showZoneWarning ? 'true;' : 'false;';
print " // =true; show note when Zone forecast used. =false; suppress Zone warning (new V5.00)\n";
print "//\n";
print "// -- end of converted settings for advforecast2.php JSON version.\n";
} // end gen_new_settings function
// end of advforecast2.php script
Function Calls
file_exists | 1 |
Stats
MD5 | 0b2ace63cfc8cbdb05d15494e51544d2 |
Eval Count | 0 |
Decode Time | 450 ms |