From a7b6c529a938f0559e2dbe9b9317abafd4692ecd Mon Sep 17 00:00:00 2001 From: John Peck Date: Mon, 8 Dec 2025 22:49:18 +0000 Subject: [PATCH] try again --- API_DOCUMENTATION.md | 596 ---------------- main.php | 1571 ------------------------------------------ 2 files changed, 2167 deletions(-) delete mode 100644 API_DOCUMENTATION.md delete mode 100644 main.php diff --git a/API_DOCUMENTATION.md b/API_DOCUMENTATION.md deleted file mode 100644 index 7fca711..0000000 --- a/API_DOCUMENTATION.md +++ /dev/null @@ -1,596 +0,0 @@ -# NWS API Documentation - Integrated Main Endpoint - -This document describes all available endpoints in the integrated `main.php` file, which consolidates functionality from multiple separate PHP scripts. - -## Base URL -``` -/main.php -``` - -## General Information -- **Content-Type**: `application/json` (unless otherwise specified) -- **CORS**: Enabled with `Access-Control-Allow-Origin: *` -- **Methods**: GET, POST, OPTIONS -- **Database**: PostgreSQL (connection: `host=localhost dbname=nws user=nws password=nws`) - -## Utility Functions (No Direct Access) - -The following utility functions are included at the top of the file for compatibility: -- `connect_db()` - Database connection -- `send_error($code, $message)` - JSON error response -- `send_json($data)` - JSON success response -- `send_geojson($features)` - GeoJSON response - -## GET Endpoints - -### 1. Single Camera Information -**Endpoint**: `?camid={id}` - -**Description**: Get detailed information for a single camera. - -**Parameters**: -- `camid` (string, required): Camera ID - -**Response**: Camera object with hydro/airport boolean fields - -**Example**: `/main.php?camid=123` - ---- - -### 2. Active Cameras -**Endpoint**: `?cams` or no parameters - -**Description**: Get all active cameras with recent successful connections. - -**Response**: Array of camera objects - -**Example**: `/main.php?cams` - ---- - -### 3. Camera List -**Endpoint**: `?camlist` - -**Description**: Get comprehensive list of all active cameras. - -**Response**: -```json -{ - "status": "success", - "data": [...], - "count": 150 -} -``` - -**Example**: `/main.php?camlist` - ---- - -### 4. Camera API with Filters -**Endpoint**: `?cams` with bounding box parameters - -**Parameters**: -- `lat1`, `lon1`, `lat2`, `lon2` (float, required): Bounding box coordinates -- `elevbottom`, `elevtop` (float, required): Elevation range - -**Response**: Filtered camera array - -**Example**: `/main.php?cams&lat1=38.0&lon1=-82.0&lat2=39.0&lon2=-81.0&elevbottom=100&elevtop=1000` - ---- - -### 5. Camera Static Search -**Endpoint**: `?camstatic` - -**Parameters**: -- `lat1`, `lon1`, `radius` (float, required): Center point and radius for radius search -- `camstatic=bbox` with `lat1`, `lon1`, `lat2`, `lon2`, `elevbottom`, `elevtop`: Bounding box search - -**Response**: Cameras within specified area - -**Example**: `/main.php?camstatic=radius&lat1=38.5&lon1=-81.5&radius=50` - ---- - -### 6. Camera Database Count -**Endpoint**: `?camdb` - -**Response**: Total count of camera records - -**Example**: `/main.php?camdb` - ---- - -### 7. Camera Circles -**Endpoint**: `?camcircle` - -**Description**: Get camera locations as buffer circles (8000m radius). - -**Response**: GeoJSON FeatureCollection with circular buffers - -**Example**: `/main.php?camcircle` - ---- - -### 8. Weather Station Data -**Endpoint**: `?outside` or `?outsideold` - -**Description**: Get current weather station observations. - -**Response**: Array of weather station data - -**Example**: `/main.php?outside` - ---- - -### 9. Fire Data -**Endpoint**: `?fire` - -**Description**: Get active wildfire information. - -**Response**: GeoJSON FeatureCollection of fire data - -**Example**: `/main.php?fire` - ---- - -### 10. Individual Camera Images -**Endpoint**: `?camid={id}&dtg={datetime}&camimages={count}` - -**Parameters**: -- `camid` (string, required): Camera ID -- `dtg` (string, optional): End time for historical images (YYYY-MM-DD HH:MM UTC) -- `camimages` (int, optional): Number of images (default: 20) - -**Response**: Array of camera image records - -**Example**: `/main.php?camid=123&camimages=10` - ---- - -### 11. OHGO Data -**Endpoint**: `?ohgo` - -**Description**: Get Ohio Department of Transportation road information. - -**Response**: Array of OHGO road status data - -**Example**: `/main.php?ohgo` - ---- - -### 12. Power Outage Data -**Endpoint**: `?power` - -**Description**: Get current power outage information for RLX CWA. - -**Response**: Array of power outage points - -**Example**: `/main.php?power` - ---- - -### 13. Power API (Advanced) -**Endpoint**: Various parameters for power data - -**Parameters**: -- `states`: Get state boundaries GeoJSON -- `county`: Get county outage summary -- `max` with `start`, `end`: Get maximum outage by county for date range -- `countyarchive` with `start`, `end`: County archive data -- `archivepoint` with `start`, `end`: Point outage archive -- `svr=current`: Current severe weather warnings -- `svrpolys`: Warning polygon metadata -- `powerids={ids}`: Specific outage details -- `poweridsgeojson={ids}`: Specific outage GeoJSON -- `polygongeojson={geojson}`: Outages within polygon - -**Response**: Varies by endpoint (JSON or GeoJSON) - -**Example**: `/main.php?states` - ---- - -### 14. Local Storm Reports (LSR) -**Endpoint**: Various LSR-related parameters - -**Parameters**: -- `ohgo`: OHGO data as GeoJSON -- `ohgotable`: OHGO tabular data -- `vtec={code}`: Warning polygon by VTEC code -- `reports={code}`: Reports within warning polygon -- `outages={code}`: Power outages within warning polygon -- `verify`: Verification table data -- `stats`: Report statistics by county -- `metars` with `start`, `end`: METAR data -- `news`, `news2`, `news3`: News data with various filtering -- `newsarchive` with search parameters: Archived news -- `wv511`: West Virginia 511 data -- `ky511`: Kentucky 511 data -- `getCombinedTable`: Combined road incident table -- `updater` with `table`, `id`, `lsr`/`hide`: Update incident flags - -**Response**: Varies by endpoint - -**Example**: `/main.php?ohgo` - ---- - -### 15. NWS Staff Data -**Endpoint**: `?officestats`, `?regionstats`, `?drilldown` - -**Parameters**: -- `datetime`: Date for statistics (m-d-Y format) - -**Response**: NWS office staffing statistics - -**Example**: `/main.php?officestats` - ---- - -### 16. Verification Data -**Endpoint**: Various verification parameters - -**Parameters**: -- `lsrslist`: Simple verification list -- `reset`: Clear verification table -- `lsrs` with `zone`, `lsr`, `dir`: Update verification counts -- `inc`/`hide` with `id`, `true`/`false`: Update report visibility - -**Response**: Verification operation results - -**Example**: `/main.php?lsrslist` - ---- - -### 17. Warning Tracking -**Endpoint**: `?warntrack` - -**Description**: Get active warning tracking information for KRLX. - -**Response**: GeoJSON FeatureCollection of warning tracks - -**Example**: `/main.php?warntrack` - ---- - -## POST Endpoints - -### 1. MP4/GIF Creation -**Endpoint**: POST with form data - -**Parameters**: -- `data` (array, required): Array of image file paths -- `images` (int, required): Number of images -- `delay` (int, required): Frame delay in milliseconds -- `lastdelay` (int, required): Last frame delay -- `maxh`, `maxv` (int, optional): Maximum dimensions - -**Response**: Base64-encoded GIF data - -**Content-Type**: Not JSON (binary/gif) - ---- - -### 2. Update Camera Field -**Endpoint**: POST with form data - -**Parameters**: -- `camid` (string, required): Camera ID -- `field` (string, required): Field name ('hydro' or 'airport') -- `value` (string, required): New value ('true' or 'false') - -**Response**: -```json -{ - "success": true -} -``` - ---- - -### 3. Admin Functions -**Endpoint**: POST with form data - -**Parameters**: -- `action` (string, required): Action type - - `checkurl`: Check if URL exists - - `newcam`: Add new camera - -**For `checkurl`**: -- `url` (string, required): URL to check - -**For `newcam`**: -- `url`, `lat`, `lon`, `description`, `method`, `permission`, `owner`, `email` (all required) - -**Response**: Operation result - ---- - -### 4. Storm Data API -**Endpoint**: POST with JSON data - -**Content-Type**: `application/json` - -**Request Body**: -```json -{ - "request_type": "ohgo|ohgonopoly|power|powernopoly|wupoly|campoly", - "start_time": "2025-01-01 00:00:00", - "end_time": "2025-01-02 00:00:00", - "area_geojson": {...}, - "buffer": 2, - "outage_threshold": 10, - "polygons": ["POLYGON(...)"], - "camimages": 20 -} -``` - -**Request Types**: -- `ohgo`: OHGO data within GeoJSON polygon -- `ohgonopoly`: OHGO data without polygon filter -- `power`: Power outages within GeoJSON polygon -- `powernopoly`: Power outages without polygon filter -- `wupoly`: Weather Underground data within polygons -- `campoly`: Camera images within GeoJSON polygon - -**Response**: GeoJSON FeatureCollection - ---- - -## Error Handling - -All endpoints return consistent error responses: - -```json -{ - "error": "Error message" -} -``` - -Common HTTP status codes: -- `200`: Success -- `400`: Bad Request (missing/invalid parameters) -- `404`: Not Found -- `500`: Internal Server Error (database/query issues) - -## Migration Guide for Existing Callers - -### Simple URL Changes -Most existing callers only need to update the filename in their requests: - -**Before:** -``` -https://your-server/single.php?camid=123 -``` - -**After:** -``` -https://your-server/main.php?camid=123 -``` - -### Specific Migration Instructions - -#### 1. single.php → main.php -**No parameter changes needed** -- Existing calls: `single.php?camid={id}` -- New calls: `main.php?camid={id}` - -#### 2. cam.php → main.php -**No parameter changes needed** -- Existing calls: `cam.php` or `cam.php?cams` -- New calls: `main.php` or `main.php?cams` - -#### 3. camlist.php → main.php -**Parameter change required** -- Existing calls: `camlist.php` -- New calls: `main.php?camlist` - -#### 4. camapi.php → main.php -**No parameter changes needed** -- Existing calls: `camapi.php?cams=1&lat1=...` -- New calls: `main.php?cams=1&lat1=...` - -#### 5. camcircle.php → main.php -**Parameter change required** -- Existing calls: `camcircle.php` -- New calls: `main.php?camcircle` - -#### 6. camobs.php → main.php -**No parameter changes needed** -- Existing calls: `camobs.php?camstatic=radius&lat1=...` -- New calls: `main.php?camstatic=radius&lat1=...` - -#### 7. db.php → main.php -**No parameter changes needed** -- Existing calls: `db.php?outside` -- New calls: `main.php?outside` -- Existing calls: `db.php?outsideold` -- New calls: `main.php?outsideold` - -#### 8. fire.php → main.php -**Parameter change required** -- Existing calls: `fire.php` -- New calls: `main.php?fire` - -#### 9. individualcam.php → main.php -**No parameter changes needed** -- Existing calls: `individualcam.php?camid=123&dtg=...` -- New calls: `main.php?camid=123&dtg=...` - -#### 10. lsr.php → main.php -**No parameter changes needed** -- Existing calls: `lsr.php?ohgo` -- New calls: `main.php?ohgo` -- Existing calls: `lsr.php?vtec=...` -- New calls: `main.php?vtec=...` - -#### 11. mp4.php → main.php -**No parameter changes needed (POST data remains the same)** -- Existing calls: POST to `mp4.php` -- New calls: POST to `main.php` - -#### 12. nws.php → main.php -**No parameter changes needed** -- Existing calls: `nws.php?officestats` -- New calls: `main.php?officestats` - -#### 13. ohgo.php → main.php -**Parameter change required** -- Existing calls: `ohgo.php` -- New calls: `main.php?ohgo` - -#### 14. power.php → main.php -**Parameter change required** -- Existing calls: `power.php` -- New calls: `main.php?power` - -#### 15. powerapi.php → main.php -**No parameter changes needed** -- Existing calls: `powerapi.php?states` -- New calls: `main.php?states` -- Existing calls: `powerapi.php?county` -- New calls: `main.php?county` - -#### 16. searchapi.php → main.php -**No parameter changes needed** -- Existing calls: `searchapi.php?county` -- New calls: `main.php?county` - -#### 17. stormdata.php → main.php -**No parameter changes needed (POST JSON remains the same)** -- Existing calls: POST to `stormdata.php` -- New calls: POST to `main.php` - -#### 18. update_field.php → main.php -**No parameter changes needed (POST data remains the same)** -- Existing calls: POST to `update_field.php` -- New calls: POST to `main.php` - -#### 19. ver.php → main.php -**No parameter changes needed** -- Existing calls: `ver.php?lsrslist` -- New calls: `main.php?lsrslist` - -#### 20. warntrack.php → main.php -**Parameter change required** -- Existing calls: `warntrack.php` -- New calls: `main.php?warntrack` - -### Files NOT Requiring Changes - -#### one.php -This file contains HTML/JavaScript interface and should remain as-is. It makes calls to other endpoints that have been integrated. - -### Bulk Update Script - -For systems with many hardcoded references, use this sed command pattern: - -```bash -# Update all .php files to use main.php -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/single\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/cam\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/camapi\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/camlist\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/camobs\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/db\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/fire\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/individualcam\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/lsr\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/mp4\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/nws\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/ohgo\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/power\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/powerapi\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/searchapi\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/stormdata\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/update_field\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/ver\.php/main.php/g' {} \; -find /path/to/your/code -name "*.php" -type f -exec sed -i 's/warntrack\.php/main.php/g' {} \; -``` - -### Testing Migration - -After updating references, test key endpoints: - -```bash -# Test basic camera functionality -curl "http://your-server/main.php?camid=123" - -# Test admin functions -curl -X POST "http://your-server/main.php" -d "action=checkurl&url=http://example.com" - -# Test storm data API -curl -X POST "http://your-server/main.php" \ - -H "Content-Type: application/json" \ - -d '{"request_type":"power","start_time":"2025-01-01","area_geojson":"{\"type\":\"Polygon\",\"coordinates\":[[[-82,38],[-81,38],[-81,39],[-82,39],[-82,38]]}"}' -``` - -## Original Files Consolidated - -This integrated file consolidates the following original scripts: -- `admin.php` - Admin functions -- `cam.php` - Active cameras -- `camapi.php` - Camera API with filters -- `camcircle.php` - Camera buffer circles -- `camlist.php` - Camera list -- `camobs.php` - Camera observations -- `db.php` - Weather station data -- `fire.php` - Fire data -- `individualcam.php` - Individual camera images -- `lsr.php` - Local storm reports -- `mp4.php` - GIF creation -- `nws.php` - NWS staff data -- `ohgo.php` - OHGO road data -- `one.php` - HTML interface (not integrated) -- `power.php` - Power outages -- `powerapi.php` - Advanced power API -- `powerapitest.php` - Power API test -- `searchapi.php` - Search API -- `single.php` - Single camera info -- `stormdata.php` - Storm data API -- `update_field.php` - Camera field updates -- `ver.php` - Verification data -- `warntrack.php` - Warning tracking - -## Usage Examples - -### Get all active cameras: -```bash -curl "http://your-server/main.php?cams" -``` - -### Get camera within bounding box: -```bash -curl "http://your-server/main.php?cams&lat1=38.0&lon1=-82.0&lat2=39.0&lon2=-81.0&elevbottom=100&elevtop=1000" -``` - -### Update camera hydro status: -```bash -curl -X POST "http://your-server/main.php" \ - -d "camid=123&field=hydro&value=true" -``` - -### Query storm data with polygon: -```bash -curl -X POST "http://your-server/main.php" \ - -H "Content-Type: application/json" \ - -d '{ - "request_type": "power", - "start_time": "2025-01-01 00:00:00", - "end_time": "2025-01-02 00:00:00", - "area_geojson": { - "type": "Polygon", - "coordinates": [[[-82, 38], [-81, 38], [-81, 39], [-82, 39], [-82, 38]]] - }, - "buffer": 2 - }' -``` - -## Notes - -1. The `one.php` file contains HTML/JavaScript interface and is not integrated into this API endpoint. -2. All database connections use the same credentials and should be pooled in production. -3. Geographic data uses SRID 4326 (WGS84) unless otherwise specified. -4. Time-based queries typically use UTC unless converted to local time zones. -5. The integrated file maintains backward compatibility with existing callers by preserving all original functionality. \ No newline at end of file diff --git a/main.php b/main.php deleted file mode 100644 index 1f5385d..0000000 --- a/main.php +++ /dev/null @@ -1,1571 +0,0 @@ - $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; -} - -?> \ No newline at end of file