34935-vm/api/facilities.php
2025-10-17 01:31:35 +00:00

164 lines
6.8 KiB
PHP

<?php
require_once __DIR__ . '/../db/config.php';
header('Content-Type: application/json');
ini_set('display_errors', 0);
// --- Utility Functions ---
function pointInPolygon($point, $polygon) {
$inside = false;
$x = $point[0];
$y = $point[1];
for ($i = 0, $j = count($polygon) - 1; $i < count($polygon); $j = $i++) {
$xi = $polygon[$i][0]; $yi = $polygon[$i][1];
$xj = $polygon[$j][0]; $yj = $polygon[$j][1];
$intersect = (($yi > $y) != ($yj > $y)) && ($x < ($xj - $xi) * ($y - $yi) / ($yj - $yi) + $xi);
if ($intersect) $inside = !$inside;
}
return $inside;
}
function haversineDistance($lat1, $lon1, $lat2, $lon2) {
$earthRadius = 6371; // km
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat / 2) * sin($dLat / 2) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon / 2) * sin($dLon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return $earthRadius * $c;
}
// --- Risk Calculation Functions ---
function getWildfireRisk($lat, $lon) {
try {
$wildfireUrl = 'https://services3.arcgis.com/T4QMspbfLg3qTGWY/arcgis/rest/services/WFIGS_Interagency_Fire_Perimeters_Current/FeatureServer/0/query?where=1%3D1&outFields=poly_IncidentName,poly_GISAcres,attr_InitialLatitude,attr_InitialLongitude&outSR=4326&f=json';
$context = stream_context_create(['http' => ['timeout' => 10]]);
$wildfireDataJson = @file_get_contents($wildfireUrl, false, $context);
if (!$wildfireDataJson) throw new Exception('Could not fetch wildfire data from source.');
$wildfireData = json_decode($wildfireDataJson, true);
if (!$wildfireData || !isset($wildfireData['features'])) return ['score' => 0, 'details' => 'No active wildfires found or data invalid.'];
$risk = 0;
$closest_distance = PHP_INT_MAX;
$closest_fire = null;
foreach ($wildfireData['features'] as $fire) {
$fireLat = $fire['attributes']['attr_InitialLatitude'];
$fireLon = $fire['attributes']['attr_InitialLongitude'];
if ($fireLat && $fireLon) {
$distance = haversineDistance($lat, $lon, $fireLat, $fireLon);
if ($distance < $closest_distance) {
$closest_distance = $distance;
$closest_fire = $fire['attributes']['poly_IncidentName'];
}
}
}
if ($closest_distance < 50) { // 50km threshold
$risk = 100 - ($closest_distance / 50) * 100;
}
return ['score' => round($risk), 'details' => $closest_fire ? "Closest fire: {$closest_fire} (" . round($closest_distance) . " km away)" : "No fires within 50km."];
} catch (Exception $e) {
error_log("Wildfire Risk Error: " . $e->getMessage());
return ['score' => 0, 'details' => 'Error calculating wildfire risk.'];
}
}
function getHurricaneRisk($lat, $lon) {
try {
$localApiUrl = 'http://localhost/api/hurricanes.php';
$context = stream_context_create(['http' => ['timeout' => 15]]);
$geoJsonData = @file_get_contents($localApiUrl, false, $context);
if (!$geoJsonData) throw new Exception('Could not fetch local hurricane data.');
$hurricaneData = json_decode($geoJsonData, true);
if (!$hurricaneData || empty($hurricaneData['features'])) return ['score' => 0, 'details' => 'No active hurricane data found.'];
$risk = 0;
$details = [];
$point = [(float)$lon, (float)$lat];
foreach ($hurricaneData['features'] as $feature) {
if (isset($feature['properties']['layer']) && $feature['properties']['layer'] === 'cone' && $feature['geometry']['type'] === 'Polygon') {
$polygon = $feature['geometry']['coordinates'][0];
if (pointInPolygon($point, $polygon)) {
$risk = 100;
$stormName = $feature['properties']['STORMNAME'] ?? 'Unnamed Storm';
$details[] = "Inside the forecast cone for " . $stormName;
break;
}
}
}
return ['score' => $risk, 'details' => !empty($details) ? implode(', ', $details) : 'Not in any active hurricane forecast cones.'];
} catch (Exception $e) {
error_log("Hurricane Risk Error: " . $e->getMessage());
return ['score' => 0, 'details' => 'Error calculating hurricane risk.'];
}
}
function getHistoricalHurricaneRisk($lat, $lon) {
try {
$pdo = db();
if (!$pdo) return ['score' => 0, 'details' => 'Database connection failed.'];
$radius = 1.0;
$stmt = $pdo->prepare("SELECT COUNT(DISTINCT storm_id) as storm_count FROM hurricanes WHERE ABS(lat - :lat) < :radius AND ABS(lon - :lon) < :radius");
$stmt->execute(['lat' => $lat, 'lon' => $lon, 'radius' => $radius]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$count = $result['storm_count'] ?? 0;
$score = min($count * 10, 100);
return ['score' => $score, 'details' => "{$count} historical storms passed within ~111km."];
} catch (Exception $e) {
error_log("Historical Hurricane Risk Error: " . $e->getMessage());
return ['score' => 0, 'details' => 'Error calculating historical hurricane risk.'];
}
}
function calculateRiskScore($lat, $lon) {
$wildfireRisk = getWildfireRisk((float)$lat, (float)$lon);
$hurricaneRisk = getHurricaneRisk((float)$lat, (float)$lon);
$historicalRisk = getHistoricalHurricaneRisk((float)$lat, (float)$lon);
$totalScore = ($wildfireRisk['score'] * 0.4) + ($hurricaneRisk['score'] * 0.4) + ($historicalRisk['score'] * 0.2);
return round($totalScore);
}
try {
$pdo = db();
$method = $_SERVER['REQUEST_METHOD'];
if ($method === 'GET') {
$stmt = $pdo->query('SELECT id, name, latitude, longitude, city, state FROM facilities ORDER BY created_at DESC');
$allFacilities = $stmt->fetchAll(PDO::FETCH_ASSOC);
$sampleSize = ceil(count($allFacilities) * 0.1);
if ($sampleSize < 1 && count($allFacilities) > 0) $sampleSize = 1;
shuffle($allFacilities);
$facilitiesSample = array_slice($allFacilities, 0, $sampleSize);
$facilitiesWithRisk = [];
foreach ($facilitiesSample as $facility) {
$facility['risk'] = calculateRiskScore($facility['latitude'], $facility['longitude']);
$facilitiesWithRisk[] = $facility;
}
echo json_encode(['success' => true, 'facilities' => $facilitiesWithRisk]);
} else {
http_response_code(405);
echo json_encode(['success' => false, 'error' => 'Method Not Allowed']);
}
} catch (Exception $e) {
http_response_code(500);
$error = [
'success' => false,
'error' => 'An unexpected error occurred in facilities API.',
'message' => $e->getMessage(),
];
error_log(print_r($error, true));
echo json_encode($error);
}
?>