Files
test/main.php
2025-12-08 22:25:45 +00:00

1571 lines
54 KiB
PHP

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