From 4997c3d85b474f766ba1cd36f4c27e10843440b4 Mon Sep 17 00:00:00 2001 From: John Peck Date: Tue, 9 Dec 2025 14:10:02 +0000 Subject: [PATCH] separate the ones we joined --- main.php | 3266 +---------------------------------------- php/admin.php | 44 + php/camapi.php | 80 + php/camcircle.php | 36 + php/camlist.php | 57 + php/camobs.php | 72 + php/cams.php | 28 + php/common.php | 396 +++++ php/db.php | 150 ++ php/fire.php | 19 + php/individualcam.php | 45 + php/lsr.php | 769 ++++++++++ php/mp4.php | 37 + php/nws.php | 385 +++++ php/ohgo.php | 23 + php/power.php | 23 + php/powerapi.php | 684 +++++++++ php/powerapitest.php | 147 ++ php/searchapi.php | 97 ++ php/single.php | 43 + php/stormdata.php | 69 + php/update_field.php | 43 + php/ver.php | 82 ++ php/warntrack.php | 20 + stormdata_backup.html | 3081 -------------------------------------- 25 files changed, 3357 insertions(+), 6339 deletions(-) create mode 100644 php/admin.php create mode 100644 php/camapi.php create mode 100644 php/camcircle.php create mode 100644 php/camlist.php create mode 100644 php/camobs.php create mode 100644 php/cams.php create mode 100644 php/common.php create mode 100644 php/db.php create mode 100644 php/fire.php create mode 100644 php/individualcam.php create mode 100644 php/lsr.php create mode 100644 php/mp4.php create mode 100644 php/nws.php create mode 100644 php/ohgo.php create mode 100644 php/power.php create mode 100644 php/powerapi.php create mode 100644 php/powerapitest.php create mode 100644 php/searchapi.php create mode 100644 php/single.php create mode 100644 php/stormdata.php create mode 100644 php/update_field.php create mode 100644 php/ver.php create mode 100644 php/warntrack.php delete mode 100644 stormdata_backup.html diff --git a/main.php b/main.php index a6496ad..fc2e269 100644 --- a/main.php +++ b/main.php @@ -1,10 +1,10 @@ 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()); - - // Printing results in HTML - $array = array(); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - // Ensure hydro is a proper boolean - $line['hydro'] = ($line['hydro'] === 't' || $line['hydro'] === true); - // Ensure airport is a proper boolean - $line['airport'] = ($line['airport'] === 't' || $line['airport'] === true); - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); -} - -// Service: camapi - Returns camera API endpoints with filtering -elseif ($service === 'camapi') { - $dbconn = getDBConnection(); - - //cams endpoint - if (isset($_GET['cams'])) { - if($_GET['lat1']) { - $lat1 = getParam('lat1'); - if($_GET['lon1']) { - $lon1 = getParam('lon1'); - if($_GET['lat2']) { - $lat2 = getParam('lat2'); - if($_GET['lon2']) { - $lon2 = getParam('lon2'); - if($_GET['elevbottom']) { - $elevbottom = getParam('elevbottom'); - if($_GET['elevtop']) { - $elevtop = getParam('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()); - - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - } - } - } - } - } - } - - // camstatic endpoint - if (isset($_GET['camstatic'])) { - if($_GET['lat1']) { - $lat1 = getParam('lat1'); - if($_GET['lon1']) { - $lon1 = getParam('lon1'); - if($_GET['radius']) { - $radius = getParam('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()); - - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - } - } - } - - // camdb endpoint - if (isset($_GET['camdb'])) { - $result = pg_query($dbconn, - "SELECT COUNT(*) FROM camdb") or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } -} - -// Service: camlist - Returns camera list with active status -elseif ($service === 'camlist') { - header('Content-Type: application/json; charset=utf-8'); - - // Initialize response array - $response = []; - - // Database connection - try { - $dbconn = getDBConnection(); - - // Performing SQL query - $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) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - // Fetch results - $data = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - // Ensure numeric values are properly typed - $line['lat'] = floatval($line['lat']); - $line['lon'] = floatval($line['lon']); - $line['elevation'] = floatval($line['elevation']); - $line['active'] = $line['active'] === 't' ? true : false; // Convert PostgreSQL boolean - - $data[] = $line; - } - - $response = [ - 'status' => 'success', - 'data' => $data, - 'count' => count($data) - ]; - - // Free resultset - pg_free_result($result); - - } catch (Exception $e) { - http_response_code(500); - $response = [ - 'status' => 'error', - 'message' => $e->getMessage() - ]; - } - - // Output JSON - echo json_encode($response, JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK); -} - -// Service: admin - Admin operations for cameras -elseif ($service === 'admin') { - $dbconn = getDBConnection(); - - $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()); - - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - - 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); - echo json_encode($status); - pg_free_result($result); - - shell_exec('python3 /var/www/html/work/runallgeom.py'); - } -} - -// Service: camcircle - Returns camera coverage circles -elseif ($service === 'camcircle') { - $dbconn = getDBConnection(); - - // Performing SQL query - $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()); - - // Printing results in HTML - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - print_r($array[0]['feature_collection']); - - // Free resultset - pg_free_result($result); -} - -// Service: db - Returns weather station data -elseif ($service === 'db') { - $dbconn = getDBConnection(); - - //no gets, current point outage info - if (empty(array_diff_key($_GET, array('service' => '')))) { - 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 cwa = 'RLX' - 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; - } - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - if (isset($result)) { - pg_free_result($result); - } - header('Content-Type: application/json'); - http_response_code(500); - echo json_encode(['error' => $e->getMessage()]); - exit; - } - } - - 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()); - - // Printing results in HTML - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - 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; - } - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - if (isset($result)) { - pg_free_result($result); - } - header('Content-Type: application/json'); - http_response_code(500); - echo json_encode(['error' => $e->getMessage()]); - exit; - } - } -} - -// Service: fire - Returns fire information -elseif ($service === 'fire') { - $dbconn = getDBConnection(); - - //no gets, current point outage info - if(empty(array_diff_key($_GET, array('service' => '')))) { - $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); - echo($resultArray[0]['json_build_object']); - } -} - -// Service: individualcam - Returns individual camera images -elseif ($service === 'individualcam') { - $dbconn = getDBConnection(); - - $camid = getParam('camid'); - - if(getParam('dtg')){ - $endtime = getParam('dtg'); - if(isset($_GET['camimages'])){ - $camimages = $_GET['camimages']; - } - if(!isset($_GET['camimages'])){ - $camimages = 20; - } - - $query = "SELECT camid, filepath, date_trunc('second', dateutc) as dateutc FROM camdb where camid = '{$camid}' and dateutc < '{$endtime}' order by dateutc desc limit '{$camimages}'"; - } - - $camimages = 20; - - //if(!isset($_GET['dtg'])) { - if (!isset($_GET['dtg'])) { - if(isset($_GET['camimages'])){ - $camimages = $_GET['camimages']; - } - - $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()); - - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); -} - -// Service: lsr - Returns local storm reports -elseif ($service === 'lsr') { - $dbconn = getDBConnection(); - - 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'"; - - // Prepare and execute the query - $result = pg_query($dbconn, $query); - if (!$result) { - header('Content-Type: application/json'); - echo json_encode(['error' => 'Query failed: ' . pg_last_error()]); - exit; - } - - // Fetch the result - $resultArray = pg_fetch_all($result); - - // Check if we got results - if ($resultArray && isset($resultArray[0]['geojson'])) { - header('Content-Type: application/json'); - echo $resultArray[0]['geojson']; // Direct output since it's already JSON from jsonb_build_object - } else { - header('Content-Type: application/json'); - echo json_encode(['error' => 'No results found']); - } - - // Free result and close connection - pg_free_result($result); - } - - if (isset($_GET['ohgotable'])) { - // Performing SQL query - $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()); - - // Printing results in JSON - $array = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - if (isset($_GET['lsrohgo'])) { - if ($_GET['lsrohgo'] == 'true') { - $lsrflag = 'true'; - } else { - $lsrflag = 'false'; - } - $id = (int) $_GET['id']; - $query = "UPDATE ohgo SET lsr = $1 WHERE id = $2"; - $result = pg_query_params($dbconn, $query, array($lsrflag, $id)) or die('Query failed: ' . pg_last_error()); - pg_free_result($result); - } - - if (isset($_GET['ohgohide'])) { - if ($_GET['ohgohide'] == 'true') { - $lsrflag = 'true'; - } else { - $lsrflag = 'false'; - } - $id = (int) $_GET['id']; - $query = "UPDATE ohgo SET hide = $1 WHERE id = $2"; - $result = pg_query_params($dbconn, $query, array($lsrflag, $id)) or die('Query failed: ' . pg_last_error()); - pg_free_result($result); - } - - //take vtec, return start, end, polygon, outages in polygon, outages in buffer, warntype, polygon pop - 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; - "; - - // Prepare and execute the query using pg_query_params - $result = pg_query_params($dbconn, $query, array($vtec)) - or die('Query failed: ' . pg_last_error()); - - // Fetch the result - $resultArray = pg_fetch_all($result); - - // Output the JSON object - echo($resultArray[0]['json_build_object']); - - // Free result - pg_free_result($result); - } - - //Get reports pre-flagged with the vtec - if (isset($_GET['preflagreports'])) { - $vtec = $_GET['preflagreports']; - $query = "SELECT * from reports WHERE severe = $1"; - $result = pg_query_params($dbconn, $query, array($vtec)) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - - //Get reports within polygon for arbitary time after end time - if (isset($_GET['reports'])) { - $vtec = $_GET['reports']; - if (isset($_GET['hours'])) { - $hours = $_GET['hours']; - } else { - $hours = 6; - } - - //echo $hours; - - $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()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - - //Get point power outages within polygon + arb time, default 60 minutes - if (isset($_GET['outages'])) { - $vtec = $_GET['outages']; - if (isset($_GET['hours'])) { - $hours = $_GET['hours']; - } else { - $hours = 1; - } - - //echo $hours; - - $query = "SELECT power.lat,power.lon,power.peakoutage,power.cause,power.derivedstart,power.lastchange from power,svr where ST_Contains(svr.nwspoly, power.realgeom) and vtec = $1 and derivedstart > svr.issue AND derivedstart < svr.issue + (INTERVAL '1 h' * $2)"; - $result = pg_query_params($dbconn, $query, array($vtec,$hours)) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - - //no gets, current point outage info - if(empty(array_diff_key($_GET, array('service' => '')))) { - $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); - echo($resultArray[0]['json_build_object']); - pg_free_result($result); - } - - //For real time mapping - if(isset($_GET['verify'])) { - $query = "select id, lat::Numeric(16,3), lon::Numeric(16,3),issue,to_char(initialdtg, 'yyyy/mm/dd hh24:mi') as initialdtg,rawemail,concat(county,' ',state,'\n',place) as place,comments,lsr::text,severe from reports where severe is not null and processed = true order by initialdtg desc"; - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - //no gets, current point outage info - if (isset($_GET['rtcad'])) { - if (isset($_GET['hours'])) { - $hours = $_GET['hours']; - } else { - $hours = 6; - } - - $query = " - 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', utcinitialdtg, - 'county', county, - 'state', state, - 'issue', issue, - 'rawemail', rawemail, - 'place', place, - 'comments', comments - ) - ) ORDER BY initialdtg DESC - ) - ) - FROM reports - WHERE lat is not null and utcinitialdtg >= NOW() - INTERVAL '1 hour' * $1; - "; - - // Prepare and execute the query using pg_query_params - $result = pg_query_params($dbconn, $query, array($hours)) - or die('Query failed: ' . pg_last_error()); - - // Fetch the result - $resultArray = pg_fetch_all($result); - - // Output the JSON object - echo($resultArray[0]['json_build_object']); - - // Free result - pg_free_result($result); - } - - //Stats - if (isset($_GET['stats'])) { - $query = "SELECT county, state, MAX(emailtime), count(*) FROM reports where county is not null and (state = 'WV' or state = 'VA' or state = 'KY' or state = 'OH') GROUP BY county, state"; - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - //Get METAR Array for Jelly Bean - if (isset($_GET['metars'])) { - if (isset($_GET['start'])) { - $start = $_GET['start']; - } - if (isset($_GET['end'])) { - $end = $_GET['end']; - } - - $query = "SELECT icao,temp,dewp,wx,precip1,precip3,precip6,raw,obtime,stationname,lat,lon from metars where obtime - interval '45 minutes' > $1 and obtime < $2 order by lon asc"; - $result = pg_query_params($dbconn, $query, array($start,$end)) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } //echo($array); - echo json_encode($array); - pg_free_result($result); - } - - if (isset($_GET['news'])) { - //$query = "SELECT headline, summary, imageurl, source, storylink, updated from news where notrelevant is not true"; - $query = " - SELECT - *, - CASE - WHEN concat(summary, ' ', headline) ILIKE ANY (ARRAY[ - '%weather%', '%flood%', '%fire%', '%fog%', '%snow%', '%emergency%' - '%wind%', '%ice%', '%rain%', '%power%', '%explosion%', - '%drown%', '%stream%', '%river%', '%air%', '%wind%', - '%river%', '%ice%', '%creek%', '%crash%', '%thunder%', - '%fog%', '%spill%', '%pileup%', '%pile-up%', '%gust%', - '%fatal%', '%injury%', '%sleet%', '%injured%', '%frost%', - '%culvert%', '%slippery%', '%wildfire%', '%tornado%', - '%thunderstorm%', '%downburst%', '%microburst%', '%crash%', '%heatstroke%', '%derecho%' - '%lightning%', '%hypothermia%', '%slide%', '%flow%', '%ski%', '%water%', '%innundation%' - ]) THEN 2 - WHEN concat(summary, ' ', headline) ILIKE ANY (ARRAY[ - '%legislative%','%history%','%budget%','%birthday%','%banning%','%academic%','%tuna%','%Service Forecast%', '%DOGE%','%demonstrators%','%forum%','%health%','%fraud%','%birthday%', '%egg%', '%eggs%', '%collector%', '%church%', ' %crypto%' - ]) THEN 0 - ELSE 1 - END AS relevance_level - FROM news WHERE timeutc > NOW() - INTERVAL '18 hours' - ORDER BY relevance_level DESC, timeutc DESC - "; - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - if (isset($_GET['news2'])) { - $query = " - SELECT - *, - (SELECT COUNT(*) - FROM unnest(ARRAY[ - 'weather', 'flood', 'fire', 'fog', 'snow', 'emergency', - 'wind', 'ice', 'rain', 'power', 'explosion', 'warmer', 'colder', - 'drown', 'stream', 'river', 'air', 'wind', 'destroyed', 'rime', 'glaze', - 'river', 'ice', 'creek', 'crash', 'thunder', 'spinup', 'black ice', 'aircraft', - 'fog', 'spill', 'pileup', 'pile-up', 'gust', 'frozen', 'funnel', 'rainfall', - 'fatal', 'injury', 'sleet', 'injured', 'frost', 'dead', 'death', 'landslide', - 'culvert', 'slippery', 'wildfire', 'tornado', 'blizzard', 'creek', 'hail', - 'thunderstorm', 'downburst', 'microburst', 'crash', 'heatstroke', 'derecho', - 'lightning', 'hypothermia', 'slide', 'flow', 'ski', 'water', 'inundation', 'victim', - 'victims', 'flooding','flooded','snowing','freezing rain','clouds','cloud','storm' - ]) AS pattern - WHERE concat(summary, ' ', headline) ~* ('\y' || pattern || '\y')) AS match_count - FROM news - WHERE timeutc > NOW() - INTERVAL '18 hours' - ORDER BY nlpscore DESC, timeutc DESC - "; - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - if (isset($_GET['news3old'])) { - $query = " - SELECT * FROM news WHERE (timeutc > NOW() - INTERVAL '24 hours' and nlpscore > 0.1) or (timeutc > NOW() - INTERVAL '6 hours') ORDER BY nlpscore DESC, timeutc DESC"; - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - if (isset($_GET['news3'])) { - // Corrected query with NULLS LAST - $query = " - SELECT * FROM news - WHERE (timeutc > NOW() - INTERVAL '24 hours' AND impact_score > 25) - OR (timeutc > NOW() - INTERVAL '6 hours' and impact_score > 0) - ORDER BY impact_score DESC NULLS LAST, timeutc DESC limit 50"; - - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - - $array = []; // It's good practice to initialize the array - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - - echo json_encode($array); - - // Free resultset - pg_free_result($result); - } - - // --- FIXED SECTION: newsarchive --- - if (isset($_GET['newsarchive'])) { - // Initialize variables - $start = isset($_GET['start']) ? $_GET['start'] : null; - $end = isset($_GET['end']) ? $_GET['end'] : null; - $keys = isset($_GET['key']) ? $_GET['key'] : []; - // Convert keys to an array if it's a string - if (is_string($keys)) { - $keys = explode(',', $keys); - } - - $patterns = array_map(function($term) { - return trim($term); - }, $keys); - - // Handle case with no search terms - if (empty($patterns)) { - $query = "SELECT * FROM news"; - $params = []; - } else { - // Build parameter placeholders - $placeholders = []; - for ($i = 1; $i <= count($patterns); $i++) { - // FIXED: Use concatenation to create "$1::text" - $placeholders[] = '$' . $i . '::text'; - } - $placeholder_string = implode(',', $placeholders); - - $query = " - SELECT - n.*, - ( - SELECT COUNT(*) - FROM unnest(ARRAY[{$placeholder_string}]::text[]) AS pattern - WHERE concat(n.summary, ' ', n.headline) ILIKE pattern - ) AS match_count - FROM news n - WHERE concat(summary, ' ', headline) ILIKE ANY (ARRAY[{$placeholder_string}]::text[]) - "; - $params = array_map(function($term) { return "%{$term}%"; }, $patterns); - } - - // Add date filters if provided - $param_count = count($patterns); - if ($start) { - $param_count++; - // FIXED: Use concatenation instead of deprecated interpolation - $query .= " AND timeutc >= $" . $param_count; - $params[] = $start; - } - if ($end) { - $param_count++; - // FIXED: Use concatenation instead of deprecated interpolation - $query .= " AND timeutc <= $" . $param_count; - $params[] = $end; - } - - $query .= " ORDER BY match_count DESC, timeutc desc"; - - // Execute query - $result = pg_query_params($query, $params) or die('Query failed: ' . pg_last_error()); - - $array = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - - echo json_encode($array); - } - - if (isset($_GET['wv511'])) { - $query = "SELECT jsonb_build_object( - 'type', 'FeatureCollection', - 'features', jsonb_agg( - jsonb_build_object( - 'type', 'Feature', - 'geometry', ST_AsGeoJSON(geom)::jsonb, - 'properties', jsonb_build_object( - 'type', name, - 'reported', first_seen, - 'end', last_seen_in_feed, - 'county', county, - 'state', st, - 'remark', latest_description, - 'lat', st_y(st_centroid(geom)), - 'lon', st_x(st_centroid(geom)) - ) - ) - ) - ) as geojson - FROM public.wv511 - WHERE last_updated > NOW() - INTERVAL '2 hours'"; - - // Prepare and execute the query - $result = pg_query($dbconn, $query); - if (!$result) { - header('Content-Type: application/json'); - echo json_encode(['error' => 'Query failed: ' . pg_last_error()]); - exit; - } - - // Fetch the result - $resultArray = pg_fetch_all($result); - - // Check if we got results - if ($resultArray && isset($resultArray[0]['geojson'])) { - header('Content-Type: application/json'); - echo $resultArray[0]['geojson']; // Direct output since it's already JSON from jsonb_build_object - } else { - header('Content-Type: application/json'); - echo json_encode(['error' => 'No results found']); - } - - // Free result and close connection - pg_free_result($result); - } - - if (isset($_GET['ky511'])) { - $query = "SELECT jsonb_build_object( - 'type', 'FeatureCollection', - 'features', jsonb_agg( - jsonb_build_object( - 'type', 'Feature', - 'geometry', ST_AsGeoJSON(geom)::jsonb, - 'properties', jsonb_build_object( - 'reported', first_seen, - 'end', last_seen_in_feed, - 'county', county, - 'state', st, - 'remark', latest_description, - 'lat', st_y(st_centroid(geom)), - 'lon', st_x(st_centroid(geom)) - ) - ) - ) - ) as geojson - FROM ky511.ky511 - WHERE last_updated > NOW() - INTERVAL '2 hours'"; - - // Prepare and execute the query - $result = pg_query($dbconn, $query); - if (!$result) { - header('Content-Type: application/json'); - echo json_encode(['error' => 'Query failed: ' . pg_last_error()]); - exit; - } - - // Fetch the result - $resultArray = pg_fetch_all($result); - - // Check if we got results - if ($resultArray && isset($resultArray[0]['geojson'])) { - header('Content-Type: application/json'); - echo $resultArray[0]['geojson']; // Direct output since it's already JSON from jsonb_build_object - } else { - header('Content-Type: application/json'); - echo json_encode(['error' => 'No results found']); - } - - // Free result and close connection - pg_free_result($result); - } - - if (isset($_GET['getCombinedTable'])) { - // Combined SQL query using UNION ALL with CAST for 'id' - $query = " - SELECT * FROM ( - -- OHGO Query - SELECT - 'ohgo' AS source, - 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, - CAST(id AS TEXT) AS id, -- Cast id to TEXT - 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 '24 hours') AND start > now() - interval '144 hours' - - UNION ALL - - -- WV511 Query - SELECT - 'wv511' AS source, - 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, - CAST(id AS TEXT) AS id, -- Cast id to TEXT - wv511.name as category, - NULL AS roadstatus, - cwa, - county, - st as state, - 'Map Link' AS location, - NULL AS routename, - latest_description as description, - TO_CHAR(first_seen, 'YYYY-MM-DD HH24:MI') AS start, - TO_CHAR(last_seen_in_feed, 'YYYY-MM-DD HH24:MI') AS endtime, - TO_CHAR(last_updated, 'YYYY-MM-DD HH24:MI') AS lastupdate - FROM wv511 - WHERE (last_seen_in_feed IS NULL OR last_seen_in_feed > NOW() - INTERVAL '24 hours') AND first_seen > now() - interval '144 hours' - and wv511.name !~ 'Crash' and - wv511.name !~ 'Vehicle' and wv511.name !~ 'Dead Animal' and wv511.name !~ 'Debris in Roadway' and wv511.name !~ 'Congestion-Delay' and - wv511.name !~ 'Pot hole' and wv511.name !~ 'Debris On Bridge' and wv511.name !~ 'Attenuator' and wv511.name !~ 'Pedestrian' and - wv511.name !~ 'Bridge Closed' and wv511.name !~ 'Truck on escape' and wv511.name !~ 'Bridge Incident' and wv511.name !~ 'Escape Ramp' AND - wv511.name !~ 'Signal' - UNION ALL - - -- KY511 Query - SELECT - 'ky511.ky511' AS source, - 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, - CAST(id AS TEXT) AS id, -- Cast id to TEXT - 'Weather' as category, - NULL AS roadstatus, - cwa, - county, - st as state, - 'Map Link' AS location, - NULL AS routename, - latest_description as description, - TO_CHAR(first_seen, 'YYYY-MM-DD HH24:MI') AS start, - TO_CHAR(last_seen_in_feed, 'YYYY-MM-DD HH24:MI') AS endtime, - TO_CHAR(last_updated, 'YYYY-MM-DD HH24:MI') AS lastupdate - FROM ky511.ky511 - WHERE (last_seen_in_feed IS NULL OR last_seen_in_feed > NOW() - INTERVAL '24 hours') AND first_seen > now() - interval '144 hours' - ) AS combined_data - ORDER BY start ASC; - "; - - // Execute the query - $result = pg_query($dbconn, $query); - - // Set header before any output - header('Content-Type: application/json'); - - if (!$result) { - // Output error as JSON - echo json_encode(['error' => 'Combined query failed: ' . pg_last_error($dbconn)]); - // Close connection if needed - // pg_close($dbconn); - exit; - } - - // Fetch results into an array - $dataArray = []; - while ($row = pg_fetch_assoc($result)) { - $dataArray[] = $row; - } - if ($dataArray === false) { - echo json_encode(['error' => 'Failed to fetch results.']); - pg_free_result($result); - // pg_close($dbconn); - exit; - } - - // Output the combined results as JSON - echo json_encode($dataArray); - - // Free result memory - pg_free_result($result); - - // Optionally close the connection - // pg_close($dbconn); - - exit; // Stop script execution - } - - if (isset($_GET['updater'])) { - $allowedTables = ['wv511', 'ky511.ky511', 'ohgo']; - if (isset($_GET['lsr']) && isset($_GET['id']) && isset($_GET['table'])) { - // --- Handle LSR Update --- - $requestedTable = $_GET['table']; - $lsrInput = $_GET['lsr']; - $idInput = $_GET['id']; // ID validation missing here, see note below - - if (!in_array($requestedTable, $allowedTables)) { - http_response_code(400); - die('Error (LSR): Invalid table specified.'); - } - - $lsrflag = ($lsrInput === 'true') ? 'true' : 'false'; - $id = $idInput; // WARNING: ID is not validated/sanitized here for LSR! - - $tableNameEscaped = $requestedTable; - // Note: {$tableNameEscaped} is VALID complex syntax. The issue was with ${var} - $query = "UPDATE {$tableNameEscaped} SET lsr = $1 WHERE id = $2"; - $result = pg_query_params($dbconn, $query, array($lsrflag, $id)); - - if ($result) { - $affectedRows = pg_affected_rows($result); - echo "LSR Update successful for table '{$requestedTable}'. {$affectedRows} row(s) affected for ID {$id}."; - } else { - http_response_code(500); - error_log("LSR Query failed for table '{$requestedTable}', ID {$id}: " . pg_last_error($dbconn)); - die('Error: The LSR update query failed.'); - } - } else if (isset($_GET['hide']) && isset($_GET['id']) && isset($_GET['table'])) { - // --- Handle Hide Update --- - $requestedTable = $_GET['table']; - $hideInput = $_GET['hide']; - $idInput = $_GET['id']; - - if (!in_array($requestedTable, $allowedTables)) { - http_response_code(400); - die('Error (Hide): Invalid table specified.'); - } - - $hideflag = ($hideInput === 'true') ? 'true' : 'false'; - - // Use INT validation for ID here - make sure this matches your DB column type - $id = $idInput; - - $tableNameEscaped = $requestedTable; - $query = "UPDATE {$tableNameEscaped} SET hide = $1 WHERE id = $2"; - $result = pg_query_params($dbconn, $query, array($hideflag, $id)); - - if ($result) { - $affectedRows = pg_affected_rows($result); - echo "Hide Update successful for table '{$requestedTable}'. {$affectedRows} row(s) affected for ID {$id}."; - } else { - http_response_code(500); - error_log("Hide Query failed for table '{$requestedTable}', ID {$id}: " . pg_last_error($dbconn)); - die('Error: The Hide update query failed.'); - } - } else { - // --- Handle Missing Parameters Error --- - // Neither 'lsr' nor 'hide' (along with id and table) were provided correctly. - http_response_code(400); // Bad Request - // Check which parameters *are* present to give a potentially more helpful error - $missing = []; - if (!isset($_GET['id'])) $missing[] = '"id"'; - if (!isset($_GET['table'])) $missing[] = '"table"'; - if (!isset($_GET['lsr']) && !isset($_GET['hide'])) { - $missing[] = 'action ("lsr" or "hide")'; - } else if (isset($_GET['lsr']) && (!isset($_GET['id']) || !isset($_GET['table']))) { - // LSR was specified, but others missing - } else if (isset($_GET['hide']) && (!isset($_GET['id']) || !isset($_GET['table']))) { - // Hide was specified, but others missing - } - - if (!empty($missing)) { - die('Error: Missing required parameter(s): ' . implode(', ', $missing) . '. Please provide a valid action ("lsr" or "hide"), "id", and "table".'); - } else { - // Should ideally not happen with the logic above, but as a fallback: - die('Error: Invalid request parameters. Please provide action ("lsr" or "hide"), "id", and "table".'); - } - } - } -} - -// Service: nws - Returns NWS personnel stats -elseif ($service === 'nws') { - $dbconn = getDBConnection(); - - //no gets, current point outage info - if(empty(array_diff_key($_GET, array('service' => '')))) { - $query = "SELECT * FROM nws order by lastupdate asc"; - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - pg_free_result($result); - } - - if (isset($_GET['officestats11'])) { - // Get all unique lastupdate dates from the database - $date_query = "SELECT DISTINCT DATE(lastupdate) as unique_date - FROM nws - WHERE status = 'active' - AND office ~ 'WFO' - ORDER BY unique_date ASC"; // Changed from DESC to ASC - - $date_result = pg_query($dbconn, $date_query) - or die('Date query failed: ' . pg_last_error()); - - $datetime_points = []; - while ($row = pg_fetch_array($date_result, null, PGSQL_ASSOC)) { - $dt = DateTime::createFromFormat('Y-m-d', $row['unique_date']); - $dt->setTime(23, 59, 59); - $datetime_points[] = $dt->format('Y-m-d H:i:s'); - } - - pg_free_result($date_result); - - if (empty($datetime_points)) { - echo json_encode(['error' => 'No valid datetime points found in database']); - exit; - } - - // Debug: Log the datetime points - error_log("Processed datetime points: " . implode(', ', $datetime_points)); - - $query = "WITH latest_records AS ( - SELECT *, - ROW_NUMBER() OVER (PARTITION BY personid ORDER BY ABS(EXTRACT(EPOCH FROM (lastupdate - CAST($1 AS TIMESTAMP)))) ASC) AS rn - FROM nws - WHERE status = 'active' - AND lastupdate <= CAST($1 AS TIMESTAMP) + INTERVAL '1 day' - AND lastupdate >= CAST($1 AS TIMESTAMP) - INTERVAL '3 days' - AND office ~ 'WFO' - ), - otitle_counts AS ( - SELECT - office, - otitle, - COUNT(*) AS otitle_count - FROM latest_records - WHERE rn = 1 - GROUP BY office, otitle - ) - SELECT - lr.office, - COUNT(DISTINCT lr.personid) AS unique_person_count, - (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) - FROM (SELECT DISTINCT jsonb_build_object( - 'otitle', tc2.otitle, - 'count', tc2.otitle_count - ) AS json_obj - FROM otitle_counts tc2 - WHERE tc2.office = lr.office) AS subquery - ) AS title_counts_array - FROM latest_records lr - WHERE lr.rn = 1 - GROUP BY lr.office - ORDER BY unique_person_count DESC"; - - $results_array = []; - - // Execute query for each unique datetime - foreach ($datetime_points as $datetime) { - $result = pg_query_params($dbconn, $query, array($datetime)) - or die('Query failed: ' . pg_last_error()); - - $office_data = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $office_data[] = $line; - } - - // Store results with the datetime used - $results_array[] = [ - 'provided_datetime' => $datetime, - 'data' => $office_data - ]; - - pg_free_result($result); - } - - // Debug: Log before output - error_log("Final results: " . json_encode($results_array)); - - // Return JSON encoded results - echo json_encode($results_array); - } - - if (isset($_GET['officestats'])) { - if (isset($_GET['datetime'])) { - // Expecting datetime as comma-separated dates or JSON array - $input_dates = is_array($_GET['datetime']) - ? $_GET['datetime'] - : explode(',', $_GET['datetime']); - - // Process each date and set to end of day - $input_dates = array_unique($input_dates); - $input_dates = array_values($input_dates); - $datetime_points = []; - foreach ($input_dates as $date) { - $dt = DateTime::createFromFormat('m-d-Y', trim($date)); - if ($dt === false) { - error_log("Invalid date skipped: " . trim($date)); - continue; - } - $dt->setTime(23, 59, 59); - $datetime_points[] = $dt->format('Y-m-d H:i:s'); - } - - // Ensure uniqueness and reindex - $datetime_points = array_unique($datetime_points); - $datetime_points = array_values($datetime_points); - - // Debug: Log the datetime points - error_log("Processed datetime points: " . implode(', ', $datetime_points)); - - if (empty($datetime_points)) { - echo json_encode(['error' => 'No valid datetime points provided']); - exit; - } - - $query = "WITH latest_records AS ( - SELECT *, - ROW_NUMBER() OVER (PARTITION BY personid ORDER BY ABS(EXTRACT(EPOCH FROM (lastupdate - CAST($1 AS TIMESTAMP)))) ASC) AS rn - FROM nws - WHERE status = 'active' - AND lastupdate <= CAST($1 AS TIMESTAMP) + INTERVAL '1 day' - AND lastupdate >= CAST($1 AS TIMESTAMP) - INTERVAL '3 days' - AND office ~ 'WFO' - ), - otitle_counts AS ( - SELECT - office, - otitle, - COUNT(*) AS otitle_count - FROM latest_records - WHERE rn = 1 - GROUP BY office, otitle - ) - SELECT - lr.office, - COUNT(DISTINCT lr.personid) AS unique_person_count, - (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) - FROM (SELECT DISTINCT jsonb_build_object( - 'otitle', tc2.otitle, - 'count', tc2.otitle_count - ) AS json_obj - FROM otitle_counts tc2 - WHERE tc2.office = lr.office) AS subquery - ) AS title_counts_array - FROM latest_records lr - WHERE lr.rn = 1 - GROUP BY lr.office - ORDER BY unique_person_count DESC"; - - $results_array = []; - - // Execute query for each provided datetime - foreach ($datetime_points as $datetime) { - $result = pg_query_params($dbconn, $query, array($datetime)) - or die('Query failed: ' . pg_last_error()); - - $office_data = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $office_data[] = $line; - } - - // Store results with the datetime used - $results_array[] = [ - 'provided_datetime' => $datetime, - 'data' => $office_data - ]; - - pg_free_result($result); - } - - // Debug: Log before output - error_log("Final results: " . json_encode($results_array)); - - // Return JSON encoded results - echo json_encode($results_array); - } - } - - if (isset($_GET['regionstats'])) { - if (isset($_GET['datetime'])) { - // Expecting datetime as comma-separated dates or JSON array - $input_dates = is_array($_GET['datetime']) - ? $_GET['datetime'] - : explode(',', $_GET['datetime']); - - // Process each date and set to end of day - $input_dates = array_unique($input_dates); - $input_dates = array_values($input_dates); - $datetime_points = []; - foreach ($input_dates as $date) { - $dt = DateTime::createFromFormat('m-d-Y', trim($date)); - if ($dt === false) { - error_log("Invalid date skipped: " . trim($date)); - continue; - } - $dt->setTime(23, 59, 59); - $datetime_points[] = $dt->format('Y-m-d H:i:s'); - } - - // Ensure uniqueness and reindex - $datetime_points = array_unique($datetime_points); - $datetime_points = array_values($datetime_points); - - // Debug: Log the datetime points - error_log("Processed datetime points: " . implode(', ', $datetime_points)); - - if (empty($datetime_points)) { - echo json_encode(['error' => 'No valid datetime points provided']); - exit; - } - - $query = "WITH latest_records AS ( - SELECT *, - ROW_NUMBER() OVER (PARTITION BY personid ORDER BY ABS(EXTRACT(EPOCH FROM (lastupdate - CAST($1 AS TIMESTAMP)))) ASC) AS rn, - SUBSTRING(office FROM 'NWS/([EWPASC]R)') AS region - FROM nws - WHERE lastupdate <= CAST($1 AS TIMESTAMP) + INTERVAL '1 day' - INTERVAL '1 second' - AND lastupdate >= CAST($1 AS TIMESTAMP) - INTERVAL '3 days' - AND office ~ 'NWS/[EWPASC]R' - and status = 'active' - ), - otitle_counts AS ( - SELECT - region, - otitle, - COUNT(*) AS otitle_count - FROM latest_records - WHERE rn = 1 - GROUP BY region, otitle - ) - SELECT - lr.region, - COUNT(DISTINCT lr.personid) AS unique_person_count, - (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) - FROM (SELECT DISTINCT jsonb_build_object( - 'otitle', tc2.otitle, - 'count', tc2.otitle_count - ) AS json_obj - FROM otitle_counts tc2 - WHERE tc2.region = lr.region) AS subquery - ) AS title_counts_array - FROM latest_records lr - WHERE lr.rn = 1 - GROUP BY lr.region - ORDER BY unique_person_count DESC"; - - $results_array = []; - - // Execute query for each provided datetime - foreach ($datetime_points as $datetime) { - $result = pg_query_params($dbconn, $query, array($datetime)) - or die('Query failed: ' . pg_last_error()); - - $office_data = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $office_data[] = $line; - } - - // Store results with the datetime used - $results_array[] = [ - 'provided_datetime' => $datetime, - 'data' => $office_data - ]; - - pg_free_result($result); - } - - // Debug: Log before output - //error_log("Final results: " . json_encode($results_array)); - - // Return JSON encoded results - echo json_encode($results_array); - } - } - - if (isset($_GET['drilldown'])) { - if (isset($_GET['datetime'])) { - // Expecting datetime as comma-separated dates or JSON array - $input_dates = is_array($_GET['datetime']) - ? $_GET['datetime'] - : explode(',', $_GET['datetime']); - - // Process each date and set to end of day - $datetime_points = []; - foreach ($input_dates as $date) { - // Specify the exact format of your input date string - $dt = DateTime::createFromFormat('m-d-Y', trim($date)); // Adjust format as needed - if ($dt === false) { - // Handle invalid date - continue; - } - $dt->setTime(23, 59, 59); - $datetime_points[] = $dt->format('Y-m-d H:i:s'); - } - $datetime_points = array_unique($datetime_points); - $datetime_points = array_values($datetime_points); - - $query = "WITH latest_records AS ( - SELECT *, - ROW_NUMBER() OVER (PARTITION BY personid ORDER BY lastupdate DESC) AS rn - FROM nws - WHERE status = 'active' - AND lastupdate <= $1 - ), - otitle_counts AS ( - SELECT - office, - otitle, - COUNT(*) AS otitle_count - FROM latest_records - WHERE rn = 1 - GROUP BY office, otitle - ) - SELECT - lr.office, - COUNT(DISTINCT lr.personid) AS unique_person_count, - (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) - FROM (SELECT DISTINCT jsonb_build_object( - 'otitle', tc2.otitle, - 'count', tc2.otitle_count - ) AS json_obj - FROM otitle_counts tc2 - WHERE tc2.office = lr.office) AS subquery - ) AS title_counts_array - FROM latest_records lr - WHERE lr.rn = 1 - GROUP BY lr.office - ORDER BY unique_person_count DESC"; - - $results_array = []; - - // Execute query for each provided datetime - foreach ($datetime_points as $datetime) { - $result = pg_query_params($dbconn, $query, array($datetime)) - or die('Query failed: ' . pg_last_error()); - - $office_data = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $office_data[] = $line; - } - - // Store results with the datetime used - $results_array[] = [ - 'provided_datetime' => $datetime, - 'data' => $office_data - ]; - - pg_free_result($result); - } - - // Return JSON encoded results - echo json_encode($results_array); - } - } -} - -// Service: powerapi - Returns power outage information -elseif ($service === 'powerapi') { - $dbconn = getDBConnection(); - - //no gets, current point outage info - if (empty(array_diff_key($_GET, array('service' => '')))) { - try { - $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) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $resultArray = pg_fetch_all($result); - - // Check if we got results - if ($resultArray && isset($resultArray[0]['json_build_object'])) { - header('Content-Type: application/json'); - echo $resultArray[0]['json_build_object']; - } else { - echo json_encode(['type' => 'FeatureCollection', 'features' => []]); - } - - pg_free_result($result); - } catch (Exception $e) { - http_response_code(500); - die('Query execution failed: ' . $e->getMessage()); - } - } - - if (isset($_GET['states'])) { - try { - $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, $query); - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $resultArray = pg_fetch_all($result); - - // Set proper JSON header and handle output - header('Content-Type: application/json'); - if ($resultArray && isset($resultArray[0]['jsonb_build_object'])) { - echo $resultArray[0]['jsonb_build_object']; - } else { - echo json_encode(['type' => 'FeatureCollection', 'features' => []]); - } - - pg_free_result($result); - } catch (Exception $e) { - http_response_code(500); - header('Content-Type: application/json'); - echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); - exit; - } - } - - if (isset($_GET['max'])) { - if (isset($_GET['start']) && isset($_GET['end'])) { - try { - $starttime = getParam('start'); - $endtime = getParam('end'); - - $query = " - SELECT DISTINCT ON (county, state) - max(outage) as max_outage, - county, - state - FROM ( - SELECT DISTINCT ON (county, state, update) - county, - state, - SUM(outages) as outage, - update as time, - SUM(served) as served - FROM countyoutages - WHERE update > $2 - AND update < $3 - AND cwa = $1 - GROUP BY county, state, update - ) as subquery - GROUP BY county, state - "; - - $result = pg_query_params( - $dbconn, - $query, - ['RLX', $starttime, $endtime] - ); - - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $results = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $results[] = $line; - } - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - header('Content-Type: application/json'); - http_response_code(500); - echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); - exit; - } - } else { - header('Content-Type: application/json'); - http_response_code(400); - echo json_encode(['error' => 'Both start and end parameters are required']); - } - } - - if (isset($_GET['county'])) { - try { - $query = " - WITH latest_update AS ( - SELECT MAX(update) as max_update FROM countyoutages - ) - SELECT - county, - state, - SUM(outages) as outage, - MAX(update) as time, - SUM(served) as served, - ROUND( - CASE - WHEN SUM(served) > 0 THEN (SUM(outages)::FLOAT / SUM(served)) * 100 - ELSE 0 - END::NUMERIC, 2 - ) as perout - FROM countyoutages - JOIN latest_update ON countyoutages.update = latest_update.max_update - WHERE cwa = $1 - GROUP BY county, state - ORDER BY county, state; - "; - - $result = pg_query_params($dbconn, $query, ['RLX']); - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $results = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $results[] = $line; - } - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - header('Content-Type: application/json'); - http_response_code(500); - echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); - exit; - } - } - - if (isset($_GET['countyarchive'])) { - if (isset($_GET['start']) && isset($_GET['end'])) { - try { - $starttime = getParam('start'); - $endtime = getParam('end'); - - $query = " - SELECT DISTINCT ON (county, state, update) - county, - state, - SUM(outages) as outage, - update as time, - SUM(served) as served - FROM countyoutages - WHERE update > $2 - AND update < $3 - AND cwa = $1 - GROUP BY county, state, update - "; - - $result = pg_query_params($dbconn, $query, ['RLX', $starttime, $endtime]); - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $results = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $results[] = $line; - } - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - header('Content-Type: application/json'); - http_response_code(500); - echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); - if (isset($result)) { - pg_free_result($result); - } - exit; - } - } else { - header('Content-Type: application/json'); - http_response_code(400); - echo json_encode(['error' => 'Both start and end parameters are required']); - } - } - - if (isset($_GET['archivepoint'])) { - try { - if (!isset($_GET['start']) || !isset($_GET['end'])) { - throw new Exception('Both start and end parameters are required'); - } - - $starttime = getParam('start'); - $endtime = getParam('end'); - - $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 - ) - ) - ORDER BY startguess ASC - ) - ) - FROM power - WHERE cwa = $1 - AND startguess > $2 - AND lastchange < $3 - "; - - $result = pg_query_params($dbconn, $query, ['RLX', $starttime, $endtime]); - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $resultArray = pg_fetch_all($result); - - header('Content-Type: application/json'); - if ($resultArray && isset($resultArray[0]['json_build_object'])) { - echo $resultArray[0]['json_build_object']; - } else { - echo json_encode(['type' => 'FeatureCollection', 'features' => []]); - } - - pg_free_result($result); - } catch (Exception $e) { - header('Content-Type: application/json'); - $statusCode = strpos($e->getMessage(), 'required') !== false ? 400 : 500; - http_response_code($statusCode); - echo json_encode(['error' => $e->getMessage()]); - if (isset($result)) { - pg_free_result($result); - } - exit; - } - } - - if(@$_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); - echo($resultArray[0]['json_build_object']); - pg_free_result($result); - } - - if (isset($_GET['svr']) && $_GET['svr'] === 'archive') { - try { - $result = null; - - if (isset($_GET['start']) && isset($_GET['end'])) { - $starttime = getParam('start'); - $endtime = getParam('end'); - - $query = " - 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 > $1 - AND endtime < $2 - "; - - $result = pg_query_params($dbconn, $query, [$starttime, $endtime]); - } elseif (!isset($_GET['start']) && !isset($_GET['end'])) { - $query = " - 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() - INTERVAL '24 hours' - AND endtime > NOW() - INTERVAL '24 hours' - "; - - $result = pg_query($dbconn, $query); - } else { - throw new Exception('Both start and end parameters are required together'); - } - - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $resultArray = pg_fetch_all($result); - - header('Content-Type: application/json'); - if ($resultArray && isset($resultArray[0]['json_build_object'])) { - echo $resultArray[0]['json_build_object']; - } else { - echo json_encode(['type' => 'FeatureCollection', 'features' => []]); - } - - pg_free_result($result); - } catch (Exception $e) { - if (isset($result)) { - pg_free_result($result); - } - header('Content-Type: application/json'); - $statusCode = strpos($e->getMessage(), 'required') !== false ? 400 : 500; - http_response_code($statusCode); - echo json_encode(['error' => $e->getMessage()]); - exit; - } - } - - if (isset($_GET['svrpolys'])) { - try { - $query = " - SELECT - vtec, - outagesvalid, - polygonpop, - outagesbuffer, - lsrids - FROM svr - WHERE EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - endtime)) / 60 / 60 / 24 < 60 - "; - - $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; - } - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - if (isset($result)) { - pg_free_result($result); - } - header('Content-Type: application/json'); - http_response_code(500); - echo json_encode(['error' => $e->getMessage()]); - exit; - } - } - - if (isset($_GET['powerids'])) { - try { - $powerids = $_GET['powerids']; - - // Validate input exists and isn't empty - if (empty($powerids)) { - throw new Exception('No power IDs provided'); - } - - // Convert comma-separated string to array and sanitize - $poweridArray = explode(',', $powerids); - $sanitizedIds = array_filter(array_map('intval', $poweridArray)); - - if (empty($sanitizedIds)) { - throw new Exception('Invalid power ID format'); - } - - // Prepare placeholders for the query - $placeholders = implode(',', array_map(function($i) { return '$' . $i; }, range(1, count($sanitizedIds)))); - - $query = " - SELECT - lat, - lon, - lastchange, - startguess, - peakoutage, - cause, - lsrtime, - lsrref, - (lsrtime AT TIME ZONE 'America/New_York')::timestamp as lsrlocal - FROM power - WHERE id IN ($placeholders) - "; - - $result = pg_query_params($dbconn, $query, $sanitizedIds); - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $results = pg_fetch_all($result) ?: []; - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - if (isset($result)) { - pg_free_result($result); - } - header('Content-Type: application/json'); - $statusCode = strpos($e->getMessage(), 'Invalid') !== false ? 400 : 500; - http_response_code($statusCode); - echo json_encode(['error' => $e->getMessage()]); - exit; - } - } - - if (isset($_GET['poweridsgeojson'])) { - try { - $powerids = $_GET['poweridsgeojson']; - - if (empty($powerids)) { - throw new Exception('No power IDs provided'); - } - - // Convert and sanitize power IDs - $poweridArray = explode(',', $powerids); - $sanitizedIds = array_filter(array_map('intval', $poweridArray)); - - if (empty($sanitizedIds)) { - throw new Exception('Invalid power ID format'); - } - - // Prepare placeholders - $placeholders = implode(',', array_map(function($i) { return '$' . $i; }, range(1, count($sanitizedIds)))); - - $query = " - SELECT json_build_object( - 'type', 'FeatureCollection', - 'features', json_agg( - json_build_object( - 'type', 'Feature', - 'geometry', ST_AsGeoJSON(realgeom)::json, - 'properties', json_build_object( - 'id', id, - 'time', (startguess AT TIME ZONE 'UTC')::timestamp, - 'county', county, - 'state', state, - 'cause', cause, - 'outage', peakoutage, - 'lsrtime', (lsrtime AT TIME ZONE 'UTC')::timestamp - ) - ) ORDER BY startguess ASC - ) - ) - FROM power - WHERE id IN ($placeholders) - "; - - $result = pg_query_params($dbconn, $query, $sanitizedIds); - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $resultArray = pg_fetch_all($result); - - header('Content-Type: application/json'); - if ($resultArray && isset($resultArray[0]['json_build_object'])) { - echo $resultArray[0]['json_build_object']; - } else { - echo json_encode(['type' => 'FeatureCollection', 'features' => []]); - } - - pg_free_result($result); - } catch (Exception $e) { - if (isset($result)) { - pg_free_result($result); - } - header('Content-Type: application/json'); - $statusCode = strpos($e->getMessage(), 'Invalid') !== false ? 400 : 500; - http_response_code($statusCode); - echo json_encode(['error' => $e->getMessage()]); - exit; - } - } - - if (isset($_GET['polygongeojson'])) { - $result = null; // Initialize result to null for catch block safety - try { - $polygonGeoJsonString = $_GET['polygongeojson']; - - if (empty($polygonGeoJsonString)) { - throw new Exception('No GeoJSON polygon provided', 400); // Use exception code for status - } - - // 1. Validate if the input is valid JSON - // We decode here primarily to check JSON validity. - // We'll pass the *original string* to PostGIS's ST_GeomFromGeoJSON for robustness. - $polygonGeoJson = json_decode($polygonGeoJsonString); - if (json_last_error() !== JSON_ERROR_NONE) { - throw new Exception('Invalid JSON format: ' . json_last_error_msg(), 400); - } - - // 2. Optional: Basic structural validation (can rely on PostGIS for full validation) - if (!is_object($polygonGeoJson) || !isset($polygonGeoJson->type) || !in_array($polygonGeoJson->type, ['MultiPolygon', 'Polygon'])) { - // Allow both Polygon and MultiPolygon for flexibility? Or stick to MultiPolygon? - // Let's allow Polygon too, as ST_Within works with both. - // If you strictly need *only* MultiPolygon, change the check. - throw new Exception('Input GeoJSON must be of type Polygon or MultiPolygon.', 400); - } - if (!isset($polygonGeoJson->coordinates) || !is_array($polygonGeoJson->coordinates)) { - throw new Exception('Input GeoJSON must have a coordinates array.', 400); - } - - // 3. Prepare the PostgreSQL Query using PostGIS functions - // - ST_GeomFromGeoJSON($1): Parses the input GeoJSON string. - // - ST_SetSRID(..., 4326): Assigns the WGS84 SRID (standard for GeoJSON). Adjust if your data uses a different SRID. - // - ST_Within(realgeom, ...): Checks if the power outage geometry is within the provided polygon geometry. - // - Ensure your 'realgeom' column has a spatial index for performance! - $query = " - SELECT json_build_object( - 'type', 'FeatureCollection', - 'features', json_agg( - json_build_object( - 'type', 'Feature', - 'geometry', ST_AsGeoJSON(realgeom)::json, - 'properties', json_build_object( - 'id', id, - 'time', (startguess AT TIME ZONE 'UTC')::timestamp, - 'county', county, - 'state', state, - 'cause', cause, - 'outage', peakoutage, - 'lsrtime', (lsrtime AT TIME ZONE 'UTC')::timestamp - ) - ) ORDER BY startguess ASC -- Optional ordering - ) - ) - FROM power - WHERE ST_Within(realgeom, ST_SetSRID(ST_GeomFromGeoJSON($1), 4326)) - "; - // Note: If 'realgeom' might be NULL, you might add "AND realgeom IS NOT NULL" - - // 4. Execute the query with the GeoJSON string as a parameter - $params = [$polygonGeoJsonString]; - $result = pg_query_params($dbconn, $query, $params); - - if ($result === false) { - // Check for specific PostGIS errors related to invalid GeoJSON input - $pgError = pg_last_error($dbconn); - if (strpos($pgError, 'invalid GeoJSON representation') !== false || strpos($pgError, 'ParseException') !== false || strpos($pgError, 'Invalid polygon') !== false) { - throw new Exception('Invalid GeoJSON geometry data provided: ' . $pgError, 400); - } else { - // Throw a generic server error for other query failures - throw new Exception('Query failed: ' . $pgError, 500); - } - } - - // 5. Fetch and Output Results - $resultArray = pg_fetch_all($result); - - header('Content-Type: application/json'); - if ($resultArray && isset($resultArray[0]['json_build_object'])) { - // Ensure null result from json_agg (no features found) returns empty array - $outputJson = $resultArray[0]['json_build_object']; - $outputData = json_decode($outputJson, true); - if (isset($outputData['features']) && $outputData['features'] === null) { - $outputData['features'] = []; - echo json_encode($outputData); - } else { - echo $outputJson; // Output the JSON directly from Postgres - } - } else { - // Should ideally be handled by the check above, but as a fallback - echo json_encode(['type' => 'FeatureCollection', 'features' => []]); - } - - pg_free_result($result); - - } catch (Exception $e) { - // 6. Error Handling - if (isset($result) && is_resource($result)) { // Check if $result is a valid resource before freeing - pg_free_result($result); - } - header('Content-Type: application/json'); - // Use exception code for status if provided (>=400), default to 500 - $statusCode = ($e->getCode() >= 400 && $e->getCode() < 600) ? $e->getCode() : 500; - http_response_code($statusCode); - echo json_encode(['error' => $e->getMessage()]); - exit; // Stop script execution after error - } - } -} - -// Service: searchapi - Returns search results for power outages -elseif ($service === 'searchapi') { - $dbconn = getDBConnection(); - - //no gets, current point outage info - if(empty(array_diff_key($_GET, array('service' => '')))) { - $result = pg_query_params($dbconn, - "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))order by startguess asc)) FROM power WHERE cwa = $1 and active = true", - array('RLX')) or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - - //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()); - - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - } - - //county archive - if(isset($_GET['countyarchive'])) { - if(isset($_GET['start'])) { - $starttime = getParam('start'); - if(isset($_GET['end'])) { - $endtime = getParam('end'); - - $result = pg_query_params($dbconn, - "SELECT county,state, update as time, county, state, outages as outage,served FROM countyoutages where cwa = $1 and update > $2 and update < $3 order by update asc", - array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); - - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - } - } - } - - //Archive point data - if(isset($_GET['archivepoint'])) { - if(isset($_GET['start'])) { - $starttime = getParam('start'); - if(isset($_GET['end'])) { - $endtime = getParam('end'); - $result = pg_query_params($dbconn, - "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))order by startguess asc)) FROM power WHERE cwa = $1 and startguess > $2 and lastchange < $3" - ,array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); - - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - } - } - - 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); - echo($resultArray[0]['json_build_object']); - } - - if(isset($_GET['svr']) && $_GET['svr'] === 'archive') { - if(isset($_GET['start'])) { - $starttime = getParam('start'); - if(isset($_GET['end'])) { - $endtime = getParam('end'); - - $result = pg_query_params($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 > $1 and endtime < $2" - ,array($starttime,$endtime)) or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - } - - if(!isset($_GET['start']) && !isset($_GET['end'])) { - $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() - interval '24 hours' and endtime > now() - interval '24 hours'") or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - } - - pg_free_result($result); -} - -// Service: ohgo - Returns Ohio traffic information -elseif ($service === 'ohgo') { - $dbconn = getDBConnection(); - - // Performing SQL query - $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()); - - // Printing results in HTML - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); -} - -// Service: power - Returns power outage information -elseif ($service === 'power') { - $dbconn = getDBConnection(); - - // Performing SQL query - $query = "SELECT lat,lon,outagen FROM power WHERE active = true and cwa = 'RLX'"; - $result = pg_query($query) or die('Query failed: ' . pg_last_error()); - - // Printing results in HTML - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - - // Free resultset - pg_free_result($result); -} - -// Service: stormdata - Returns storm data based on POST request -elseif ($service === 'stormdata') { - $dbconn = getDBConnection(); - - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $input_data = null; - $request_type = null; - $contentType = trim(strtolower($_SERVER['HTTP_CONTENT_TYPE'] ?? $_SERVER['CONTENT_TYPE'] ?? '')); - - if (strpos($contentType, 'application/json') === 0) { - $raw_post_data = file_get_contents('php://input'); - - if ($raw_post_data === false || $raw_post_data === '') { - send_error(400, 'Received empty request body or could not read input.', "Error: Could not read php://input or it was empty."); - } - - $input_data = json_decode($raw_post_data, true); - - if (json_last_error() !== JSON_ERROR_NONE) { - send_error(400, 'Invalid JSON payload received.', 'JSON Decode Error: ' . json_last_error_msg() . " | Raw data snippet: " . substr($raw_post_data, 0, 100)); - } elseif (!is_array($input_data)) { - send_error(400, 'Invalid JSON payload: Expected a JSON object.', "JSON Decode Warning: Result is not an array. Data: " . print_r($input_data, true)); - } else { - $request_type = $input_data['request_type'] ?? null; - } - } else { - send_error(415, 'Unsupported Media Type. This endpoint requires application/json.', "Unsupported Media Type Received: " . $contentType); - } - - if ($request_type === null) { - if (is_array($input_data) && !isset($input_data['request_type'])) { - send_error(400, 'Missing "request_type" field within the request payload.'); - } else { - send_error(400, 'Missing required parameter: request_type (or processing error).'); - } - } - - 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; - } - } else { - http_response_code(405); - header('Allow: POST'); - header('Content-Type: application/json; charset=utf-8'); - echo json_encode(['error' => 'Invalid request method. Only POST is allowed.']); - exit; - } -} - -// Service: warntrack - Returns warning tracking data -elseif ($service === 'warntrack') { - $dbconn = getDBConnection(); - - //no gets, curent point outage info - if(empty(array_diff_key($_GET, array('service' => '')))) { - $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); - echo($resultArray[0]['json_build_object']); - } - - pg_free_result($result); -} - -// Service: ver - Returns version information -elseif ($service === 'ver') { - $dbconn = getDBConnection(); - - //no gets, curent point outage info - if(empty(array_diff_key($_GET, array('service' => '')))) { - $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); - header('Content-Type: application/json; charset=utf-8'); - echo($resultArray[0]['jsonb_build_object']); - pg_free_result($result); - } - - if (isset($_GET['lsrslist'])) { - $result = pg_query($dbconn,"SELECT * from simplever") or die('Query failed: ' . pg_last_error()); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - - if (isset($_GET['reset'])) { - $result = pg_query($dbconn,"truncate simplever") or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray); - } - - if (isset($_GET['lsrs'])) { - if (isset($_GET['zone'])) { - $zone = $_GET['zone']; - if (isset($_GET['lsr'])) { - $lsr = (int) $_GET['lsr']; - } else { - $lsr = 1; - } - if (isset($_GET['lsrs'])) { - $dir = $_GET['dir']; - } - - 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()); - $resultArray = pg_fetch_all($result); - } 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); - } - pg_free_result($result); - } - } - - if (isset($_GET['inc'])) { - if ($_GET['inc'] == 'true') { - $hideflag = 'true'; - } else { - $hideflag = 'false'; - } - $id = (int) $_GET['id']; - $query = "UPDATE reports SET hide = $1 WHERE id = $2"; - $result = pg_query_params($dbconn, $query, array($hideflag, $id)) or die('Query failed: ' . pg_last_error()); - pg_free_result($result); - } - - if (isset($_GET['hide'])) { - if ($_GET['hide'] == 'true') { - $hideflag = 'true'; - } else { - $hideflag = 'false'; - } - $id = (int) $_GET['id']; - $query = "UPDATE reports SET hide = $1 WHERE id = $2"; - $result = pg_query_params($dbconn, $query, array($hideflag, $id)) or die('Query failed: ' . pg_last_error()); - pg_free_result($result); - } -} - -// Service: update_field - Updates table fields -elseif ($service === 'update_field') { - $dbconn = getDBConnection(); - - // Get POST data - $camid = $_POST['camid']; - $field = $_POST['field']; - $value = $_POST['value']; - - // Validate inputs - if (empty($camid) || empty($field)) { - echo json_encode(['success' => false, 'message' => 'Invalid input']); - exit; - } - - // Check if the field is valid - if (!in_array($field, ['hydro', 'airport'])) { - echo json_encode(['success' => false, 'message' => 'Invalid field']); - exit; - } - - // Convert to proper boolean for PostgreSQL - // JavaScript sends true/false as strings 'true' or 'false' - $value_bool = ($value === 'true'); - - // Update the field value in the database - use boolean directly - // PostgreSQL accepts 't'/'f' for boolean values - $query = "UPDATE cams SET $field = $1 WHERE camid = $2"; - $result = pg_query_params($dbconn, $query, array($value_bool ? 't' : 'f', $camid)); - - if ($result) { - echo json_encode(['success' => true]); - } else { - $error = pg_last_error($dbconn); - echo json_encode(['success' => false, 'message' => $error]); - } - - // Closing connection -} - -// Service: mp4 - Generates MP4/gif from images -elseif ($service === 'mp4') { - // This service expects POST data with specific parameters - $elements = $_POST['data']; - $numimages = $_POST['images']; - $delay = $_POST['delay']; - $lastdelay = $_POST['lastdelay']; - $maxh = $_POST['maxh']; - $maxv = $_POST['maxv']; - - 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; -} - -// Service: camobs - Camera observation queries with radius and bbox filtering -elseif ($service === 'camobs') { - $dbconn = getDBConnection(); - - if($_GET['camstatic'] ?? null) { - if ($_GET['camstatic'] == 'radius') { - if($_GET['lat1'] ?? null) { - $lat1 = getParam('lat1'); - if($_GET['lon1'] ?? null) { - $lon1 = getParam('lon1'); - if($_GET['radius'] ?? null) { - $radius = getParam('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 = array(); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - } - } - } - } - - if($_GET['camstatic'] ?? null) { - if ($_GET['camstatic'] == 'bbox') { - if($_GET['lat1'] ?? null) { - $lat1 = getParam('lat1'); - if($_GET['lon1'] ?? null) { - $lon1 = getParam('lon1'); - if($_GET['lat2'] ?? null) { - $lat2 = getParam('lat2'); - if($_GET['lon2'] ?? null) { - $lon2 = getParam('lon2'); - if($_GET['elevbottom'] ?? null) { - $elevbottom = getParam('elevbottom'); - if($_GET['elevtop'] ?? null) { - $elevtop = getParam('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 = array(); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - pg_free_result($result); - } - } - } - } - } - } - } - } -} - -// Service: single - Returns single camera information by camid -elseif ($service === 'single') { - header('Content-Type: application/json'); - - $dbconn = getDBConnection(); - - // Check if camid is actually set to avoid warnings - if (!isset($_GET['camid'])) { - echo json_encode(array("error" => "No camid specified")); - exit; - } - - $camid = $_GET['camid']; - - // Performing SQL query - $query = "SELECT *, COALESCE(hydro, false) as hydro, COALESCE(airport, false) as airport FROM cams WHERE camid = $1"; - - // Use pg_query_params to safely bind the $camid variable - $result = pg_query_params($dbconn, $query, array($camid)) - or die('Query failed: ' . pg_last_error()); - - // Processing results - $array = array(); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - // Ensure hydro is a proper boolean - $line['hydro'] = ($line['hydro'] === 't' || $line['hydro'] === true); - // Ensure airport is a proper boolean - $line['airport'] = ($line['airport'] === 't' || $line['airport'] === true); - $array[] = $line; - } - - // Output the ORIGINAL full array (including errorcode) to the client - echo json_encode($array); - - // Free resultset - pg_free_result($result); -} - -// Service: powerapitest - Extended power outage testing API -elseif ($service === 'powerapitest') { - $dbconn = getDBConnection(); - - //no gets, current point outage info - if(empty(array_diff_key($_GET, array('service' => '')))) { - $result = pg_query_params($dbconn, - "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))order by startguess asc)) FROM power WHERE cwa = $1 and active = true", - array('RLX')) or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - - //county/state max - if($_GET['max'] ?? null) { - if($_GET['start'] ?? null) { - $starttime = pg_escape_string($_GET['start']); - if($_GET['end'] ?? null) { - $endtime = pg_escape_string($_GET['end']); - - $result = pg_query_params($dbconn, - "select distinct on (county,state) max(outage),county,state from (select distinct on (county,state,update) county,state,sum(outages) as outage, update as time, sum(served) as served from countyoutages where update > $2 and update < $3 and cwa = $1 group by county,state,update) as potato group by county,state", - array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); - - $array = array(); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - } - } - } - - if (isset($_GET['county'])) { - try { - $query = " - SELECT DISTINCT ON (county, state) - county, - state, - SUM(outages) as outage, - update as time, - SUM(served) as served, - ROUND( - CAST( - CASE - WHEN SUM(served) = 0 THEN NULL - ELSE (SUM(outages)::FLOAT / SUM(served)) * 100 - END AS NUMERIC - ), 2 - ) as perout - FROM countyoutages - WHERE update = (SELECT MAX(update) FROM countyoutages) - AND (cwa = $1 OR cwa = $2 OR cwa = $3 OR cwa = $4 OR cwa = $5 OR cwa = $6 OR cwa = $7) - GROUP BY county, state, update - "; - - $result = pg_query_params($dbconn, $query, ['RLX','JKL','ILN','PBZ','MRX','LWX','RNK']); - if ($result === false) { - throw new Exception('Query failed: ' . pg_last_error()); - } - - $results = []; - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $results[] = $line; - } - - header('Content-Type: application/json'); - echo json_encode($results); - - pg_free_result($result); - } catch (Exception $e) { - header('Content-Type: application/json'); - http_response_code(500); - echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); - exit; - } - } - - //county archive - if($_GET['countyarchive'] ?? null) { - if($_GET['start'] ?? null) { - $starttime = pg_escape_string($_GET['start']); - if($_GET['end'] ?? null) { - $endtime = pg_escape_string($_GET['end']); - - $result = pg_query_params($dbconn, - "select distinct on (county,state,update) county,state,sum(outages) as outage, update as time, sum(served) as served from countyoutages where update > $9 and update < $10 and (cwa = $1 or cwa = $2 or cwa = $3 or cwa = $4 or cwa = $5 or cwa = $6 or cwa = $7 or cwa = $8) group by county,state,update", - array('RLX','JKL','ILN','PBZ','MRX','LWX','RNK','CTP',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); - - $array = array(); - while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { - $array[] = $line; - } - echo json_encode($array); - } - } - } - - //Archive point data - if($_GET['archivepoint'] ?? null) { - $starttime = pg_escape_string($_GET['start']); - $endtime = pg_escape_string($_GET['end']); - $result = pg_query_params($dbconn, - "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))order by startguess asc)) FROM power WHERE cwa = $1 and startguess > $2 and lastchange < $3", - array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - - // Svr related functionality - if($_GET['svr'] ?? null =='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); - echo($resultArray[0]['json_build_object']); - } - - if($_GET['svr'] ?? null == 'archive') { - if($_GET['start'] ?? null) { - $starttime = pg_escape_string($_GET['start']); - if($_GET['end'] ?? null) { - $endtime = pg_escape_string($_GET['end']); - - $result = pg_query_params($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 > $1 and endtime < $2" - ,array($starttime,$endtime)) or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - } - - if(!isset($_GET['start']) && !isset($_GET['end'])) { - $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() - interval '24 hours' and endtime > now() - interval '24 hours'") or die('Query failed: ' . pg_last_error()); - $resultArray = pg_fetch_all($result); - echo($resultArray[0]['json_build_object']); - } - } - - pg_free_result($result); -} - -// Default behavior - show available services if no valid service provided -else { +if (file_exists($serviceFile)) { + require_once $serviceFile; +} else { + // Default behavior - show available services if no valid service provided header('Content-Type: application/json'); http_response_code(400); echo json_encode([ @@ -3014,375 +135,4 @@ else { 'documentation' => 'See main.php file for detailed documentation on each service.' ]); } - -// Helper functions for stormdata service -function send_error(int $http_code, string $message, ?string $log_message = null): void { - if ($log_message) { error_log($log_message); } - elseif ($http_code >= 500) { error_log("Server Error (" . $http_code . "): " . $message); } - http_response_code($http_code); - header('Content-Type: application/json; charset=utf-8'); - echo json_encode(['error' => $message]); - exit; -} - -function handle_cam_request($dbconn, array $data): void { - error_log("Handling 'camera image' request."); - - $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.', 'GeoJSON Decode Error: ' . json_last_error_msg()); - } - 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 -- Assuming filepath is the relative URL path - ) ORDER BY dateutc ASC -- Order images chronologically - ) AS images - FROM - camdb - WHERE - dateutc >= $1::timestamp -- start_time - AND dateutc <= $2::timestamp -- end_time - GROUP BY - camid - ) AS img_agg ON c.camid = img_agg.camid - WHERE - c.active = TRUE -- Only active cameras - AND ST_Within(c.geom, ST_GeomFromGeoJSON($3)) -- Camera location within area - ORDER BY - c.camid; -- Optional: Order cameras by ID - "; - - $params = array( - $start_time_str, // $1: start_time - $end_time_str, // $2: end_time - $geojson_str // $3: area_geojson string - ); - - $result = pg_query_params($dbconn, $query, $params); - - if (!$result) { - send_error(500, 'Database query failed for camera data.', 'Camera Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); - } - - $cameras_output = []; - while ($row = pg_fetch_assoc($result)) { - $geometry = json_decode($row['geometry_geojson']); - if (json_last_error() !== JSON_ERROR_NONE) { - error_log('Failed to decode geometry for camid ' . ($row['camid'] ?? 'N/A') . ': ' . json_last_error_msg()); - $geometry = null; - } - - $images = json_decode($row['images']); - if (json_last_error() !== JSON_ERROR_NONE) { - error_log('Failed to decode images JSON for camid ' . ($row['camid'] ?? 'N/A') . ': ' . json_last_error_msg()); - $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); - error_log("Found " . count($cameras_output) . " cameras matching criteria."); - - header('Content-Type: application/json'); - echo json_encode($cameras_output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; -} - -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)) { - http_response_code(500); - echo json_encode(['error' => 'No polygons provided']); - exit; - } - - $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) { - http_response_code(500); - echo json_encode(['error' => pg_last_error($dbconn)]); - exit; - } - - $results = []; - while ($row = pg_fetch_assoc($result)) { - $results[] = $row; - } - - pg_free_result($result); - - header('Content-Type: application/json'); - echo json_encode($results); -} - -function handle_ohgo_request($dbconn, array $data): void { - error_log("Handling 'ohgo' request."); - $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.', 'GeoJSON Decode Error: ' . json_last_error_msg()); - } - 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.', 'OHGO Query Failed: ' . pg_last_error($dbconn)); - } - - $features = []; - while ($line = pg_fetch_assoc($result)) { - $geometry = json_decode($line['geometry']); - if (json_last_error() !== JSON_ERROR_NONE) { - error_log('Failed to decode geometry for ohgo row: ' . json_last_error_msg()); - continue; - } - $properties = $line; - unset($properties['geometry']); - $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; - } - pg_free_result($result); - error_log("Found " . count($features) . " features for ohgo request."); - - send_geojson($features); -} - -function handle_power_request($dbconn, array $data): void { - error_log("Handling 'power' request."); - $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.', 'GeoJSON Decode Error: ' . json_last_error_msg()); - } - 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, // $1: start_time from JSON - $geojson_str, // $2: area_geojson STRING from JSON - $end, // $3: end_time from JSON - $buffer_hours_int // $4: buffer_hours from JSON (as integer) - ); - $result = pg_query_params($dbconn, $query, $params); - if (!$result) { - send_error(500, 'Database query failed for power data.', 'Power Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); - } - - $features = []; - while ($line = pg_fetch_assoc($result)) { - $geometry = json_decode($line['geometry']); - if (json_last_error() !== JSON_ERROR_NONE) { - error_log('Failed to decode geometry for power row: ' . json_last_error_msg()); - continue; - } - $properties = $line; - unset($properties['geometry']); - $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; - } - pg_free_result($result); - error_log("Found " . count($features) . " features for power request."); - - send_geojson($features); -} - -function handle_ohgo_request_no_poly($dbconn, array $data): void { - error_log("Handling 'ohgo' request no poly."); - $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.', 'OHGO Query Failed: ' . pg_last_error($dbconn)); - } - - $features = []; - while ($line = pg_fetch_assoc($result)) { - $geometry = json_decode($line['geometry']); - if (json_last_error() !== JSON_ERROR_NONE) { - error_log('Failed to decode geometry for ohgo row: ' . json_last_error_msg()); - continue; - } - $properties = $line; - unset($properties['geometry']); - $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; - } - pg_free_result($result); - error_log("Found " . count($features) . " features for ohgo request."); - - send_geojson($features); -} - -function handle_power_request_no_poly($dbconn, array $data): void { - error_log("Handling 'power' request no poly."); - $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, // $1: start_time from JSON - $end, // $2: end_time from JSON - $buffer_hours_int, // $3: buffer_hours from JSON (as integer) - $outage_thresh // $4 - ); - $result = pg_query_params($dbconn, $query, $params); - if (!$result) { - send_error(500, 'Database query failed for power data.', 'Power Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); - } - - $features = []; - while ($line = pg_fetch_assoc($result)) { - $geometry = json_decode($line['geometry']); - if (json_last_error() !== JSON_ERROR_NONE) { - error_log('Failed to decode geometry for power row: ' . json_last_error_msg()); - continue; - } - $properties = $line; - unset($properties['geometry']); - $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; - } - pg_free_result($result); - error_log("Found " . count($features) . " features for power request."); - - send_geojson($features); -} - -function send_geojson(array $features): void { - $geojson_output = ['type' => 'FeatureCollection', 'features' => $features]; - header('Content-Type: application/geo+json; charset=utf-8'); - echo json_encode($geojson_output); - exit; -} - -// Close database connection when needed -if (isset($dbconn)) { - pg_close($dbconn); -} ?> \ No newline at end of file diff --git a/php/admin.php b/php/admin.php new file mode 100644 index 0000000..fad4e7a --- /dev/null +++ b/php/admin.php @@ -0,0 +1,44 @@ + \ No newline at end of file diff --git a/php/camapi.php b/php/camapi.php new file mode 100644 index 0000000..c4ebf1c --- /dev/null +++ b/php/camapi.php @@ -0,0 +1,80 @@ + $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()); + + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); + } + } + } + } + } + } +} + +// camstatic endpoint +if (isset($_GET['camstatic'])) { + if($_GET['lat1']) { + $lat1 = getParam('lat1'); + if($_GET['lon1']) { + $lon1 = getParam('lon1'); + if($_GET['radius']) { + $radius = getParam('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()); + + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); + } + } + } +} + +// camdb endpoint +if (isset($_GET['camdb'])) { + $result = pg_query($dbconn, + "SELECT COUNT(*) FROM camdb") or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/camcircle.php b/php/camcircle.php new file mode 100644 index 0000000..20e3e82 --- /dev/null +++ b/php/camcircle.php @@ -0,0 +1,36 @@ + \ No newline at end of file diff --git a/php/camlist.php b/php/camlist.php new file mode 100644 index 0000000..3fe968b --- /dev/null +++ b/php/camlist.php @@ -0,0 +1,57 @@ + 'success', + 'data' => $data, + 'count' => count($data) + ]; + + // Free resultset + pg_free_result($result); + +} catch (Exception $e) { + http_response_code(500); + $response = [ + 'status' => 'error', + 'message' => $e->getMessage() + ]; +} + +// Output JSON +echo json_encode($response, JSON_PRETTY_PRINT | JSON_NUMERIC_CHECK); + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/camobs.php b/php/camobs.php new file mode 100644 index 0000000..fe74a40 --- /dev/null +++ b/php/camobs.php @@ -0,0 +1,72 @@ + $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 = array(); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); + } + } + } + } + } + } + } +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/cams.php b/php/cams.php new file mode 100644 index 0000000..cdee508 --- /dev/null +++ b/php/cams.php @@ -0,0 +1,28 @@ + 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()); + +// Printing results in HTML +$array = array(); +while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + // Ensure hydro is a proper boolean + $line['hydro'] = ($line['hydro'] === 't' || $line['hydro'] === true); + // Ensure airport is a proper boolean + $line['airport'] = ($line['airport'] === 't' || $line['airport'] === true); + $array[] = $line; +} +echo json_encode($array); + +// Free resultset +pg_free_result($result); + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/common.php b/php/common.php new file mode 100644 index 0000000..10cdbfe --- /dev/null +++ b/php/common.php @@ -0,0 +1,396 @@ += 500) { error_log("Server Error (" . $http_code . "): " . $message); } + http_response_code($http_code); + header('Content-Type: application/json; charset=utf-8'); + echo json_encode(['error' => $message]); + exit; +} + +function send_geojson(array $features): void { + $geojson_output = ['type' => 'FeatureCollection', 'features' => $features]; + header('Content-Type: application/geo+json; charset=utf-8'); + echo json_encode($geojson_output); + exit; +} + +function handle_cam_request($dbconn, array $data): void { + error_log("Handling 'camera image' request."); + + $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.', 'GeoJSON Decode Error: ' . json_last_error_msg()); + } + 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 -- Assuming filepath is the relative URL path + ) ORDER BY dateutc ASC -- Order images chronologically + ) AS images + FROM + camdb + WHERE + dateutc >= $1::timestamp -- start_time + AND dateutc <= $2::timestamp -- end_time + GROUP BY + camid + ) AS img_agg ON c.camid = img_agg.camid + WHERE + c.active = TRUE -- Only active cameras + AND ST_Within(c.geom, ST_GeomFromGeoJSON($3)) -- Camera location within area + ORDER BY + c.camid; -- Optional: Order cameras by ID + "; + + $params = array( + $start_time_str, // $1: start_time + $end_time_str, // $2: end_time + $geojson_str // $3: area_geojson string + ); + + $result = pg_query_params($dbconn, $query, $params); + + if (!$result) { + send_error(500, 'Database query failed for camera data.', 'Camera Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); + } + + $cameras_output = []; + while ($row = pg_fetch_assoc($result)) { + $geometry = json_decode($row['geometry_geojson']); + if (json_last_error() !== JSON_ERROR_NONE) { + error_log('Failed to decode geometry for camid ' . ($row['camid'] ?? 'N/A') . ': ' . json_last_error_msg()); + $geometry = null; + } + + $images = json_decode($row['images']); + if (json_last_error() !== JSON_ERROR_NONE) { + error_log('Failed to decode images JSON for camid ' . ($row['camid'] ?? 'N/A') . ': ' . json_last_error_msg()); + $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); + error_log("Found " . count($cameras_output) . " cameras matching criteria."); + + header('Content-Type: application/json'); + echo json_encode($cameras_output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + exit; +} + +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)) { + http_response_code(500); + echo json_encode(['error' => 'No polygons provided']); + exit; + } + + $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) { + http_response_code(500); + echo json_encode(['error' => pg_last_error($dbconn)]); + exit; + } + + $results = []; + while ($row = pg_fetch_assoc($result)) { + $results[] = $row; + } + + pg_free_result($result); + + header('Content-Type: application/json'); + echo json_encode($results); +} + +function handle_ohgo_request($dbconn, array $data): void { + error_log("Handling 'ohgo' request."); + $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.', 'GeoJSON Decode Error: ' . json_last_error_msg()); + } + 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.', 'OHGO Query Failed: ' . pg_last_error($dbconn)); + } + + $features = []; + while ($line = pg_fetch_assoc($result)) { + $geometry = json_decode($line['geometry']); + if (json_last_error() !== JSON_ERROR_NONE) { + error_log('Failed to decode geometry for ohgo row: ' . json_last_error_msg()); + continue; + } + $properties = $line; + unset($properties['geometry']); + $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; + } + pg_free_result($result); + error_log("Found " . count($features) . " features for ohgo request."); + + send_geojson($features); +} + +function handle_power_request($dbconn, array $data): void { + error_log("Handling 'power' request."); + $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.', 'GeoJSON Decode Error: ' . json_last_error_msg()); + } + 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, // $1: start_time from JSON + $geojson_str, // $2: area_geojson STRING from JSON + $end, // $3: end_time from JSON + $buffer_hours_int // $4: buffer_hours from JSON (as integer) + ); + $result = pg_query_params($dbconn, $query, $params); + if (!$result) { + send_error(500, 'Database query failed for power data.', 'Power Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); + } + + $features = []; + while ($line = pg_fetch_assoc($result)) { + $geometry = json_decode($line['geometry']); + if (json_last_error() !== JSON_ERROR_NONE) { + error_log('Failed to decode geometry for power row: ' . json_last_error_msg()); + continue; + } + $properties = $line; + unset($properties['geometry']); + $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; + } + pg_free_result($result); + error_log("Found " . count($features) . " features for power request."); + + send_geojson($features); +} + +function handle_ohgo_request_no_poly($dbconn, array $data): void { + error_log("Handling 'ohgo' request no poly."); + $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.', 'OHGO Query Failed: ' . pg_last_error($dbconn)); + } + + $features = []; + while ($line = pg_fetch_assoc($result)) { + $geometry = json_decode($line['geometry']); + if (json_last_error() !== JSON_ERROR_NONE) { + error_log('Failed to decode geometry for ohgo row: ' . json_last_error_msg()); + continue; + } + $properties = $line; + unset($properties['geometry']); + $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; + } + pg_free_result($result); + error_log("Found " . count($features) . " features for ohgo request."); + + send_geojson($features); +} + +function handle_power_request_no_poly($dbconn, array $data): void { + error_log("Handling 'power' request no poly."); + $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, // $1: start_time from JSON + $end, // $2: end_time from JSON + $buffer_hours_int, // $3: buffer_hours from JSON (as integer) + $outage_thresh // $4 + ); + $result = pg_query_params($dbconn, $query, $params); + if (!$result) { + send_error(500, 'Database query failed for power data.', 'Power Query Failed: ' . pg_last_error($dbconn) . " | Query: " . $query . " | Params: " . print_r($params, true)); + } + + $features = []; + while ($line = pg_fetch_assoc($result)) { + $geometry = json_decode($line['geometry']); + if (json_last_error() !== JSON_ERROR_NONE) { + error_log('Failed to decode geometry for power row: ' . json_last_error_msg()); + continue; + } + $properties = $line; + unset($properties['geometry']); + $features[] = ['type' => 'Feature', 'geometry' => $geometry, 'properties' => $properties]; + } + pg_free_result($result); + error_log("Found " . count($features) . " features for power request."); + + send_geojson($features); +} +?> \ No newline at end of file diff --git a/php/db.php b/php/db.php new file mode 100644 index 0000000..76a4906 --- /dev/null +++ b/php/db.php @@ -0,0 +1,150 @@ + '')))) { + 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 cwa = 'RLX' + 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; + } + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + if (isset($result)) { + pg_free_result($result); + } + header('Content-Type: application/json'); + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + exit; + } +} + +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()); + + // Printing results in HTML + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +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; + } + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + if (isset($result)) { + pg_free_result($result); + } + header('Content-Type: application/json'); + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + exit; + } +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/fire.php b/php/fire.php new file mode 100644 index 0000000..5079cb1 --- /dev/null +++ b/php/fire.php @@ -0,0 +1,19 @@ + '')))) { + $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); + echo($resultArray[0]['json_build_object']); +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/individualcam.php b/php/individualcam.php new file mode 100644 index 0000000..cb36c7a --- /dev/null +++ b/php/individualcam.php @@ -0,0 +1,45 @@ + \ No newline at end of file diff --git a/php/lsr.php b/php/lsr.php new file mode 100644 index 0000000..d9492fe --- /dev/null +++ b/php/lsr.php @@ -0,0 +1,769 @@ + NOW() - INTERVAL '2 hours'"; + + // Prepare and execute the query + $result = pg_query($dbconn, $query); + if (!$result) { + header('Content-Type: application/json'); + echo json_encode(['error' => 'Query failed: ' . pg_last_error()]); + exit; + } + + // Fetch the result + $resultArray = pg_fetch_all($result); + + // Check if we got results + if ($resultArray && isset($resultArray[0]['geojson'])) { + header('Content-Type: application/json'); + echo $resultArray[0]['geojson']; // Direct output since it's already JSON from jsonb_build_object + } else { + header('Content-Type: application/json'); + echo json_encode(['error' => 'No results found']); + } + + // Free result and close connection + pg_free_result($result); +} + +if (isset($_GET['ohgotable'])) { + // Performing SQL query + $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()); + + // Printing results in JSON + $array = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +if (isset($_GET['lsrohgo'])) { + if ($_GET['lsrohgo'] == 'true') { + $lsrflag = 'true'; + } else { + $lsrflag = 'false'; + } + $id = (int) $_GET['id']; + $query = "UPDATE ohgo SET lsr = $1 WHERE id = $2"; + $result = pg_query_params($dbconn, $query, array($lsrflag, $id)) or die('Query failed: ' . pg_last_error()); + pg_free_result($result); +} + +if (isset($_GET['ohgohide'])) { + if ($_GET['ohgohide'] == 'true') { + $lsrflag = 'true'; + } else { + $lsrflag = 'false'; + } + $id = (int) $_GET['id']; + $query = "UPDATE ohgo SET hide = $1 WHERE id = $2"; + $result = pg_query_params($dbconn, $query, array($lsrflag, $id)) or die('Query failed: ' . pg_last_error()); + pg_free_result($result); +} + +//take vtec, return start, end, polygon, outages in polygon, outages in buffer, warntype, polygon pop +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; + "; + + // Prepare and execute the query using pg_query_params + $result = pg_query_params($dbconn, $query, array($vtec)) + or die('Query failed: ' . pg_last_error()); + + // Fetch the result + $resultArray = pg_fetch_all($result); + + // Output the JSON object + echo($resultArray[0]['json_build_object']); + + // Free result + pg_free_result($result); +} + +//Get reports pre-flagged with the vtec +if (isset($_GET['preflagreports'])) { + $vtec = $_GET['preflagreports']; + $query = "SELECT * from reports WHERE severe = $1"; + $result = pg_query_params($dbconn, $query, array($vtec)) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); +} + +//Get reports within polygon for arbitary time after end time +if (isset($_GET['reports'])) { + $vtec = $_GET['reports']; + if (isset($_GET['hours'])) { + $hours = $_GET['hours']; + } else { + $hours = 6; + } + + //echo $hours; + + $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()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); +} + +//Get point power outages within polygon + arb time, default 60 minutes +if (isset($_GET['outages'])) { + $vtec = $_GET['outages']; + if (isset($_GET['hours'])) { + $hours = $_GET['hours']; + } else { + $hours = 1; + } + + //echo $hours; + + $query = "SELECT power.lat,power.lon,power.peakoutage,power.cause,power.derivedstart,power.lastchange from power,svr where ST_Contains(svr.nwspoly, power.realgeom) and vtec = $1 and derivedstart > svr.issue AND derivedstart < svr.issue + (INTERVAL '1 h' * $2)"; + $result = pg_query_params($dbconn, $query, array($vtec,$hours)) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); +} + +//no gets, current point outage info +if(empty(array_diff_key($_GET, array('service' => '')))) { + $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); + echo($resultArray[0]['json_build_object']); + pg_free_result($result); +} + +//For real time mapping +if(isset($_GET['verify'])) { + $query = "select id, lat::Numeric(16,3), lon::Numeric(16,3),issue,to_char(initialdtg, 'yyyy/mm/dd hh24:mi') as initialdtg,rawemail,concat(county,' ',state,'\n',place) as place,comments,lsr::text,severe from reports where severe is not null and processed = true order by initialdtg desc"; + $result = pg_query($query) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +//no gets, current point outage info +if (isset($_GET['rtcad'])) { + if (isset($_GET['hours'])) { + $hours = $_GET['hours']; + } else { + $hours = 6; + } + + $query = " + 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', utcinitialdtg, + 'county', county, + 'state', state, + 'issue', issue, + 'rawemail', rawemail, + 'place', place, + 'comments', comments + ) + ) ORDER BY initialdtg DESC + ) + ) + FROM reports + WHERE lat is not null and utcinitialdtg >= NOW() - INTERVAL '1 hour' * $1; + "; + + // Prepare and execute the query using pg_query_params + $result = pg_query_params($dbconn, $query, array($hours)) + or die('Query failed: ' . pg_last_error()); + + // Fetch the result + $resultArray = pg_fetch_all($result); + + // Output the JSON object + echo($resultArray[0]['json_build_object']); + + // Free result + pg_free_result($result); +} + +//Stats +if (isset($_GET['stats'])) { + $query = "SELECT county, state, MAX(emailtime), count(*) FROM reports where county is not null and (state = 'WV' or state = 'VA' or state = 'KY' or state = 'OH') GROUP BY county, state"; + $result = pg_query($query) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +//Get METAR Array for Jelly Bean +if (isset($_GET['metars'])) { + if (isset($_GET['start'])) { + $start = $_GET['start']; + } + if (isset($_GET['end'])) { + $end = $_GET['end']; + } + + $query = "SELECT icao,temp,dewp,wx,precip1,precip3,precip6,raw,obtime,stationname,lat,lon from metars where obtime - interval '45 minutes' > $1 and obtime < $2 order by lon asc"; + $result = pg_query_params($dbconn, $query, array($start,$end)) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } //echo($array); + echo json_encode($array); + pg_free_result($result); +} + +if (isset($_GET['news'])) { + //$query = "SELECT headline, summary, imageurl, source, storylink, updated from news where notrelevant is not true"; + $query = " + SELECT + *, + CASE + WHEN concat(summary, ' ', headline) ILIKE ANY (ARRAY[ + '%weather%', '%flood%', '%fire%', '%fog%', '%snow%', '%emergency%' + '%wind%', '%ice%', '%rain%', '%power%', '%explosion%', + '%drown%', '%stream%', '%river%', '%air%', '%wind%', + '%river%', '%ice%', '%creek%', '%crash%', '%thunder%', + '%fog%', '%spill%', '%pileup%', '%pile-up%', '%gust%', + '%fatal%', '%injury%', '%sleet%', '%injured%', '%frost%', + '%culvert%', '%slippery%', '%wildfire%', '%tornado%', + '%thunderstorm%', '%downburst%', '%microburst%', '%crash%', '%heatstroke%', '%derecho%' + '%lightning%', '%hypothermia%', '%slide%', '%flow%', '%ski%', '%water%', '%innundation%' + ]) THEN 2 + WHEN concat(summary, ' ', headline) ILIKE ANY (ARRAY[ + '%legislative%','%history%','%budget%','%birthday%','%banning%','%academic%','%tuna%','%Service Forecast%', '%DOGE%','%demonstrators%','%forum%','%health%','%fraud%','%birthday%', '%egg%', '%eggs%', '%collector%', '%church%', ' %crypto%' + ]) THEN 0 + ELSE 1 + END AS relevance_level + FROM news WHERE timeutc > NOW() - INTERVAL '18 hours' + ORDER BY relevance_level DESC, timeutc DESC + "; + $result = pg_query($query) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +if (isset($_GET['news2'])) { + $query = " + SELECT + *, + (SELECT COUNT(*) + FROM unnest(ARRAY[ + 'weather', 'flood', 'fire', 'fog', 'snow', 'emergency', + 'wind', 'ice', 'rain', 'power', 'explosion', 'warmer', 'colder', + 'drown', 'stream', 'river', 'air', 'wind', 'destroyed', 'rime', 'glaze', + 'river', 'ice', 'creek', 'crash', 'thunder', 'spinup', 'black ice', 'aircraft', + 'fog', 'spill', 'pileup', 'pile-up', 'gust', 'frozen', 'funnel', 'rainfall', + 'fatal', 'injury', 'sleet', 'injured', 'frost', 'dead', 'death', 'landslide', + 'culvert', 'slippery', 'wildfire', 'tornado', 'blizzard', 'creek', 'hail', + 'thunderstorm', 'downburst', 'microburst', 'crash', 'heatstroke', 'derecho', + 'lightning', 'hypothermia', 'slide', 'flow', 'ski', 'water', 'inundation', 'victim', + 'victims', 'flooding','flooded','snowing','freezing rain','clouds','cloud','storm' + ]) AS pattern + WHERE concat(summary, ' ', headline) ~* ('\y' || pattern || '\y')) AS match_count + FROM news + WHERE timeutc > NOW() - INTERVAL '18 hours' + ORDER BY nlpscore DESC, timeutc DESC + "; + $result = pg_query($query) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +if (isset($_GET['news3old'])) { + $query = " + SELECT * FROM news WHERE (timeutc > NOW() - INTERVAL '24 hours' and nlpscore > 0.1) or (timeutc > NOW() - INTERVAL '6 hours') ORDER BY nlpscore DESC, timeutc DESC"; + $result = pg_query($query) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +if (isset($_GET['news3'])) { + // Corrected query with NULLS LAST + $query = " + SELECT * FROM news + WHERE (timeutc > NOW() - INTERVAL '24 hours' AND impact_score > 25) + OR (timeutc > NOW() - INTERVAL '6 hours') ORDER BY impact_score DESC NULLS LAST, timeutc DESC limit 50"; + + $result = pg_query($query) or die('Query failed: ' . pg_last_error()); + + $array = []; // It's good practice to initialize the array + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + + echo json_encode($array); + + // Free resultset + pg_free_result($result); +} + +// --- FIXED SECTION: newsarchive --- +if (isset($_GET['newsarchive'])) { + // Initialize variables + $start = isset($_GET['start']) ? $_GET['start'] : null; + $end = isset($_GET['end']) ? $_GET['end'] : null; + $keys = isset($_GET['key']) ? $_GET['key'] : []; + // Convert keys to an array if it's a string + if (is_string($keys)) { + $keys = explode(',', $keys); + } + + $patterns = array_map(function($term) { + return trim($term); + }, $keys); + + // Handle case with no search terms + if (empty($patterns)) { + $query = "SELECT * FROM news"; + $params = []; + } else { + // Build parameter placeholders + $placeholders = []; + for ($i = 1; $i <= count($patterns); $i++) { + // FIXED: Use concatenation to create "$1::text" + $placeholders[] = '$' . $i . '::text'; + } + $placeholder_string = implode(',', $placeholders); + + $query = " + SELECT + n.*, + ( + SELECT COUNT(*) + FROM unnest(ARRAY[{$placeholder_string}]::text[]) AS pattern + WHERE concat(n.summary, ' ', n.headline) ILIKE pattern + ) AS match_count + FROM news n + WHERE concat(summary, ' ', headline) ILIKE ANY (ARRAY[{$placeholder_string}]::text[]) + "; + $params = array_map(function($term) { return "%{$term}%"; }, $patterns); + } + + // Add date filters if provided + $param_count = count($patterns); + if ($start) { + $param_count++; + // FIXED: Use concatenation instead of deprecated interpolation + $query .= " AND timeutc >= $" . $param_count; + $params[] = $start; + } + if ($end) { + $param_count++; + // FIXED: Use concatenation instead of deprecated interpolation + $query .= " AND timeutc <= $" . $param_count; + $params[] = $end; + } + + $query .= " ORDER BY match_count DESC, timeutc desc"; + + // Execute query + $result = pg_query_params($dbconn, $query, $params) or die('Query failed: ' . pg_last_error()); + + $array = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + + echo json_encode($array); +} + +if (isset($_GET['wv511'])) { + $query = "SELECT jsonb_build_object( + 'type', 'FeatureCollection', + 'features', jsonb_agg( + jsonb_build_object( + 'type', 'Feature', + 'geometry', ST_AsGeoJSON(geom)::jsonb, + 'properties', jsonb_build_object( + 'type', name, + 'reported', first_seen, + 'end', last_seen_in_feed, + 'county', county, + 'state', st, + 'remark', latest_description, + 'lat', st_y(st_centroid(geom)), + 'lon', st_x(st_centroid(geom)) + ) + ) + ) + ) as geojson + FROM public.wv511 + WHERE last_updated > NOW() - INTERVAL '2 hours'"; + + // Prepare and execute the query + $result = pg_query($dbconn, $query); + if (!$result) { + header('Content-Type: application/json'); + echo json_encode(['error' => 'Query failed: ' . pg_last_error()]); + exit; + } + + // Fetch the result + $resultArray = pg_fetch_all($result); + + // Check if we got results + if ($resultArray && isset($resultArray[0]['geojson'])) { + header('Content-Type: application/json'); + echo $resultArray[0]['geojson']; // Direct output since it's already JSON from jsonb_build_object + } else { + header('Content-Type: application/json'); + echo json_encode(['error' => 'No results found']); + } + + // Free result and close connection + pg_free_result($result); +} + +if (isset($_GET['ky511'])) { + $query = "SELECT jsonb_build_object( + 'type', 'FeatureCollection', + 'features', jsonb_agg( + jsonb_build_object( + 'type', 'Feature', + 'geometry', ST_AsGeoJSON(geom)::jsonb, + 'properties', jsonb_build_object( + 'reported', first_seen, + 'end', last_seen_in_feed, + 'county', county, + 'state', st, + 'remark', latest_description, + 'lat', st_y(st_centroid(geom)), + 'lon', st_x(st_centroid(geom)) + ) + ) + ) + ) as geojson + FROM ky511.ky511 + WHERE last_updated > NOW() - INTERVAL '2 hours'"; + + // Prepare and execute the query + $result = pg_query($dbconn, $query); + if (!$result) { + header('Content-Type: application/json'); + echo json_encode(['error' => 'Query failed: ' . pg_last_error()]); + exit; + } + + // Fetch the result + $resultArray = pg_fetch_all($result); + + // Check if we got results + if ($resultArray && isset($resultArray[0]['geojson'])) { + header('Content-Type: application/json'); + echo $resultArray[0]['geojson']; // Direct output since it's already JSON from jsonb_build_object + } else { + header('Content-Type: application/json'); + echo json_encode(['error' => 'No results found']); + } + + // Free result and close connection + pg_free_result($result); +} + +if (isset($_GET['getCombinedTable'])) { + // Combined SQL query using UNION ALL with CAST for 'id' + $query = " + SELECT * FROM ( + -- OHGO Query + SELECT + 'ohgo' AS source, + 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, + CAST(id AS TEXT) AS id, -- Cast id to TEXT + 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 '24 hours') AND start > now() - interval '144 hours' + + UNION ALL + + -- WV511 Query + SELECT + 'wv511' AS source, + 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, + CAST(id AS TEXT) AS id, -- Cast id to TEXT + wv511.name as category, + NULL AS roadstatus, + cwa, + county, + st as state, + 'Map Link' AS location, + NULL AS routename, + latest_description as description, + TO_CHAR(first_seen, 'YYYY-MM-DD HH24:MI') AS start, + TO_CHAR(last_seen_in_feed, 'YYYY-MM-DD HH24:MI') AS endtime, + TO_CHAR(last_updated, 'YYYY-MM-DD HH24:MI') AS lastupdate + FROM wv511 + WHERE (last_seen_in_feed IS NULL OR last_seen_in_feed > NOW() - INTERVAL '24 hours') AND first_seen > now() - interval '144 hours' + and wv511.name !~ 'Crash' and + wv511.name !~ 'Vehicle' and wv511.name !~ 'Dead Animal' and wv511.name !~ 'Debris in Roadway' and wv511.name !~ 'Congestion-Delay' and + wv511.name !~ 'Pot hole' and wv511.name !~ 'Debris On Bridge' and wv511.name !~ 'Attenuator' and wv511.name !~ 'Pedestrian' and + wv511.name !~ 'Bridge Closed' and wv511.name !~ 'Truck on escape' and wv511.name !~ 'Bridge Incident' and wv511.name !~ 'Escape Ramp' AND + wv511.name !~ 'Signal' + UNION ALL + + -- KY511 Query + SELECT + 'ky511.ky511' AS source, + 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, + CAST(id AS TEXT) AS id, -- Cast id to TEXT + 'Weather' as category, + NULL AS roadstatus, + cwa, + county, + st as state, + 'Map Link' AS location, + NULL AS routename, + latest_description as description, + TO_CHAR(first_seen, 'YYYY-MM-DD HH24:MI') AS start, + TO_CHAR(last_seen_in_feed, 'YYYY-MM-DD HH24:MI') AS endtime, + TO_CHAR(last_updated, 'YYYY-MM-DD HH24:MI') AS lastupdate + FROM ky511.ky511 + WHERE (last_seen_in_feed IS NULL OR last_seen_in_feed > NOW() - INTERVAL '24 hours') AND first_seen > now() - interval '144 hours' + ) AS combined_data + ORDER BY start ASC; + "; + + // Execute the query + $result = pg_query($dbconn, $query); + + // Set header before any output + header('Content-Type: application/json'); + + if (!$result) { + // Output error as JSON + echo json_encode(['error' => 'Combined query failed: ' . pg_last_error($dbconn)]); + // Close connection if needed + // pg_close($dbconn); + exit; + } + + // Fetch results into an array + $dataArray = []; + while ($row = pg_fetch_assoc($result)) { + $dataArray[] = $row; + } + if ($dataArray === false) { + echo json_encode(['error' => 'Failed to fetch results.']); + pg_free_result($result); + // pg_close($dbconn); + exit; + } + + // Output the combined results as JSON + echo json_encode($dataArray); + + // Free result memory + pg_free_result($result); + + // Optionally close the connection + // pg_close($dbconn); + + exit; // Stop script execution +} + +if (isset($_GET['updater'])) { + $allowedTables = ['wv511', 'ky511.ky511', 'ohgo']; + if (isset($_GET['lsr']) && isset($_GET['id']) && isset($_GET['table'])) { + // --- Handle LSR Update --- + $requestedTable = $_GET['table']; + $lsrInput = $_GET['lsr']; + $idInput = $_GET['id']; // ID validation missing here, see note below + + if (!in_array($requestedTable, $allowedTables)) { + http_response_code(400); + die('Error (LSR): Invalid table specified.'); + } + + $lsrflag = ($lsrInput === 'true') ? 'true' : 'false'; + $id = $idInput; // WARNING: ID is not validated/sanitized here for LSR! + + $tableNameEscaped = $requestedTable; + // Note: {$tableNameEscaped} is VALID complex syntax. The issue was with ${var} + $query = "UPDATE {$tableNameEscaped} SET lsr = $1 WHERE id = $2"; + $result = pg_query_params($dbconn, $query, array($lsrflag, $id)); + + if ($result) { + $affectedRows = pg_affected_rows($result); + echo "LSR Update successful for table '{$requestedTable}'. {$affectedRows} row(s) affected for ID {$id}."; + } else { + http_response_code(500); + error_log("LSR Query failed for table '{$requestedTable}', ID {$id}: " . pg_last_error($dbconn)); + die('Error: The LSR update query failed.'); + } + } else if (isset($_GET['hide']) && isset($_GET['id']) && isset($_GET['table'])) { + // --- Handle Hide Update --- + $requestedTable = $_GET['table']; + $hideInput = $_GET['hide']; + $idInput = $_GET['id']; + + if (!in_array($requestedTable, $allowedTables)) { + http_response_code(400); + die('Error (Hide): Invalid table specified.'); + } + + $hideflag = ($hideInput === 'true') ? 'true' : 'false'; + + // Use INT validation for ID here - make sure this matches your DB column type + $id = $idInput; + + $tableNameEscaped = $requestedTable; + $query = "UPDATE {$tableNameEscaped} SET hide = $1 WHERE id = $2"; + $result = pg_query_params($dbconn, $query, array($hideflag, $id)); + + if ($result) { + $affectedRows = pg_affected_rows($result); + echo "Hide Update successful for table '{$requestedTable}'. {$affectedRows} row(s) affected for ID {$id}."; + } else { + http_response_code(500); + error_log("Hide Query failed for table '{$requestedTable}', ID {$id}: " . pg_last_error($dbconn)); + die('Error: The Hide update query failed.'); + } + } else { + // --- Handle Missing Parameters Error --- + // Neither 'lsr' nor 'hide' (along with id and table) were provided correctly. + http_response_code(400); // Bad Request + // Check which parameters *are* present to give a potentially more helpful error + $missing = []; + if (!isset($_GET['id'])) $missing[] = '"id"'; + if (!isset($_GET['table'])) $missing[] = '"table"'; + if (!isset($_GET['lsr']) && !isset($_GET['hide'])) { + $missing[] = 'action ("lsr" or "hide")'; + } else if (isset($_GET['lsr']) && (!isset($_GET['id']) || !isset($_GET['table']))) { + // LSR was specified, but others missing + } else if (isset($_GET['hide']) && (!isset($_GET['id']) || !isset($_GET['table']))) { + // Hide was specified, but others missing + } + + if (!empty($missing)) { + die('Error: Missing required parameter(s): ' . implode(', ', $missing) . '. Please provide a valid action ("lsr" or "hide"), "id", and "table".'); + } else { + // Should ideally not happen with the logic above, but as a fallback: + die('Error: Invalid request parameters. Please provide action ("lsr" or "hide"), "id", and "table".'); + } + } +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/mp4.php b/php/mp4.php new file mode 100644 index 0000000..4845e24 --- /dev/null +++ b/php/mp4.php @@ -0,0 +1,37 @@ + -layers Optimize gif:-"); + +echo base64_encode($gif); +exit; +?> \ No newline at end of file diff --git a/php/nws.php b/php/nws.php new file mode 100644 index 0000000..52ca1b7 --- /dev/null +++ b/php/nws.php @@ -0,0 +1,385 @@ + '')))) { + $query = "SELECT * FROM nws order by lastupdate asc"; + $result = pg_query($query) or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + + pg_free_result($result); +} + +if (isset($_GET['officestats11'])) { + // Get all unique lastupdate dates from the database + $date_query = "SELECT DISTINCT DATE(lastupdate) as unique_date + FROM nws + WHERE status = 'active' + AND office ~ 'WFO' + ORDER BY unique_date ASC"; // Changed from DESC to ASC + + $date_result = pg_query($dbconn, $date_query) + or die('Date query failed: ' . pg_last_error()); + + $datetime_points = []; + while ($row = pg_fetch_array($date_result, null, PGSQL_ASSOC)) { + $dt = DateTime::createFromFormat('Y-m-d', $row['unique_date']); + $dt->setTime(23, 59, 59); + $datetime_points[] = $dt->format('Y-m-d H:i:s'); + } + + pg_free_result($date_result); + + if (empty($datetime_points)) { + echo json_encode(['error' => 'No valid datetime points found in database']); + exit; + } + + // Debug: Log the datetime points + error_log("Processed datetime points: " . implode(', ', $datetime_points)); + + $query = "WITH latest_records AS ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY personid ORDER BY ABS(EXTRACT(EPOCH FROM (lastupdate - CAST($1 AS TIMESTAMP)))) ASC) AS rn + FROM nws + WHERE status = 'active' + AND lastupdate <= CAST($1 AS TIMESTAMP) + INTERVAL '1 day' + AND lastupdate >= CAST($1 AS TIMESTAMP) - INTERVAL '3 days' + AND office ~ 'WFO' + ), + otitle_counts AS ( + SELECT + office, + otitle, + COUNT(*) AS otitle_count + FROM latest_records + WHERE rn = 1 + GROUP BY office, otitle + ) + SELECT + lr.office, + COUNT(DISTINCT lr.personid) AS unique_person_count, + (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) + FROM (SELECT DISTINCT jsonb_build_object( + 'otitle', tc2.otitle, + 'count', tc2.otitle_count + ) AS json_obj + FROM otitle_counts tc2 + WHERE tc2.office = lr.office) AS subquery + ) AS title_counts_array + FROM latest_records lr + WHERE lr.rn = 1 + GROUP BY lr.office + ORDER BY unique_person_count DESC"; + + $results_array = []; + + // Execute query for each unique datetime + foreach ($datetime_points as $datetime) { + $result = pg_query_params($dbconn, $query, array($datetime)) + or die('Query failed: ' . pg_last_error()); + + $office_data = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $office_data[] = $line; + } + + // Store results with the datetime used + $results_array[] = [ + 'provided_datetime' => $datetime, + 'data' => $office_data + ]; + + pg_free_result($result); + } + + // Debug: Log before output + error_log("Final results: " . json_encode($results_array)); + + // Return JSON encoded results + echo json_encode($results_array); +} + +if (isset($_GET['officestats'])) { + if (isset($_GET['datetime'])) { + // Expecting datetime as comma-separated dates or JSON array + $input_dates = is_array($_GET['datetime']) + ? $_GET['datetime'] + : explode(',', $_GET['datetime']); + + // Process each date and set to end of day + $input_dates = array_unique($input_dates); + $input_dates = array_values($input_dates); + $datetime_points = []; + foreach ($input_dates as $date) { + $dt = DateTime::createFromFormat('m-d-Y', trim($date)); + if ($dt === false) { + error_log("Invalid date skipped: " . trim($date)); + continue; + } + $dt->setTime(23, 59, 59); + $datetime_points[] = $dt->format('Y-m-d H:i:s'); + } + + // Ensure uniqueness and reindex + $datetime_points = array_unique($datetime_points); + $datetime_points = array_values($datetime_points); + + // Debug: Log the datetime points + error_log("Processed datetime points: " . implode(', ', $datetime_points)); + + if (empty($datetime_points)) { + echo json_encode(['error' => 'No valid datetime points provided']); + exit; + } + + $query = "WITH latest_records AS ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY personid ORDER BY ABS(EXTRACT(EPOCH FROM (lastupdate - CAST($1 AS TIMESTAMP)))) ASC) AS rn + FROM nws + WHERE status = 'active' + AND lastupdate <= CAST($1 AS TIMESTAMP) + INTERVAL '1 day' + AND lastupdate >= CAST($1 AS TIMESTAMP) - INTERVAL '3 days' + AND office ~ 'WFO' + ), + otitle_counts AS ( + SELECT + office, + otitle, + COUNT(*) AS otitle_count + FROM latest_records + WHERE rn = 1 + GROUP BY office, otitle + ) + SELECT + lr.office, + COUNT(DISTINCT lr.personid) AS unique_person_count, + (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) + FROM (SELECT DISTINCT jsonb_build_object( + 'otitle', tc2.otitle, + 'count', tc2.otitle_count + ) AS json_obj + FROM otitle_counts tc2 + WHERE tc2.office = lr.office) AS subquery + ) AS title_counts_array + FROM latest_records lr + WHERE lr.rn = 1 + GROUP BY lr.office + ORDER BY unique_person_count DESC"; + + $results_array = []; + + // Execute query for each provided datetime + foreach ($datetime_points as $datetime) { + $result = pg_query_params($dbconn, $query, array($datetime)) + or die('Query failed: ' . pg_last_error()); + + $office_data = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $office_data[] = $line; + } + + // Store results with the datetime used + $results_array[] = [ + 'provided_datetime' => $datetime, + 'data' => $office_data + ]; + + pg_free_result($result); + } + + // Debug: Log before output + error_log("Final results: " . json_encode($results_array)); + + // Return JSON encoded results + echo json_encode($results_array); + } +} + +if (isset($_GET['regionstats'])) { + if (isset($_GET['datetime'])) { + // Expecting datetime as comma-separated dates or JSON array + $input_dates = is_array($_GET['datetime']) + ? $_GET['datetime'] + : explode(',', $_GET['datetime']); + + // Process each date and set to end of day + $input_dates = array_unique($input_dates); + $input_dates = array_values($input_dates); + $datetime_points = []; + foreach ($input_dates as $date) { + $dt = DateTime::createFromFormat('m-d-Y', trim($date)); + if ($dt === false) { + error_log("Invalid date skipped: " . trim($date)); + continue; + } + $dt->setTime(23, 59, 59); + $datetime_points[] = $dt->format('Y-m-d H:i:s'); + } + + // Ensure uniqueness and reindex + $datetime_points = array_unique($datetime_points); + $datetime_points = array_values($datetime_points); + + // Debug: Log the datetime points + error_log("Processed datetime points: " . implode(', ', $datetime_points)); + + if (empty($datetime_points)) { + echo json_encode(['error' => 'No valid datetime points provided']); + exit; + } + + $query = "WITH latest_records AS ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY personid ORDER BY ABS(EXTRACT(EPOCH FROM (lastupdate - CAST($1 AS TIMESTAMP)))) ASC) AS rn, + SUBSTRING(office FROM 'NWS/([EWPASC]R)') AS region + FROM nws + WHERE lastupdate <= CAST($1 AS TIMESTAMP) + INTERVAL '1 day' - INTERVAL '1 second' + AND lastupdate >= CAST($1 AS TIMESTAMP) - INTERVAL '3 days' + AND office ~ 'NWS/[EWPASC]R' + and status = 'active' + ), + otitle_counts AS ( + SELECT + region, + otitle, + COUNT(*) AS otitle_count + FROM latest_records + WHERE rn = 1 + GROUP BY region, otitle + ) + SELECT + lr.region, + COUNT(DISTINCT lr.personid) AS unique_person_count, + (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) + FROM (SELECT DISTINCT jsonb_build_object( + 'otitle', tc2.otitle, + 'count', tc2.otitle_count + ) AS json_obj + FROM otitle_counts tc2 + WHERE tc2.region = lr.region) AS subquery + ) AS title_counts_array + FROM latest_records lr + WHERE lr.rn = 1 + GROUP BY lr.region + ORDER BY unique_person_count DESC"; + + $results_array = []; + + // Execute query for each provided datetime + foreach ($datetime_points as $datetime) { + $result = pg_query_params($dbconn, $query, array($datetime)) + or die('Query failed: ' . pg_last_error()); + + $office_data = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $office_data[] = $line; + } + + // Store results with the datetime used + $results_array[] = [ + 'provided_datetime' => $datetime, + 'data' => $office_data + ]; + + pg_free_result($result); + } + + // Debug: Log before output + //error_log("Final results: " . json_encode($results_array)); + + // Return JSON encoded results + echo json_encode($results_array); + } +} + +if (isset($_GET['drilldown'])) { + if (isset($_GET['datetime'])) { + // Expecting datetime as comma-separated dates or JSON array + $input_dates = is_array($_GET['datetime']) + ? $_GET['datetime'] + : explode(',', $_GET['datetime']); + + // Process each date and set to end of day + $datetime_points = []; + foreach ($input_dates as $date) { + // Specify the exact format of your input date string + $dt = DateTime::createFromFormat('m-d-Y', trim($date)); // Adjust format as needed + if ($dt === false) { + // Handle invalid date + continue; + } + $dt->setTime(23, 59, 59); + $datetime_points[] = $dt->format('Y-m-d H:i:s'); + } + $datetime_points = array_unique($datetime_points); + $datetime_points = array_values($datetime_points); + + $query = "WITH latest_records AS ( + SELECT *, + ROW_NUMBER() OVER (PARTITION BY personid ORDER BY lastupdate DESC) AS rn + FROM nws + WHERE status = 'active' + AND lastupdate <= $1 + ), + otitle_counts AS ( + SELECT + office, + otitle, + COUNT(*) AS otitle_count + FROM latest_records + WHERE rn = 1 + GROUP BY office, otitle + ) + SELECT + lr.office, + COUNT(DISTINCT lr.personid) AS unique_person_count, + (SELECT ARRAY_AGG(json_obj ORDER BY json_obj->>'otitle' ASC) + FROM (SELECT DISTINCT jsonb_build_object( + 'otitle', tc2.otitle, + 'count', tc2.otitle_count + ) AS json_obj + FROM otitle_counts tc2 + WHERE tc2.office = lr.office) AS subquery + ) AS title_counts_array + FROM latest_records lr + WHERE lr.rn = 1 + GROUP BY lr.office + ORDER BY unique_person_count DESC"; + + $results_array = []; + + // Execute query for each provided datetime + foreach ($datetime_points as $datetime) { + $result = pg_query_params($dbconn, $query, array($datetime)) + or die('Query failed: ' . pg_last_error()); + + $office_data = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $office_data[] = $line; + } + + // Store results with the datetime used + $results_array[] = [ + 'provided_datetime' => $datetime, + 'data' => $office_data + ]; + + pg_free_result($result); + } + + // Return JSON encoded results + echo json_encode($results_array); + } +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/ohgo.php b/php/ohgo.php new file mode 100644 index 0000000..bb734c8 --- /dev/null +++ b/php/ohgo.php @@ -0,0 +1,23 @@ + now() - interval '48 hours' order by start asc;"; +$result = pg_query($query) or die('Query failed: ' . pg_last_error()); + +// Printing results in HTML +while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; +} +echo json_encode($array); + +// Free resultset +pg_free_result($result); + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/power.php b/php/power.php new file mode 100644 index 0000000..a225087 --- /dev/null +++ b/php/power.php @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/php/powerapi.php b/php/powerapi.php new file mode 100644 index 0000000..0425b8d --- /dev/null +++ b/php/powerapi.php @@ -0,0 +1,684 @@ + '')))) { + try { + $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) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $resultArray = pg_fetch_all($result); + + // Check if we got results + if ($resultArray && isset($resultArray[0]['json_build_object'])) { + header('Content-Type: application/json'); + echo $resultArray[0]['json_build_object']; + } else { + echo json_encode(['type' => 'FeatureCollection', 'features' => []]); + } + + pg_free_result($result); + } catch (Exception $e) { + http_response_code(500); + die('Query execution failed: ' . $e->getMessage()); + } +} + +if (isset($_GET['states'])) { + try { + $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, $query); + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $resultArray = pg_fetch_all($result); + + // Set proper JSON header and handle output + header('Content-Type: application/json'); + if ($resultArray && isset($resultArray[0]['jsonb_build_object'])) { + echo $resultArray[0]['jsonb_build_object']; + } else { + echo json_encode(['type' => 'FeatureCollection', 'features' => []]); + } + + pg_free_result($result); + } catch (Exception $e) { + http_response_code(500); + header('Content-Type: application/json'); + echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); + exit; + } +} + +if (isset($_GET['max'])) { + if (isset($_GET['start']) && isset($_GET['end'])) { + try { + $starttime = getParam('start'); + $endtime = getParam('end'); + + $query = " + SELECT DISTINCT ON (county, state) + max(outage) as max_outage, + county, + state + FROM ( + SELECT DISTINCT ON (county, state, update) + county, + state, + SUM(outages) as outage, + update as time, + SUM(served) as served + FROM countyoutages + WHERE update > $2 + AND update < $3 + AND cwa = $1 + GROUP BY county, state, update + ) as subquery + GROUP BY county, state + "; + + $result = pg_query_params( + $dbconn, + $query, + ['RLX', $starttime, $endtime] + ); + + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $results = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $results[] = $line; + } + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + header('Content-Type: application/json'); + http_response_code(500); + echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); + exit; + } + } else { + header('Content-Type: application/json'); + http_response_code(400); + echo json_encode(['error' => 'Both start and end parameters are required']); + } +} + +if (isset($_GET['county'])) { + try { + $query = " + WITH latest_update AS ( + SELECT MAX(update) as max_update FROM countyoutages + ) + SELECT + county, + state, + SUM(outages) as outage, + MAX(update) as time, + SUM(served) as served, + ROUND( + CASE + WHEN SUM(served) > 0 THEN (SUM(outages)::FLOAT / SUM(served)) * 100 + ELSE 0 + END::NUMERIC, 2 + ) as perout + FROM countyoutages + JOIN latest_update ON countyoutages.update = latest_update.max_update + WHERE cwa = $1 + GROUP BY county, state + ORDER BY county, state; + "; + + $result = pg_query_params($dbconn, $query, ['RLX']); + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $results = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $results[] = $line; + } + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + header('Content-Type: application/json'); + http_response_code(500); + echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); + exit; + } +} + +if (isset($_GET['countyarchive'])) { + if (isset($_GET['start']) && isset($_GET['end'])) { + try { + $starttime = getParam('start'); + $endtime = getParam('end'); + + $query = " + SELECT DISTINCT ON (county, state, update) + county, + state, + SUM(outages) as outage, + update as time, + SUM(served) as served + FROM countyoutages + WHERE update > $2 + AND update < $3 + AND cwa = $1 + GROUP BY county, state, update + "; + + $result = pg_query_params($dbconn, $query, ['RLX', $starttime, $endtime]); + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $results = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $results[] = $line; + } + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + header('Content-Type: application/json'); + http_response_code(500); + echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); + if (isset($result)) { + pg_free_result($result); + } + exit; + } + } else { + header('Content-Type: application/json'); + http_response_code(400); + echo json_encode(['error' => 'Both start and end parameters are required']); + } +} + +if (isset($_GET['archivepoint'])) { + try { + if (!isset($_GET['start']) || !isset($_GET['end'])) { + throw new Exception('Both start and end parameters are required'); + } + + $starttime = getParam('start'); + $endtime = getParam('end'); + + $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 + ) + ) + ORDER BY startguess ASC + ) + ) + FROM power + WHERE cwa = $1 + AND startguess > $2 + AND lastchange < $3 + "; + + $result = pg_query_params($dbconn, $query, ['RLX', $starttime, $endtime]); + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $resultArray = pg_fetch_all($result); + + header('Content-Type: application/json'); + if ($resultArray && isset($resultArray[0]['json_build_object'])) { + echo $resultArray[0]['json_build_object']; + } else { + echo json_encode(['type' => 'FeatureCollection', 'features' => []]); + } + + pg_free_result($result); + } catch (Exception $e) { + header('Content-Type: application/json'); + $statusCode = strpos($e->getMessage(), 'required') !== false ? 400 : 500; + http_response_code($statusCode); + echo json_encode(['error' => $e->getMessage()]); + if (isset($result)) { + pg_free_result($result); + } + exit; + } +} + +if(@$_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); + echo($resultArray[0]['json_build_object']); + pg_free_result($result); +} + +if (isset($_GET['svr']) && $_GET['svr'] === 'archive') { + try { + $result = null; + + if (isset($_GET['start']) && isset($_GET['end'])) { + $starttime = getParam('start'); + $endtime = getParam('end'); + + $query = " + 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 > $1 + AND endtime < $2 + "; + + $result = pg_query_params($dbconn, $query, [$starttime, $endtime]); + } elseif (!isset($_GET['start']) && !isset($_GET['end'])) { + $query = " + 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() - INTERVAL '24 hours' + AND endtime > NOW() - INTERVAL '24 hours' + "; + + $result = pg_query($dbconn, $query); + } else { + throw new Exception('Both start and end parameters are required together'); + } + + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $resultArray = pg_fetch_all($result); + + header('Content-Type: application/json'); + if ($resultArray && isset($resultArray[0]['json_build_object'])) { + echo $resultArray[0]['json_build_object']; + } else { + echo json_encode(['type' => 'FeatureCollection', 'features' => []]); + } + + pg_free_result($result); + } catch (Exception $e) { + if (isset($result)) { + pg_free_result($result); + } + header('Content-Type: application/json'); + $statusCode = strpos($e->getMessage(), 'required') !== false ? 400 : 500; + http_response_code($statusCode); + echo json_encode(['error' => $e->getMessage()]); + exit; + } +} + +if (isset($_GET['svrpolys'])) { + try { + $query = " + SELECT + vtec, + outagesvalid, + polygonpop, + outagesbuffer, + lsrids + FROM svr + WHERE EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - endtime)) / 60 / 60 / 24 < 60 + "; + + $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; + } + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + if (isset($result)) { + pg_free_result($result); + } + header('Content-Type: application/json'); + http_response_code(500); + echo json_encode(['error' => $e->getMessage()]); + exit; + } +} + +if (isset($_GET['powerids'])) { + try { + $powerids = $_GET['powerids']; + + // Validate input exists and isn't empty + if (empty($powerids)) { + throw new Exception('No power IDs provided'); + } + + // Convert comma-separated string to array and sanitize + $poweridArray = explode(',', $powerids); + $sanitizedIds = array_filter(array_map('intval', $poweridArray)); + + if (empty($sanitizedIds)) { + throw new Exception('Invalid power ID format'); + } + + // Prepare placeholders for the query + $placeholders = implode(',', array_map(function($i) { return '$' . $i; }, range(1, count($sanitizedIds)))); + + $query = " + SELECT + lat, + lon, + lastchange, + startguess, + peakoutage, + cause, + lsrtime, + lsrref, + (lsrtime AT TIME ZONE 'America/New_York')::timestamp as lsrlocal + FROM power + WHERE id IN ($placeholders) + "; + + $result = pg_query_params($dbconn, $query, $sanitizedIds); + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $results = pg_fetch_all($result) ?: []; + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + if (isset($result)) { + pg_free_result($result); + } + header('Content-Type: application/json'); + $statusCode = strpos($e->getMessage(), 'Invalid') !== false ? 400 : 500; + http_response_code($statusCode); + echo json_encode(['error' => $e->getMessage()]); + exit; + } +} + +if (isset($_GET['poweridsgeojson'])) { + try { + $powerids = $_GET['poweridsgeojson']; + + if (empty($powerids)) { + throw new Exception('No power IDs provided'); + } + + // Convert and sanitize power IDs + $poweridArray = explode(',', $powerids); + $sanitizedIds = array_filter(array_map('intval', $poweridArray)); + + if (empty($sanitizedIds)) { + throw new Exception('Invalid power ID format'); + } + + // Prepare placeholders + $placeholders = implode(',', array_map(function($i) { return '$' . $i; }, range(1, count($sanitizedIds)))); + + $query = " + SELECT json_build_object( + 'type', 'FeatureCollection', + 'features', json_agg( + json_build_object( + 'type', 'Feature', + 'geometry', ST_AsGeoJSON(realgeom)::json, + 'properties', json_build_object( + 'id', id, + 'time', (startguess AT TIME ZONE 'UTC')::timestamp, + 'county', county, + 'state', state, + 'cause', cause, + 'outage', peakoutage, + 'lsrtime', (lsrtime AT TIME ZONE 'UTC')::timestamp + ) + ) ORDER BY startguess ASC + ) + ) + FROM power + WHERE id IN ($placeholders) + "; + + $result = pg_query_params($dbconn, $query, $sanitizedIds); + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $resultArray = pg_fetch_all($result); + + header('Content-Type: application/json'); + if ($resultArray && isset($resultArray[0]['json_build_object'])) { + echo $resultArray[0]['json_build_object']; + } else { + echo json_encode(['type' => 'FeatureCollection', 'features' => []]); + } + + pg_free_result($result); + } catch (Exception $e) { + if (isset($result)) { + pg_free_result($result); + } + header('Content-Type: application/json'); + $statusCode = strpos($e->getMessage(), 'Invalid') !== false ? 400 : 500; + http_response_code($statusCode); + echo json_encode(['error' => $e->getMessage()]); + exit; + } +} + +if (isset($_GET['polygongeojson'])) { + $result = null; // Initialize result to null for catch block safety + try { + $polygonGeoJsonString = $_GET['polygongeojson']; + + if (empty($polygonGeoJsonString)) { + throw new Exception('No GeoJSON polygon provided', 400); // Use exception code for status + } + + // 1. Validate if the input is valid JSON + // We decode here primarily to check JSON validity. + // We'll pass the *original string* to PostGIS's ST_GeomFromGeoJSON for robustness. + $polygonGeoJson = json_decode($polygonGeoJsonString); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception('Invalid JSON format: ' . json_last_error_msg(), 400); + } + + // 2. Optional: Basic structural validation (can rely on PostGIS for full validation) + if (!is_object($polygonGeoJson) || !isset($polygonGeoJson->type) || !in_array($polygonGeoJson->type, ['MultiPolygon', 'Polygon'])) { + // Allow both Polygon and MultiPolygon for flexibility? Or stick to MultiPolygon? + // Let's allow Polygon too, as ST_Within works with both. + // If you strictly need *only* MultiPolygon, change the check. + throw new Exception('Input GeoJSON must be of type Polygon or MultiPolygon.', 400); + } + if (!isset($polygonGeoJson->coordinates) || !is_array($polygonGeoJson->coordinates)) { + throw new Exception('Input GeoJSON must have a coordinates array.', 400); + } + + // 3. Prepare the PostgreSQL Query using PostGIS functions + // - ST_GeomFromGeoJSON($1): Parses the input GeoJSON string. + // - ST_SetSRID(..., 4326): Assigns the WGS84 SRID (standard for GeoJSON). Adjust if your data uses a different SRID. + // - ST_Within(realgeom, ...): Checks if the power outage geometry is within the provided polygon geometry. + // - Ensure your 'realgeom' column has a spatial index for performance! + $query = " + SELECT json_build_object( + 'type', 'FeatureCollection', + 'features', json_agg( + json_build_object( + 'type', 'Feature', + 'geometry', ST_AsGeoJSON(realgeom)::json, + 'properties', json_build_object( + 'id', id, + 'time', (startguess AT TIME ZONE 'UTC')::timestamp, + 'county', county, + 'state', state, + 'cause', cause, + 'outage', peakoutage, + 'lsrtime', (lsrtime AT TIME ZONE 'UTC')::timestamp + ) + ) ORDER BY startguess ASC -- Optional ordering + ) + ) + FROM power + WHERE ST_Within(realgeom, ST_SetSRID(ST_GeomFromGeoJSON($1), 4326)) + "; + // Note: If 'realgeom' might be NULL, you might add "AND realgeom IS NOT NULL" + + // 4. Execute the query with the GeoJSON string as a parameter + $params = [$polygonGeoJsonString]; + $result = pg_query_params($dbconn, $query, $params); + + if ($result === false) { + // Check for specific PostGIS errors related to invalid GeoJSON input + $pgError = pg_last_error($dbconn); + if (strpos($pgError, 'invalid GeoJSON representation') !== false || strpos($pgError, 'ParseException') !== false || strpos($pgError, 'Invalid polygon') !== false) { + throw new Exception('Invalid GeoJSON geometry data provided: ' . $pgError, 400); + } else { + // Throw a generic server error for other query failures + throw new Exception('Query failed: ' . $pgError, 500); + } + } + + // 5. Fetch and Output Results + $resultArray = pg_fetch_all($result); + + header('Content-Type: application/json'); + if ($resultArray && isset($resultArray[0]['json_build_object'])) { + // Ensure null result from json_agg (no features found) returns empty array + $outputJson = $resultArray[0]['json_build_object']; + $outputData = json_decode($outputJson, true); + if (isset($outputData['features']) && $outputData['features'] === null) { + $outputData['features'] = []; + echo json_encode($outputData); + } else { + echo $outputJson; // Output the JSON directly from Postgres + } + } else { + // Should ideally be handled by the check above, but as a fallback + echo json_encode(['type' => 'FeatureCollection', 'features' => []]); + } + + pg_free_result($result); + + } catch (Exception $e) { + // 6. Error Handling + if (isset($result) && is_resource($result)) { // Check if $result is a valid resource before freeing + pg_free_result($result); + } + header('Content-Type: application/json'); + // Use exception code for status if provided (>=400), default to 500 + $statusCode = ($e->getCode() >= 400 && $e->getCode() < 600) ? $e->getCode() : 500; + http_response_code($statusCode); + echo json_encode(['error' => $e->getMessage()]); + exit; // Stop script execution after error + } +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/powerapitest.php b/php/powerapitest.php new file mode 100644 index 0000000..706acc7 --- /dev/null +++ b/php/powerapitest.php @@ -0,0 +1,147 @@ + '')))) { + $result = pg_query_params($dbconn, + "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))order by startguess asc)) FROM power WHERE cwa = $1 and active = true", + array('RLX')) or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); +} + +//county/state max +if($_GET['max'] ?? null) { + if($_GET['start'] ?? null) { + $starttime = pg_escape_string($_GET['start']); + if($_GET['end'] ?? null) { + $endtime = pg_escape_string($_GET['end']); + + $result = pg_query_params($dbconn, + "select distinct on (county,state) max(outage),county,state from (select distinct on (county,state,update) county,state,sum(outages) as outage, update as time, sum(served) as served from countyoutages where update > $2 and update < $3 and cwa = $1 group by county,state,update) as potato group by county,state", + array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); + + $array = array(); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + } + } +} + +if (isset($_GET['county'])) { + try { + $query = " + SELECT DISTINCT ON (county, state) + county, + state, + SUM(outages) as outage, + update as time, + SUM(served) as served, + ROUND( + CAST( + CASE + WHEN SUM(served) = 0 THEN NULL + ELSE (SUM(outages)::FLOAT / SUM(served)) * 100 + END AS NUMERIC + ), 2 + ) as perout + FROM countyoutages + WHERE update = (SELECT MAX(update) FROM countyoutages) + AND (cwa = $1 OR cwa = $2 OR cwa = $3 OR cwa = $4 OR cwa = $5 OR cwa = $6 OR cwa = $7) + GROUP BY county, state, update + "; + + $result = pg_query_params($dbconn, $query, ['RLX','JKL','ILN','PBZ','MRX','LWX','RNK']); + if ($result === false) { + throw new Exception('Query failed: ' . pg_last_error()); + } + + $results = []; + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $results[] = $line; + } + + header('Content-Type: application/json'); + echo json_encode($results); + + pg_free_result($result); + } catch (Exception $e) { + header('Content-Type: application/json'); + http_response_code(500); + echo json_encode(['error' => 'Query execution failed: ' . $e->getMessage()]); + exit; + } +} + +//county archive +if($_GET['countyarchive'] ?? null) { + if($_GET['start'] ?? null) { + $starttime = pg_escape_string($_GET['start']); + if($_GET['end'] ?? null) { + $endtime = pg_escape_string($_GET['end']); + + $result = pg_query_params($dbconn, + "select distinct on (county,state,update) county,state,sum(outages) as outage, update as time, sum(served) as served from countyoutages where update > $9 and update < $10 and (cwa = $1 or cwa = $2 or cwa = $3 or cwa = $4 or cwa = $5 or cwa = $6 or cwa = $7 or cwa = $8) group by county,state,update", + array('RLX','JKL','ILN','PBZ','MRX','LWX','RNK','CTP',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); + + $array = array(); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + } + } +} + +//Archive point data +if($_GET['archivepoint'] ?? null) { + $starttime = pg_escape_string($_GET['start']); + $endtime = pg_escape_string($_GET['end']); + $result = pg_query_params($dbconn, + "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))order by startguess asc)) FROM power WHERE cwa = $1 and startguess > $2 and lastchange < $3", + array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); +} + +// Svr related functionality +if($_GET['svr'] ?? null =='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); + echo($resultArray[0]['json_build_object']); +} + +if($_GET['svr'] ?? null == 'archive') { + if($_GET['start'] ?? null) { + $starttime = pg_escape_string($_GET['start']); + if($_GET['end'] ?? null) { + $endtime = pg_escape_string($_GET['end']); + + $result = pg_query_params($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 > $1 and endtime < $2" + ,array($starttime,$endtime)) or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); + } + } + + if(!isset($_GET['start']) && !isset($_GET['end'])) { + $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() - interval '24 hours' and endtime > now() - interval '24 hours'") or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); + } +} + +pg_free_result($result); + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/searchapi.php b/php/searchapi.php new file mode 100644 index 0000000..b25994e --- /dev/null +++ b/php/searchapi.php @@ -0,0 +1,97 @@ + '')))) { + $result = pg_query_params($dbconn, + "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))order by startguess asc)) FROM power WHERE cwa = $1 and active = true", + array('RLX')) or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); +} + +//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()); + + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); +} + +//county archive +if(isset($_GET['countyarchive'])) { + if(isset($_GET['start'])) { + $starttime = getParam('start'); + if(isset($_GET['end'])) { + $endtime = getParam('end'); + + $result = pg_query_params($dbconn, + "SELECT county,state, update as time, county, state, outages as outage,served FROM countyoutages where cwa = $1 and update > $2 and update < $3 order by update asc", + array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); + + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + } + } +} + +//Archive point data +if(isset($_GET['archivepoint'])) { + if(isset($_GET['start'])) { + $starttime = getParam('start'); + if(isset($_GET['end'])) { + $endtime = getParam('end'); + $result = pg_query_params($dbconn, + "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))order by startguess asc)) FROM power WHERE cwa = $1 and startguess > $2 and lastchange < $3" + ,array('RLX',$starttime,$endtime)) or die('Query failed: ' . pg_last_error()); + + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); + } + } +} + +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); + echo($resultArray[0]['json_build_object']); +} + +if(isset($_GET['svr']) && $_GET['svr'] === 'archive') { + if(isset($_GET['start'])) { + $starttime = getParam('start'); + if(isset($_GET['end'])) { + $endtime = getParam('end'); + + $result = pg_query_params($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 > $1 and endtime < $2" + ,array($starttime,$endtime)) or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); + } + } + + if(!isset($_GET['start']) && !isset($_GET['end'])) { + $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() - interval '24 hours' and endtime > now() - interval '24 hours'") or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray[0]['json_build_object']); + } +} + +pg_free_result($result); + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/single.php b/php/single.php new file mode 100644 index 0000000..74f6dc4 --- /dev/null +++ b/php/single.php @@ -0,0 +1,43 @@ + "No camid specified")); + exit; +} + +$camid = $_GET['camid']; + +// Performing SQL query +$query = "SELECT *, COALESCE(hydro, false) as hydro, COALESCE(airport, false) as airport FROM cams WHERE camid = $1"; + +// Use pg_query_params to safely bind the $camid variable +$result = pg_query_params($dbconn, $query, array($camid)) + or die('Query failed: ' . pg_last_error()); + +// Processing results +$array = array(); +while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + // Ensure hydro is a proper boolean + $line['hydro'] = ($line['hydro'] === 't' || $line['hydro'] === true); + // Ensure airport is a proper boolean + $line['airport'] = ($line['airport'] === 't' || $line['airport'] === true); + $array[] = $line; +} + +// Output the ORIGINAL full array (including errorcode) to the client +echo json_encode($array); + +// Free resultset +pg_free_result($result); + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/stormdata.php b/php/stormdata.php new file mode 100644 index 0000000..a7eacb4 --- /dev/null +++ b/php/stormdata.php @@ -0,0 +1,69 @@ + 'Invalid request method. Only POST is allowed.']); + exit; +} +?> \ No newline at end of file diff --git a/php/update_field.php b/php/update_field.php new file mode 100644 index 0000000..f0474b3 --- /dev/null +++ b/php/update_field.php @@ -0,0 +1,43 @@ + false, 'message' => 'Invalid input']); + exit; +} + +// Check if the field is valid +if (!in_array($field, ['hydro', 'airport'])) { + echo json_encode(['success' => false, 'message' => 'Invalid field']); + exit; +} + +// Convert to proper boolean for PostgreSQL +// JavaScript sends true/false as strings 'true' or 'false' +$value_bool = ($value === 'true'); + +// Update the field value in the database - use boolean directly +// PostgreSQL accepts 't'/'f' for boolean values +$query = "UPDATE cams SET $field = $1 WHERE camid = $2"; +$result = pg_query_params($dbconn, $query, array($value_bool ? 't' : 'f', $camid)); + +if ($result) { + echo json_encode(['success' => true]); +} else { + $error = pg_last_error($dbconn); + echo json_encode(['success' => false, 'message' => $error]); +} + +// Closing connection +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/ver.php b/php/ver.php new file mode 100644 index 0000000..703563e --- /dev/null +++ b/php/ver.php @@ -0,0 +1,82 @@ + '')))) { + $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); + header('Content-Type: application/json; charset=utf-8'); + echo($resultArray[0]['jsonb_build_object']); + pg_free_result($result); +} + +if (isset($_GET['lsrslist'])) { + $result = pg_query($dbconn,"SELECT * from simplever") or die('Query failed: ' . pg_last_error()); + while ($line = pg_fetch_array($result, null, PGSQL_ASSOC)) { + $array[] = $line; + } + echo json_encode($array); + pg_free_result($result); +} + +if (isset($_GET['reset'])) { + $result = pg_query($dbconn,"truncate simplever") or die('Query failed: ' . pg_last_error()); + $resultArray = pg_fetch_all($result); + echo($resultArray); +} + +if (isset($_GET['lsrs'])) { + if (isset($_GET['zone'])) { + $zone = $_GET['zone']; + if (isset($_GET['lsr'])) { + $lsr = (int) $_GET['lsr']; + } else { + $lsr = 1; + } + if (isset($_GET['lsrs'])) { + $dir = $_GET['dir']; + } + + 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()); + $resultArray = pg_fetch_all($result); + } 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); + } + pg_free_result($result); + } +} + +if (isset($_GET['inc'])) { + if ($_GET['inc'] == 'true') { + $hideflag = 'true'; + } else { + $hideflag = 'false'; + } + $id = (int) $_GET['id']; + $query = "UPDATE reports SET hide = $1 WHERE id = $2"; + $result = pg_query_params($dbconn, $query, array($hideflag, $id)) or die('Query failed: ' . pg_last_error()); + pg_free_result($result); +} + +if (isset($_GET['hide'])) { + if ($_GET['hide'] == 'true') { + $hideflag = 'true'; + } else { + $hideflag = 'false'; + } + $id = (int) $_GET['id']; + $query = "UPDATE reports SET hide = $1 WHERE id = $2"; + $result = pg_query_params($dbconn, $query, array($hideflag, $id)) or die('Query failed: ' . pg_last_error()); + pg_free_result($result); +} + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/php/warntrack.php b/php/warntrack.php new file mode 100644 index 0000000..4578bb7 --- /dev/null +++ b/php/warntrack.php @@ -0,0 +1,20 @@ + '')))) { + $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); + echo($resultArray[0]['json_build_object']); +} + +pg_free_result($result); + +// Close database connection when needed +if (isset($dbconn)) { + pg_close($dbconn); +} +?> \ No newline at end of file diff --git a/stormdata_backup.html b/stormdata_backup.html deleted file mode 100644 index 1d80917..0000000 --- a/stormdata_backup.html +++ /dev/null @@ -1,3081 +0,0 @@ - - - - - - NWS VTEC Browser - - - - - - - - - - - - -
-
-

Select Products

- - - - - -

Options

- - - - - - -
-

Export Options

- - - Note: Reports not from LSRs (power outage, DOT, 911, etc) will be filtered if an LSR has already been issued within 1 mile of them. -
-
-
-
-
- -

Select one or more warnings and click "Generate Summary".

- -
-
-
- - - -