34935-vm/api/wind.php
Flatlogic Bot f7aa8776f6 wind plot
2025-10-14 03:08:30 +00:00

203 lines
5.6 KiB
PHP

<?php
// Enable error reporting for debugging
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once __DIR__ . '/../config.php';
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
$cacheFile = __DIR__ . '/../assets/cache/wind.json';
$cacheTime = 3600; // 1 hour in seconds
// Check if a valid cache file exists
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTime) {
readfile($cacheFile);
exit;
}
// --- Helper Functions ---
/**
* Calculates the great-circle distance between two points on a sphere.
* @param float $lat1 Latitude of point 1
* @param float $lon1 Longitude of point 1
* @param float $lat2 Latitude of point 2
* @param float $lon2 Longitude of point 2
* @return float Distance in kilometers
*/
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;
}
/**
* Interpolates wind data for a grid point using Inverse Distance Weighting.
* @param float $gridLat Latitude of the grid point
* @param float $gridLon Longitude of the grid point
* @param array $cities Array of city weather data
* @param int $numNearest Number of nearest cities to use
* @param float $power Power parameter for IDW
* @return array ['u' => float, 'v' => float]
*/
function interpolatePoint($gridLat, $gridLon, &$cities, $numNearest = 4, $power = 2) {
// Find nearest cities
usort($cities, function($a, $b) use ($gridLat, $gridLon) {
$distA = haversineDistance($gridLat, $gridLon, $a['coord']['Lat'], $a['coord']['Lon']);
$distB = haversineDistance($gridLat, $gridLon, $b['coord']['Lat'], $b['coord']['Lon']);
return $distA - $distB;
});
$nearestCities = array_slice($cities, 0, $numNearest);
$totalWeight = 0;
$weightedU = 0;
$weightedV = 0;
foreach ($nearestCities as $city) {
$dist = haversineDistance($gridLat, $gridLon, $city['coord']['Lat'], $city['coord']['Lon']);
if ($dist == 0) { // If the grid point is exactly at a city, use its data directly
$windSpeed = $city['wind']['speed']; // m/s
$windDeg = $city['wind']['deg'];
$windRad = deg2rad($windDeg);
return [
'u' => -$windSpeed * sin($windRad),
'v' => -$windSpeed * cos($windRad)
];
}
$weight = 1 / pow($dist, $power);
$windSpeed = $city['wind']['speed']; // m/s
$windDeg = $city['wind']['deg'];
$windRad = deg2rad($windDeg);
// Convert to U/V components (meteorological convention)
$u = -$windSpeed * sin($windRad);
$v = -$windSpeed * cos($windRad);
$weightedU += $u * $weight;
$weightedV += $v * $weight;
$totalWeight += $weight;
}
if ($totalWeight == 0) return ['u' => 0, 'v' => 0];
return [
'u' => $weightedU / $totalWeight,
'v' => $weightedV / $totalWeight
];
}
// --- Data Fetching and Processing ---
$bulkFileName = 'weather_14.json.gz';
$bulkUrl = "https://bulk.openweathermap.org/snapshot/{$bulkFileName}?appid=" . OWM_API_KEY;
$gzData = @file_get_contents($bulkUrl);
if ($gzData === false) {
http_response_code(500);
echo json_encode(['error' => 'Failed to download bulk weather data. Check API key and URL.']);
exit;
}
$jsonData = gzdecode($gzData);
if ($jsonData === false) {
http_response_code(500);
echo json_encode(['error' => 'Failed to decompress weather data.']);
exit;
}
// The file contains JSON objects separated by newlines
$lines = explode("\n", trim($jsonData));
$cities = [];
foreach ($lines as $line) {
if (!empty($line)) {
$cities[] = json_decode($line, true);
}
}
if (empty($cities)) {
http_response_code(500);
echo json_encode(['error' => 'Failed to parse weather data JSON or file is empty.']);
exit;
}
// --- Interpolation ---
$nx = 72; // Grid points in longitude (every 5 degrees)
$ny = 37; // Grid points in latitude (every 5 degrees)
$lo1 = -180;
$la1 = 90;
$dx = 5;
$dy = 5;
$uData = [];
$vData = [];
for ($j = 0; $j < $ny; $j++) {
$lat = $la1 - $j * $dy;
for ($i = 0; $i < $nx; $i++) {
$lon = $lo1 + $i * $dx;
$wind = interpolatePoint($lat, $lon, $cities);
$uData[] = $wind['u'];
$vData[] = $wind['v'];
}
}
// --- Format Data ---
$refTime = gmdate("Y-m-d\\TH:i:s.v\\Z");
$formattedData = [
[
"header" => [
"nx" => $nx,
"ny" => $ny,
"lo1" => $lo1,
"la1" => $la1,
"dx" => $dx,
"dy" => $dy,
"parameterCategory" => 2,
"parameterNumber" => 2,
"forecastTime" => 0,
"refTime" => $refTime,
],
"data" => $uData
],
[
"header" => [
"nx" => $nx,
"ny" => $ny,
"lo1" => $lo1,
"la1" => $la1,
"dx" => $dx,
"dy" => $dy,
"parameterCategory" => 2,
"parameterNumber" => 3,
"forecastTime" => 0,
"refTime" => $refTime,
],
"data" => $vData
]
];
// --- Caching and Output ---
$cacheDir = dirname($cacheFile);
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
file_put_contents($cacheFile, json_encode($formattedData));
echo json_encode($formattedData);
?>