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); ?>