$message]); exit; } /** * Send JSON success response */ function send_json($data) { header('Content-Type: application/json'); echo json_encode($data); exit; } /** * Send GeoJSON response */ function send_geojson($features) { $geojson_output = ['type' => 'FeatureCollection', 'features' => $features]; header('Content-Type: application/geo+json'); echo json_encode($geojson_output); exit; } // ============================================================================ // MAIN ROUTING AND REQUEST HANDLING // ============================================================================ // Set common headers header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type'); // Handle OPTIONS request for CORS if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { exit(0); } // Connect to database $dbconn = connect_db(); // ============================================================================ // POST REQUEST HANDLING // ============================================================================ if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Handle mp4.php POST request for GIF creation if (isset($_POST['data']) && isset($_POST['images'])) { handle_mp4_request(); } // Handle update_field.php POST request if (isset($_POST['camid']) && isset($_POST['field'])) { handle_update_field(); } // Handle admin.php POST requests if (isset($_POST['action'])) { handle_admin_post(); } // Handle stormdata.php POST requests $input_data = json_decode(file_get_contents('php://input'), true); if ($input_data && isset($input_data['request_type'])) { handle_stormdata_post($input_data); } exit; } // ============================================================================ // GET REQUEST HANDLING // ============================================================================ // Get all query parameters $action = $_GET['action'] ?? ''; $camid = $_GET['camid'] ?? ''; $endpoint = $_GET; // Route based on parameters if (empty($_GET)) { // Default behavior - could serve a basic API info send_json([ 'message' => 'NWS API Main Endpoint', 'version' => '1.0', 'endpoints' => [ 'single' => '?camid={id}', 'cam' => 'Get active cameras', 'camlist' => 'Get camera list', 'power' => 'Get power outages', 'admin' => '?action={checkurl|newcam}', // Add more endpoints as needed ] ]); } // ============================================================================ // SPECIFIC ENDPOINT HANDLERS // ============================================================================ // single.php - Get single camera info if (isset($_GET['camid']) && !isset($_GET['action'])) { handle_single_camera(); } // admin.php - Admin functions if (isset($_GET['action'])) { handle_admin_get(); } // cam.php - Get active cameras if (isset($_GET['cams']) || (empty($_GET) && count($_GET) === 0)) { handle_active_cameras(); } // camlist.php - Get camera list if (isset($_GET['camlist'])) { handle_camera_list(); } // camapi.php - Camera API with various filters if (isset($_GET['cams']) || isset($_GET['camstatic']) || isset($_GET['camdb'])) { handle_camera_api(); } // camcircle.php - Camera circles if (isset($_GET['camcircle'])) { handle_camera_circles(); } // camobs.php - Camera observations if (isset($_GET['camstatic'])) { handle_camera_observations(); } // db.php - Database queries if (isset($_GET['outside']) || isset($_GET['outsideold'])) { handle_db_queries(); } // fire.php - Fire data if (isset($_GET['fire']) || (empty($_GET) && in_array('fire.php', get_included_files()))) { handle_fire_data(); } // individualcam.php - Individual camera images if (isset($_GET['camid']) && (isset($_GET['dtg']) || isset($_GET['camimages']))) { handle_individual_camera(); } // lsr.php - Local storm reports if (isset($_GET['ohgo']) || isset($_GET['ohgotable']) || isset($_GET['vtec']) || isset($_GET['reports']) || isset($_GET['outages']) || isset($_GET['verify']) || isset($_GET['stats']) || isset($_GET['metars']) || isset($_GET['news']) || isset($_GET['news2']) || isset($_GET['news3']) || isset($_GET['newsarchive']) || isset($_GET['wv511']) || isset($_GET['ky511']) || isset($_GET['getCombinedTable']) || isset($_GET['updater'])) { handle_lsr_requests(); } // nws.php - NWS data if (isset($_GET['officestats']) || isset($_GET['regionstats']) || isset($_GET['drilldown'])) { handle_nws_data(); } // ohgo.php - OHGO data if (isset($_GET['ohgo']) && !isset($_GET['action'])) { handle_ohgo_data(); } // power.php - Power data if (isset($_GET['power']) && !isset($_GET['action'])) { handle_power_data(); } // powerapi.php - Power API if (isset($_GET['states']) || isset($_GET['max']) || isset($_GET['county']) || isset($_GET['countyarchive']) || isset($_GET['archivepoint']) || isset($_GET['svr']) || isset($_GET['svrpolys']) || isset($_GET['powerids']) || isset($_GET['poweridsgeojson']) || isset($_GET['polygongeojson'])) { handle_power_api(); } // searchapi.php - Search API if (isset($_GET['county']) || isset($_GET['countyarchive']) || isset($_GET['archivepoint']) || isset($_GET['svr'])) { handle_search_api(); } // stormdata.php - Storm data (POST handled above) if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['storm'])) { handle_stormdata_get(); } // ver.php - Verification data if (isset($_GET['lsrslist']) || isset($_GET['reset']) || isset($_GET['lsrs']) || isset($_GET['inc']) || isset($_GET['hide'])) { handle_verification_data(); } // warntrack.php - Warning tracking if (isset($_GET['warntrack'])) { handle_warning_tracking(); } // Close database connection pg_close($dbconn); // ============================================================================ // FUNCTION IMPLEMENTATIONS // ============================================================================ /** * Handle single camera request (from single.php) */ function handle_single_camera() { global $dbconn; $camid = $_GET['camid']; $query = "SELECT *, COALESCE(hydro, false) as hydro, COALESCE(airport, false) as airport FROM cams WHERE camid = $1"; $result = pg_query_params($dbconn, $query, array($camid)) or die('Query failed: ' . pg_last_error()); $array = array(); while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $line['hydro'] = ($line['hydro'] === 't' || $line['hydro'] === true); $line['airport'] = ($line['airport'] === 't' || $line['airport'] === true); $array[] = $line; } pg_free_result($result); send_json($array); } /** * Handle active cameras request (from cam.php) */ function handle_active_cameras() { global $dbconn; $query = "SELECT cwa,lat,lon,lastimage,county,elevation,camid,state,description,hydro,airport FROM cams WHERE active <> false AND lastsuccess IS NOT NULL AND (EXTRACT(EPOCH FROM (current_timestamp - lastsuccess ))/60) < (interval + 20) order by elevation desc"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = array(); while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $line['hydro'] = ($line['hydro'] === 't' || $line['hydro'] === true); $line['airport'] = ($line['airport'] === 't' || $line['airport'] === true); $array[] = $line; } pg_free_result($result); send_json($array); } /** * Handle camera list request (from camlist.php) */ function handle_camera_list() { global $dbconn; $query = "SELECT url, lat, lon, elevation, county, state, active, aspect, bloomsky, source, method FROM cams where active = true"; $result = pg_query($dbconn, $query); if (!$result) { send_error(500, 'Query failed: ' . pg_last_error()); } $data = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $line['lat'] = floatval($line['lat']); $line['lon'] = floatval($line['lon']); $line['elevation'] = floatval($line['elevation']); $line['active'] = $line['active'] === 't' ? true : false; $data[] = $line; } $response = [ 'status' => 'success', 'data' => $data, 'count' => count($data) ]; pg_free_result($result); send_json($response); } /** * Handle camera API request (from camapi.php) */ function handle_camera_api() { global $dbconn; if (isset($_GET['cams'])) { // Handle camera bounding box query if(isset($_GET['lat1']) && isset($_GET['lon1']) && isset($_GET['lat2']) && isset($_GET['lon2']) && isset($_GET['elevbottom']) && isset($_GET['elevtop'])) { $lat1 = pg_escape_string($_GET['lat1']); $lon1 = pg_escape_string($_GET['lon1']); $lat2 = pg_escape_string($_GET['lat2']); $lon2 = pg_escape_string($_GET['lon2']); $elevbottom = pg_escape_string($_GET['elevbottom']); $elevtop = pg_escape_string($_GET['elevtop']); $result = pg_query_params($dbconn, "select camid,url,description from cams where method = 'rtsp' and active = true and cwa = 'RLX' and elevation > $5 and elevation < $6 and (EXTRACT(EPOCH FROM (current_timestamp - lastsuccess ))/60) < (interval + 20) and lat < $1 and lat > $2 and lon < $3 and lon > $4 order by elevation desc", array($lat1,$lat2,$lon1,$lon2,$elevbottom,$elevtop)) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } if (isset($_GET['camstatic'])) { if(isset($_GET['lat1']) && isset($_GET['lon1']) && isset($_GET['radius'])) { $lat1 = pg_escape_string($_GET['lat1']); $lon1 = pg_escape_string($_GET['lon1']); $radius = pg_escape_string($_GET['radius']); $rad = $radius / 70; $lat1 = floatval($lat1); $lon1 = floatval($lon1); $radius = floatval($rad); $query = "select * from cams where method = 'rtsp' and active = true and cwa = 'RLX' and (EXTRACT(EPOCH FROM (current_timestamp - lastsuccess ))/60) < (interval + 20) and st_dwithin(geom, ST_SetSRID(ST_Point(" . strval($lon1) . ", " . strval($lat1) . "), 4326)," . strval($radius) . ") order by elevation desc"; $result = pg_query($dbconn,$query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } if (isset($_GET['camdb'])) { $result = pg_query($dbconn, "SELECT COUNT(*) FROM camdb") or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } /** * Handle camera circles request (from camcircle.php) */ function handle_camera_circles() { global $dbconn; $query = "WITH subquery_points AS ( SELECT geom AS point_geometry FROM cams WHERE active = true and lastsuccess IS NOT NULL AND (EXTRACT(EPOCH FROM (current_timestamp - lastsuccess ))/60) < 60 ) SELECT jsonb_build_object( 'type', 'FeatureCollection', 'features', jsonb_agg(jsonb_build_object( 'type', 'Feature', 'geometry', ST_AsGeoJSON(ST_Buffer(point_geometry::geography, 8000))::jsonb )) ) AS feature_collection FROM subquery_points"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); if (isset($array[0]['feature_collection'])) { echo $array[0]['feature_collection']; } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } } /** * Handle camera observations request (from camobs.php) */ function handle_camera_observations() { global $dbconn; if($_GET['camstatic'] == 'radius') { if(isset($_GET['lat1']) && isset($_GET['lon1']) && isset($_GET['radius'])) { $lat1 = pg_escape_string($_GET['lat1']); $lon1 = pg_escape_string($_GET['lon1']); $radius = pg_escape_string($_GET['radius']); $rad = $radius / 70; $lat1 = floatval($lat1); $lon1 = floatval($lon1); $radius = floatval($rad); $query = "select * from cams where active = true and cwa = 'RLX' and (EXTRACT(EPOCH FROM (current_timestamp - lastsuccess ))/60) < (interval + 20) and st_dwithin(geom, ST_SetSRID(ST_Point(" . strval($lon1) . ", " . strval($lat1) . "), 4326)," . strval($radius) . ") order by elevation desc"; $result = pg_query($dbconn,$query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } if($_GET['camstatic'] == 'bbox') { if(isset($_GET['lat1']) && isset($_GET['lon1']) && isset($_GET['lat2']) && isset($_GET['lon2']) && isset($_GET['elevbottom']) && isset($_GET['elevtop'])) { $lat1 = pg_escape_string($_GET['lat1']); $lon1 = pg_escape_string($_GET['lon1']); $lat2 = pg_escape_string($_GET['lat2']); $lon2 = pg_escape_string($_GET['lon2']); $elevbottom = pg_escape_string($_GET['elevbottom']); $elevtop = pg_escape_string($_GET['elevtop']); $result = pg_query_params($dbconn, "select * from cams where active = true and cwa = 'RLX' and elevation > $5 and elevation < $6 and (EXTRACT(EPOCH FROM (current_timestamp - lastsuccess ))/60) < (interval + 20) and lat < $1 and lat > $2 and lon < $3 and lon > $4 order by elevation desc", array($lat1,$lat2,$lon1,$lon2,$elevbottom,$elevtop)) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } } /** * Handle database queries (from db.php) */ function handle_db_queries() { global $dbconn; if (isset($_GET['outside'])) { try { $query = " SELECT stationid, lat, lon, tempf, dewpt, preciptotal, winddir, windspd, windgust, elev, adm1, adm2, neighborhood, maxt, mint, pressure, lastob, county, rain24, rain3, rain6, windmax, cwa FROM ( SELECT DISTINCT ON (stationid) * FROM wusites WHERE active = TRUE AND lastob BETWEEN timezone('utc', NOW()) - INTERVAL '0.5 hours' AND timezone('utc', NOW()) ) p ORDER BY lastob DESC "; $result = pg_query($dbconn, $query); if ($result === false) { throw new Exception('Query failed: ' . pg_last_error()); } $results = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $results[] = $line; } pg_free_result($result); send_json($results); } catch (Exception $e) { send_error(500, $e->getMessage()); } } if (isset($_GET['outsideold'])) { $query = "SELECT stationid, lat, lon, tempf, dewpt,preciptotal,winddir,windspd,windgust,elev,adm1,adm2,neighborhood,maxt,mint,pressure,lastob,county,rain24,rain3,rain6,windmax,cwa FROM (SELECT DISTINCT ON (stationid) * FROM wusites WHERE (active = TRUE) and lastob BETWEEN timezone('utc', now()) - INTERVAL '.5 HOURS'AND timezone('utc', now())) p ORDER BY lastob desc;"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } /** * Handle fire data request (from fire.php) */ function handle_fire_data() { global $dbconn; $result = pg_query_params($dbconn, "SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type','Feature', 'geometry', ST_AsGeoJSON(geom)::json,'properties',json_build_object('incname',incname,'discovery',discovery,'modified',modified,'age',age,'dailyacres',dailyacres,'type',type,'contained',contained,'personnel',personnel))order by modified asc)) FROM fire where type = $1 and contained <> 100 and modified > now() - interval '36 hours'", array('WF')) or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); if (isset($resultArray[0]['json_build_object'])) { echo($resultArray[0]['json_build_object']); } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } } /** * Handle individual camera images request (from individualcam.php) */ function handle_individual_camera() { global $dbconn; $camid = pg_escape_string($_GET['camid'] ?? null); $camimages = $_GET['camimages'] ?? 20; if(isset($_GET['dtg'])) { $endtime = pg_escape_string($_GET['dtg']); $query = "SELECT camid, filepath, date_trunc('second', dateutc) as dateutc FROM camdb where camid = '{$camid}' and dateutc < '{$endtime}' order by dateutc desc limit '{$camimages}'"; } else { $query = "SELECT camid, filepath, date_trunc('second', dateutc) as dateutc FROM camdb where camid = '{$camid}' order by dateutc desc limit '{$camimages}'"; } $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } /** * Handle MP4/GIF creation request (from mp4.php) */ function handle_mp4_request() { ini_set('display_errors', 'off'); $elements = $_POST['data']; $numimages = $_POST['images']; $delay = $_POST['delay']; $lastdelay = $_POST['lastdelay']; $maxh = $_POST['maxh'] ?? 500; $maxv = $_POST['maxv'] ?? 400; if (! is_numeric($maxh)) { $maxh = 500; } if (! is_numeric($maxv)) { $maxv = 400; } $numimages = $numimages - 1; $inputfiles = ""; foreach ($elements as $value) { if ($value != $elements[array_key_last($elements)]) { $inputfiles = $inputfiles . " -delay {$delay} {$value}"; } if ($value == $elements[array_key_last($elements)]) { $inputfiles = $inputfiles . " -delay {$lastdelay} {$value}"; } } $gif = shell_exec("convert {$inputfiles} -resize {$maxh}x{$maxv}\> -layers Optimize gif:-"); echo base64_encode($gif); exit; } /** * Handle update field request (from update_field.php) */ function handle_update_field() { global $dbconn; $camid = $_POST['camid']; $field = $_POST['field']; $value = $_POST['value']; // Validate inputs if (empty($camid) || empty($field)) { send_json(['success' => false, 'message' => 'Invalid input']); } // Check if field is valid if (!in_array($field, ['hydro', 'airport'])) { send_json(['success' => false, 'message' => 'Invalid field']); } // Convert to proper boolean for PostgreSQL $value_bool = ($value === 'true'); // Update field value in database $query = "UPDATE cams SET $field = $1 WHERE camid = $2"; $result = pg_query_params($dbconn, $query, array($value_bool ? 't' : 'f', $camid)); if ($result) { send_json(['success' => true]); } else { $error = pg_last_error($dbconn); send_json(['success' => false, 'message' => $error]); } } /** * Handle admin GET requests (from admin.php) */ function handle_admin_get() { global $dbconn; $action = $_GET['action']; if ($action == 'checkurl') { $url = $_POST['url']; $query = "SELECT exists (SELECT 1 FROM cams WHERE url = '{$url}')"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } /** * Handle admin POST requests (from admin.php) */ function handle_admin_post() { global $dbconn; $action = $_POST['action']; if ($action == 'checkurl') { $url = $_POST['url']; $query = "SELECT exists (SELECT 1 FROM cams WHERE url = '{$url}')"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } if ($action == 'newcam') { $url = $_POST['url']; $lat = $_POST['lat']; $lon = $_POST['lon']; $desc = $_POST['description']; $method = $_POST['method']; $permission = $_POST['permission']; $owner = $_POST['owner']; $email = $_POST['email']; $query = "INSERT into cams (url,lat,lon,description,interval,method,active,permission,owner,email,keephours) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)"; $result = pg_query_params($query, Array($url,$lat,$lon,$desc,'10',$method,'t',$permission,$owner,$email,'240')) or die('Query failed: ' . pg_last_error()); $status = pg_result_status($result); pg_free_result($result); shell_exec('python3 /var/www/html/work/runallgeom.py'); send_json($status); } } /** * Handle LSR requests (from lsr.php) - simplified version */ function handle_lsr_requests() { global $dbconn; // Handle OHGO GeoJSON if (isset($_GET['ohgo'])) { $query = "SELECT jsonb_build_object( 'type', 'FeatureCollection', 'features', jsonb_agg( jsonb_build_object( 'type', 'Feature', 'geometry', ST_AsGeoJSON(geom)::jsonb, 'properties', jsonb_build_object( 'lat', lat, 'lon', lon, 'description', description, 'roadstatus', roadstatus, 'start', start, 'lastupdate', lastupdate ) ) ) ) as geojson FROM public.ohgo WHERE endtime IS NULL AND lastupdate > NOW() - INTERVAL '2 hours'"; $result = pg_query($dbconn); if (!$result) { send_error(500, 'Query failed: ' . pg_last_error()); } $resultArray = pg_fetch_all($result); if ($resultArray && isset($resultArray[0]['geojson'])) { echo $resultArray[0]['geojson']; } else { send_json(['error' => 'No results found']); } pg_free_result($result); exit; } // Handle OHGO table if (isset($_GET['ohgotable'])) { $query = "SELECT CASE WHEN COALESCE(lsr, FALSE) THEN 'true' ELSE 'false' END AS lsr, CASE WHEN COALESCE(hide, FALSE) THEN 'true' ELSE 'false' END AS hide, ROUND(ST_Y(geom)::numeric, 3) AS lat, ROUND(ST_X(geom)::numeric, 3) AS lon, id, category, roadstatus, cwa, county, state, location, routename, description, TO_CHAR(start, 'YYYY-MM-DD HH24:MI') AS start, TO_CHAR(endtime, 'YYYY-MM-DD HH24:MI') AS endtime, TO_CHAR(lastupdate, 'YYYY-MM-DD HH24:MI') AS lastupdate FROM ohgo WHERE (endtime IS NULL OR endtime > NOW() - INTERVAL '48 hours') and start > now() - interval '144 hours' ORDER BY start ASC"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } // Handle VTEC query if (isset($_GET['vtec'])) { $vtec = $_GET['vtec']; $query = " SELECT json_build_object( 'type', 'FeatureCollection', 'features', json_agg( json_build_object( 'type', 'Feature', 'geometry', ST_AsGeoJSON(nwspoly)::json, 'properties', json_build_object( 'id', warnindex, 'issue', issue, 'endtime', endtime, 'warntype', warntype, 'issue', issue, 'outagesvalid', outagesvalid, 'outagesbuffer', outagesbuffer, 'polygonpop', polygonpop, 'lat', st_y(st_centroid(nwspoly)), 'lon', st_x(st_centroid(nwspoly)), 'vtec', vtec ) ) ) ) FROM svr WHERE vtec = $1; "; $result = pg_query_params($dbconn, $query, array($vtec)) or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); if (isset($resultArray[0]['json_build_object'])) { echo($resultArray[0]['json_build_object']); } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); exit; } // Handle reports query if (isset($_GET['reports'])) { $vtec = $_GET['reports']; $hours = $_GET['hours'] ?? 6; $query = "SELECT * from reports,svr where ST_Contains(svr.nwspoly, reports.geom) and vtec = $1 and reports.initialdtg AT TIME ZONE 'America/New_York' > svr.issue AND reports.initialdtg AT TIME ZONE 'America/New_York' < svr.issue + (INTERVAL '1 h' * $2)"; $result = pg_query_params($dbconn, $query, array($vtec,$hours)) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } // Handle current reports (no GET parameters) if (empty($_GET) || (count($_GET) === 1 && isset($_GET['reports']))) { $result = pg_query_params($dbconn, "SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type','Feature', 'geometry', ST_AsGeoJSON(geom)::json,'properties',json_build_object('id',id,'time',initialdtg,'county',county,'state',state,'issue',issue,'rawemail',rawemail,'place',place,'comments',comments)) order by initialdtg desc)) FROM reports where initialdtg > $1 ", array('2024-06-07')) or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); if (isset($resultArray[0]['json_build_object'])) { echo($resultArray[0]['json_build_object']); } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); exit; } } /** * Handle NWS data requests (from nws.php) - simplified version */ function handle_nws_data() { global $dbconn; if (isset($_GET['officestats'])) { $query = "SELECT * FROM nws order by lastupdate asc"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } } /** * Handle OHGO data request (from ohgo.php) */ function handle_ohgo_data() { global $dbconn; $query = "SELECT lat,lon,id,category,roadstatus,cwa,county,state,location,routename,description,lsr,date_trunc('minute', start) as start, date_trunc('minute', endtime) as endtime,date_trunc('minute', lastupdate) as lastupdate from ohgo where endtime is null or endtime > now() - interval '48 hours' order by start asc;"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } /** * Handle power data request (from power.php) */ function handle_power_data() { global $dbconn; $query = "SELECT lat,lon,outagen FROM power WHERE active = true and cwa = 'RLX'"; $result = pg_query($query) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } /** * Handle power API requests (from powerapi.php) - simplified version */ function handle_power_api() { global $dbconn; // Handle default power outage request if (empty($_GET) || (count($_GET) === 1 && isset($_GET['power']))) { $query = " SELECT json_build_object( 'type', 'FeatureCollection', 'features', json_agg( json_build_object( 'type', 'Feature', 'geometry', ST_AsGeoJSON(realgeom)::json, 'properties', json_build_object( 'time', startguess, 'county', county, 'state', state, 'outage', outagen, 'lastchange', lastchange, 'cause', cause, 'area_geometry', ST_AsGeoJSON(COALESCE(realareageom, realgeom))::json ) ) ORDER BY startguess ASC ) ) FROM power WHERE cwa = $1 AND active = true "; $result = pg_query_params($dbconn, $query, array('RLX')); if ($result === false) { send_error(500, 'Query failed: ' . pg_last_error()); } $resultArray = pg_fetch_all($result); if ($resultArray && isset($resultArray[0]['json_build_object'])) { echo $resultArray[0]['json_build_object']; } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); exit; } // Handle states request if (isset($_GET['states'])) { $query = " SELECT jsonb_build_object( 'type', 'FeatureCollection', 'features', jsonb_agg(features.feature) ) FROM ( SELECT jsonb_build_object( 'type', 'Feature', 'geometry', ST_AsGeoJSON(ST_Transform(geom, 4326))::jsonb, 'properties', to_jsonb(properties) - 'geom' ) AS feature FROM ( SELECT * FROM states WHERE state IN ('WV', 'VA', 'KY', 'MD', 'PA', 'OH') ) AS properties ) AS features "; $result = pg_query($dbconn); if ($result === false) { send_error(500, 'Query failed: ' . pg_last_error()); } $resultArray = pg_fetch_all($result); if ($resultArray && isset($resultArray[0]['jsonb_build_object'])) { echo $resultArray[0]['jsonb_build_object']; } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); exit; } // Handle county request if (isset($_GET['county'])) { $query = " SELECT DISTINCT ON (county, state) county, state, SUM(outages) as outage, update as time, SUM(served) as served, ROUND(CAST((SUM(outages)::FLOAT / SUM(served)) * 100 AS NUMERIC), 2) as perout FROM countyoutages WHERE update = (SELECT MAX(update) FROM countyoutages) AND cwa = $1 GROUP BY county, state, update "; $result = pg_query_params($dbconn, $query, ['RLX']); if ($result === false) { send_error(500, 'Query failed: ' . pg_last_error()); } $results = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $results[] = $line; } pg_free_result($result); send_json($results); } // Handle current SVR warnings if (isset($_GET['svr']) && $_GET['svr'] == 'current') { $result = pg_query($dbconn, "SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type','Feature', 'geometry', ST_AsGeoJSON(nwspoly)::json,'properties',json_build_object('issue',issue,'end',endtime,'vtec',vtec,'type',warntype)))) FROM svr where issue < now() and endtime > now()") or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); if (isset($resultArray[0]['json_build_object'])) { echo($resultArray[0]['json_build_object']); } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); exit; } } /** * Handle search API requests (from searchapi.php) */ function handle_search_api() { global $dbconn; // Handle county current if(isset($_GET['county'])) { $result = pg_query_params($dbconn, "SELECT distinct on (county,state) update as time, county, state, outages as outage,served FROM countyoutages where cwa = $1 order by county,state,update desc", array('RLX')) or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } // Handle SVR current if(isset($_GET['svr']) && $_GET['svr'] == 'current') { $result = pg_query($dbconn, "SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type','Feature', 'geometry', ST_AsGeoJSON(nwspoly)::json,'properties',json_build_object('issue',issue,'end',endtime,'vtec',vtec,'type',warntype)))) FROM svr where issue < now() and endtime > now()") or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); if (isset($resultArray[0]['json_build_object'])) { echo($resultArray[0]['json_build_object']); } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); exit; } } /** * Handle storm data POST requests (from stormdata.php) */ function handle_stormdata_post($input_data) { global $dbconn; $request_type = $input_data['request_type']; switch ($request_type) { case 'ohgo': handle_ohgo_request($dbconn, $input_data); break; case 'ohgonopoly': handle_ohgo_request_no_poly($dbconn, $input_data); break; case 'power': handle_power_request($dbconn, $input_data); break; case 'powernopoly': handle_power_request_no_poly($dbconn, $input_data); break; case 'wupoly': handle_wu_request_poly($dbconn, $input_data); break; case 'campoly': handle_cam_request($dbconn, $input_data); break; default: send_error(400, 'Invalid request_type specified: ' . htmlspecialchars($request_type)); break; } } /** * Handle verification data requests (from ver.php) */ function handle_verification_data() { global $dbconn; // Handle default request if (empty($_GET) || (count($_GET) === 1 && isset($_GET['ver']))) { $result = pg_query($dbconn, "SELECT jsonb_build_object('type', 'FeatureCollection','features', jsonb_agg(features.feature)) FROM (SELECT jsonb_build_object('type', 'Feature','geometry', ST_AsGeoJSON(ST_Transform(geom, 4326))::jsonb,'properties', to_jsonb(properties) - 'geom') AS feature FROM (SELECT *FROM pzone where cwa ='RLX') AS properties) AS features") or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); if (isset($resultArray[0]['jsonb_build_object'])) { echo($resultArray[0]['jsonb_build_object']); } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); exit; } // Handle lsrslist if (isset($_GET['lsrslist'])) { $result = pg_query($dbconn,"SELECT * from simplever") or die('Query failed: ' . pg_last_error()); $array = []; while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { $array[] = $line; } pg_free_result($result); send_json($array); } // Handle reset if (isset($_GET['reset'])) { $result = pg_query($dbconn,"truncate simplever") or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); send_json($resultArray); } // Handle lsrs if (isset($_GET['lsrs'])) { if (isset($_GET['zone'])) { $zone = $_GET['zone']; $lsr = isset($_GET['lsr']) ? (int) $_GET['lsr'] : 1; $dir = $_GET['dir'] ?? 1; if ($dir == 1) { $result = pg_query_params($dbconn,"INSERT into simplever (zone,lsr) values ($1,$2) on conflict (zone) do update set lsr = (simplever.lsr + 1) where simplever.zone = $1", array($zone,$lsr)) or die('Query failed: ' . pg_last_error()); } else { $result = pg_query_params($dbconn,"INSERT into simplever (zone,lsr) values ($1,$2) on conflict (zone) do update set lsr = 0 where simplever.zone = $1", array($zone,$lsr)) or die('Query failed: ' . pg_last_error()); } $resultArray = pg_fetch_all($result); send_json($resultArray); } } // Handle inc/hide if (isset($_GET['inc']) || isset($_GET['hide'])) { $field = isset($_GET['inc']) ? 'inc' : 'hide'; $value = $_GET[$field] ?? 'false'; $hideflag = ($value == 'true') ? 'true' : 'false'; $id = (int) $_GET['id']; $query = "UPDATE reports SET $field = $1 WHERE id = $2"; $result = pg_query_params($dbconn, $query, array($hideflag, $id)) or die('Query failed: ' . pg_last_error()); pg_free_result($result); send_json(['success' => true]); } } /** * Handle warning tracking requests (from warntrack.php) */ function handle_warning_tracking() { global $dbconn; $result = pg_query($dbconn, "SELECT json_build_object('type', 'FeatureCollection','features', json_agg(json_build_object('type','Feature', 'geometry', ST_AsGeoJSON(nwspoly)::json,'properties',json_build_object('issue',issue,'endtime',endtime,'warntype',warntype,'etin',etin,'followups',followups,'followup',followup,'canexp',canexp,'warnexpired',warnexpired,'vtectext',vtectext,'office',office))order by issue desc)) FROM warntracker WHERE office = 'KRLX' and svstype = 'NEW' and EXTRACT(EPOCH FROM (current_timestamp - endtime ))/60 < 2400") or die('Query failed: ' . pg_last_error()); $resultArray = pg_fetch_all($result); if (isset($resultArray[0]['json_build_object'])) { echo($resultArray[0]['json_build_object']); } else { send_json(['type' => 'FeatureCollection', 'features' => []]); } pg_free_result($result); } // ============================================================================ // STORMDATA HELPER FUNCTIONS (from stormdata.php) // ============================================================================ function handle_ohgo_request($dbconn, array $data): void { $start = $data['start_time'] ?? null; $geojson_str = $data['area_geojson'] ?? null; $end = $data['end_time'] ?? null; if ($start === null || $geojson_str === null || $end === null) { send_error(400, 'Missing required parameters for ohgo request: start, geojson, end'); } $geojson_obj = json_decode($geojson_str); if (json_last_error() !== JSON_ERROR_NONE) { send_error(400, 'Invalid GeoJSON provided: Not valid JSON.'); } if (!isset($geojson_obj->type) || !in_array($geojson_obj->type, ['Polygon', 'MultiPolygon'])) { send_error(400, 'Invalid GeoJSON provided: Type must be Polygon or MultiPolygon.'); } $query = "SELECT ST_AsGeoJSON(geom) AS geometry, category, roadstatus, county, state, location, routename, description, start AS start_timestamp, endtime AS end_timestamp, lastupdate FROM ohgo WHERE start > $1::timestamp AND start < $3::timestamp AND ST_Within(geom, ST_GeomFromGeoJSON($2)) ORDER BY start ASC"; $params = array($start, $geojson_str, $end); $result = pg_query_params($dbconn, $query, $params); if (!$result) { send_error(500, 'Database query failed for ohgo data.'); } $features = []; while ($line = pg_fetch_assoc($result)) { $geometry = json_decode($line['geometry']); if (json_last_error() !== JSON_ERROR_NONE) { continue; } $properties = $line; unset($properties['geometry']); $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; } pg_free_result($result); send_geojson($features); } function handle_ohgo_request_no_poly($dbconn, array $data): void { $start = $data['start_time'] ?? null; $end = $data['end_time'] ?? null; if ($start === null || $end === null) { send_error(400, 'Missing required parameters for ohgo request: start, end'); } $query = "SELECT ST_AsGeoJSON(geom) AS geometry, county, state AS st, location, routename AS city, upper(cwa) AS wfo, 'FLOOD' AS typetext, 'Department of Highways' AS source, description AS remark, TO_CHAR(start, 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') AS valid FROM ohgo WHERE start > $1::timestamp AND start < $2::timestamp AND cwa = 'RLX' ORDER BY start ASC"; $params = array($start, $end); $result = pg_query_params($dbconn, $query, $params); if (!$result) { send_error(500, 'Database query failed for ohgo data.'); } $features = []; while ($line = pg_fetch_assoc($result)) { $geometry = json_decode($line['geometry']); if (json_last_error() !== JSON_ERROR_NONE) { continue; } $properties = $line; unset($properties['geometry']); $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; } pg_free_result($result); send_geojson($features); } function handle_power_request($dbconn, array $data): void { $start = $data['start_time'] ?? null; $geojson_str = $data['area_geojson'] ?? null; $end = $data['end_time'] ?? null; $buffer_hours = $data['buffer'] ?? 0; if ($start === null || $geojson_str === null || $end === null || $buffer_hours === null) { send_error(400, 'Missing required parameters for power request: start_time, area_geojson, end_time, buffer_hours'); } if (!is_numeric($buffer_hours) || ($buffer_hours_float = floatval($buffer_hours)) < 0) { send_error(400, 'Invalid buffer_hours provided: Must be a non-negative number.'); } $buffer_hours_int = (int)$buffer_hours_float; $geojson_obj = json_decode($geojson_str); if (json_last_error() !== JSON_ERROR_NONE) { send_error(400, 'Invalid area_geojson provided: Contains invalid JSON string.'); } if (!is_object($geojson_obj) || !isset($geojson_obj->type) || !in_array($geojson_obj->type, ['Polygon', 'MultiPolygon'])) { send_error(400, 'Invalid area_geojson provided: Decoded JSON must be a Polygon or MultiPolygon object.'); } $query = "SELECT ST_AsGeoJSON(realgeom) AS geometry, derivedstart AS start_timestamp, cause, peakoutage, lastchange AS end_timestamp FROM power WHERE derivedstart >= $1::timestamp AND derivedstart < ($3::timestamp + make_interval(hours => $4::integer)) AND ST_Within(realgeom, ST_GeomFromGeoJSON($2)) ORDER BY derivedstart ASC"; $params = array( $start, $geojson_str, $end, $buffer_hours_int ); $result = pg_query_params($dbconn, $query, $params); if (!$result) { send_error(500, 'Database query failed for power data.'); } $features = []; while ($line = pg_fetch_assoc($result)) { $geometry = json_decode($line['geometry']); if (json_last_error() !== JSON_ERROR_NONE) { continue; } $properties = $line; unset($properties['geometry']); $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; } pg_free_result($result); send_geojson($features); } function handle_power_request_no_poly($dbconn, array $data): void { $start = $data['start_time'] ?? null; $end = $data['end_time'] ?? null; $outage_threshold = $data['outage_threshold'] ?? 9; $buffer_hours = $data['buffer'] ?? 0; if ($start === null || $end === null || $buffer_hours === null) { send_error(400, 'Missing required parameters for power request: start_time, end_time, buffer_hours'); } if (!is_numeric($buffer_hours) || ($buffer_hours_float = floatval($buffer_hours)) < 0) { send_error(400, 'Invalid buffer_hours provided: Must be a non-negative number.'); } $buffer_hours_int = (int)$buffer_hours_float; $outage_thresh = (float)$outage_threshold; $query = "SELECT ST_AsGeoJSON(realgeom) AS geometry, TO_CHAR(derivedstart, 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') AS valid, ('Power Outage affecting ' || peakoutage || ' customers caused by ' || COALESCE(cause, 'unknown')) AS remark, 'Utility Company' as source, 'POWER OUTAGE' as typetext, 'U' as type, (ROUND(ST_Y(realgeom)::numeric, 3) || ', ' || ROUND(ST_X(realgeom)::numeric, 3)) AS city, county as county, state as state, state as st FROM power WHERE derivedstart >= $1::timestamp AND derivedstart < ($2::timestamp + make_interval(hours => $3::integer)) and peakoutage > $4 AND ST_Within(realgeom, (SELECT geom FROM public.cwa WHERE cwa = 'RLX')) ORDER BY derivedstart ASC"; $params = array( $start, $end, $buffer_hours_int, $outage_thresh ); $result = pg_query_params($dbconn, $query, $params); if (!$result) { send_error(500, 'Database query failed for power data.'); } $features = []; while ($line = pg_fetch_assoc($result)) { $geometry = json_decode($line['geometry']); if (json_last_error() !== JSON_ERROR_NONE) { continue; } $properties = $line; unset($properties['geometry']); $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; } pg_free_result($result); send_geojson($features); } function handle_wu_request_poly($dbconn, array $data): void { $polygons = $data['polygons'] ?? []; $start_time = $data['start_time'] ?? '2025-01-01 00:00:00'; $end_time = $data['end_time'] ?? '2025-01-02 00:00:00'; if (empty($polygons)) { send_error(500, 'No polygons provided'); } $polygon_placeholders = []; $params = []; $param_index = 1; foreach ($polygons as $polygon) { $polygon_placeholders[] = "ST_GeomFromText(\$$param_index, 4326)"; $params[] = $polygon; $param_index++; } $params[] = $start_time; $params[] = $end_time; $start_time_placeholder = "\$$param_index"; $param_index++; $end_time_placeholder = "\$$param_index"; $polygon_sql = implode(', ', $polygon_placeholders); $sql = " SELECT wo.* FROM wuobs wo JOIN wusites ws ON wo.stationid = ws.stationid WHERE ws.geom && ST_Union(ARRAY[$polygon_sql])::geometry AND ST_Within(ws.geom, ST_Union(ARRAY[$polygon_sql])::geometry) AND wo.observation_time BETWEEN $start_time_placeholder AND $end_time_placeholder "; $result = pg_query_params($dbconn, $sql, $params); if ($result === false) { send_error(500, pg_last_error($dbconn)); } $results = []; while ($row = pg_fetch_assoc($result)) { $results[] = $row; } pg_free_result($result); send_json($results); } function handle_cam_request($dbconn, array $data): void { $start_time_str = $data['start_time'] ?? null; $end_time_str = $data['end_time'] ?? null; $geojson_str = $data['area_geojson'] ?? null; if ($start_time_str === null || $end_time_str === null || $geojson_str === null) { send_error(400, 'Missing required parameters for camera request: start_time, end_time, area_geojson'); } if (strtotime($start_time_str) === false) { send_error(400, 'Invalid start_time format.'); } if (strtotime($end_time_str) === false) { send_error(400, 'Invalid end_time format.'); } $geojson_obj = json_decode($geojson_str); if (json_last_error() !== JSON_ERROR_NONE) { send_error(400, 'Invalid area_geojson provided: Contains invalid JSON string.'); } if (!is_object($geojson_obj) || !isset($geojson_obj->type) || !in_array($geojson_obj->type, ['Polygon', 'MultiPolygon'])) { send_error(400, 'Invalid area_geojson provided: Decoded JSON must be a Polygon or MultiPolygon object.'); } $query = " SELECT c.*, ST_AsGeoJSON(c.geom) as geometry_geojson, COALESCE(img_agg.images, '[]'::jsonb) AS images FROM cams c LEFT JOIN ( SELECT camid, jsonb_agg( jsonb_build_object( 'timestamp', dateutc, 'url', filepath ) ORDER BY dateutc ASC ) AS images FROM camdb WHERE dateutc >= $1::timestamp AND dateutc <= $2::timestamp GROUP BY camid ) AS img_agg ON c.camid = img_agg.camid WHERE c.active = TRUE AND ST_Within(c.geom, ST_GeomFromGeoJSON($3)) ORDER BY c.camid; "; $params = array( $start_time_str, $end_time_str, $geojson_str ); $result = pg_query_params($dbconn, $query, $params); if (!$result) { send_error(500, 'Database query failed for camera data.'); } $cameras_output = []; while ($row = pg_fetch_assoc($result)) { $geometry = json_decode($row['geometry_geojson']); if (json_last_error() !== JSON_ERROR_NONE) { $geometry = null; } $images = json_decode($row['images']); if (json_last_error() !== JSON_ERROR_NONE) { $images = []; } $camera_data = $row; unset($camera_data['geometry_geojson']); unset($camera_data['geom']); $camera_data['geometry'] = $geometry; $camera_data['images'] = $images; $cameras_output[] = $camera_data; } pg_free_result($result); header('Content-Type: application/json'); echo json_encode($cameras_output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); exit; } ?>