179 lines
7.0 KiB
PHP
179 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\WeatherReport;
|
|
use Illuminate\Support\Facades\File;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class WeatherController extends Controller
|
|
{
|
|
public function index(): Response
|
|
{
|
|
$latestHourly = WeatherReport::query()
|
|
->where('type', 'hourly')
|
|
->with('periods')
|
|
->orderByDesc('reported_at')
|
|
->first();
|
|
|
|
$latestWeekly = WeatherReport::query()
|
|
->where('type', 'weekly')
|
|
->with('periods')
|
|
->orderByDesc('reported_at')
|
|
->first();
|
|
|
|
$currentPeriod = $latestHourly?->periods->first();
|
|
|
|
$hourlyForecast = $latestHourly?->periods
|
|
->take(6)
|
|
->map(fn ($period) => [
|
|
'time' => $period->start_time->format('H:i'),
|
|
'hour' => $period->start_time->format('H'),
|
|
'temperature' => $period->temperature,
|
|
'temperatureUnit' => $period->temperature_unit,
|
|
'icon' => $this->mapIconToEmoji($period->short_forecast),
|
|
'shortForecast' => $period->short_forecast,
|
|
'precipitationProbability' => $period->precipitation_probability,
|
|
]);
|
|
|
|
$weeklyForecast = $latestWeekly?->periods
|
|
->filter(fn ($period) => $period->is_daytime)
|
|
->take(7)
|
|
->values()
|
|
->map(fn ($period) => [
|
|
'name' => $period->name,
|
|
'temperature' => $period->temperature,
|
|
'temperatureUnit' => $period->temperature_unit,
|
|
'icon' => $this->mapIconToEmoji($period->short_forecast),
|
|
'shortForecast' => $period->short_forecast,
|
|
'precipitationProbability' => $period->precipitation_probability,
|
|
'windSpeed' => $period->wind_speed,
|
|
'windDirection' => $period->wind_direction,
|
|
'detailedForecast' => $period->detailed_forecast,
|
|
]);
|
|
|
|
$background = $this->getBackgroundForForecast(
|
|
$currentPeriod?->short_forecast,
|
|
$currentPeriod?->is_daytime ?? true
|
|
);
|
|
|
|
return Inertia::render('Weather', [
|
|
'current' => $currentPeriod ? [
|
|
'temperature' => $currentPeriod->temperature,
|
|
'temperatureUnit' => $currentPeriod->temperature_unit,
|
|
'shortForecast' => $currentPeriod->short_forecast,
|
|
'detailedForecast' => $currentPeriod->detailed_forecast,
|
|
'windSpeed' => $currentPeriod->wind_speed,
|
|
'windDirection' => $currentPeriod->wind_direction,
|
|
'humidity' => $currentPeriod->relative_humidity,
|
|
'dewpoint' => $currentPeriod->dewpoint_celsius,
|
|
'precipitationProbability' => $currentPeriod->precipitation_probability,
|
|
'isDaytime' => $currentPeriod->is_daytime,
|
|
'icon' => $this->mapIconToEmoji($currentPeriod->short_forecast),
|
|
] : null,
|
|
'hourlyForecast' => $hourlyForecast ?? collect(),
|
|
'weeklyForecast' => $weeklyForecast ?? collect(),
|
|
'location' => [
|
|
'latitude' => $latestHourly?->latitude,
|
|
'longitude' => $latestHourly?->longitude,
|
|
'name' => 'Utah County',
|
|
],
|
|
'reportedAt' => $latestHourly?->reported_at?->format('M d, Y H:i'),
|
|
'background' => $background,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @return array{imageUrl: string|null, licenseHtml: string|null}
|
|
*/
|
|
private function getBackgroundForForecast(?string $forecast, bool $isDaytime): array
|
|
{
|
|
$folder = $this->mapForecastToFolder($forecast);
|
|
$timeOfDay = $isDaytime ? 'day' : 'night';
|
|
$basePath = storage_path('app/public/backgrounds/'.$folder);
|
|
|
|
if (! File::isDirectory($basePath)) {
|
|
return ['imageUrl' => null, 'licenseHtml' => null];
|
|
}
|
|
|
|
$files = File::files($basePath);
|
|
$pattern = '/^'.$timeOfDay.'_[a-zA-Z0-9_-]+\.jpg$/';
|
|
|
|
$imageFiles = collect($files)
|
|
->filter(fn ($file) => preg_match($pattern, $file->getFilename()))
|
|
->values();
|
|
|
|
if ($imageFiles->isEmpty()) {
|
|
$fallbackPattern = '/^(day|night)_[a-zA-Z0-9_-]+\.jpg$/';
|
|
$imageFiles = collect($files)
|
|
->filter(fn ($file) => preg_match($fallbackPattern, $file->getFilename()))
|
|
->values();
|
|
}
|
|
|
|
if ($imageFiles->isEmpty()) {
|
|
return ['imageUrl' => null, 'licenseHtml' => null];
|
|
}
|
|
|
|
$selectedImage = $imageFiles->random();
|
|
$imageFilename = $selectedImage->getFilename();
|
|
$imageUrl = '/storage/backgrounds/'.$folder.'/'.$imageFilename;
|
|
|
|
$licenseFilename = preg_replace('/\.jpg$/', '_license.html', $imageFilename);
|
|
$licensePath = $basePath.'/'.$licenseFilename;
|
|
$licenseHtml = null;
|
|
|
|
if (File::exists($licensePath)) {
|
|
$licenseHtml = trim(File::get($licensePath));
|
|
}
|
|
|
|
return [
|
|
'imageUrl' => $imageUrl,
|
|
'licenseHtml' => $licenseHtml,
|
|
];
|
|
}
|
|
|
|
private function mapForecastToFolder(?string $forecast): string
|
|
{
|
|
if (! $forecast) {
|
|
return 'cloudy';
|
|
}
|
|
|
|
$forecast = strtolower($forecast);
|
|
|
|
return match (true) {
|
|
str_contains($forecast, 'thunder') || str_contains($forecast, 'storm') => 'stormy',
|
|
str_contains($forecast, 'snow') => 'snowy',
|
|
str_contains($forecast, 'rain') || str_contains($forecast, 'shower') || str_contains($forecast, 'drizzle') => 'rainy',
|
|
str_contains($forecast, 'wind') => 'windy',
|
|
str_contains($forecast, 'sunny') || str_contains($forecast, 'clear') => 'clear',
|
|
str_contains($forecast, 'cloud') || str_contains($forecast, 'overcast') => 'cloudy',
|
|
str_contains($forecast, 'fog') || str_contains($forecast, 'mist') => 'cloudy',
|
|
default => 'cloudy',
|
|
};
|
|
}
|
|
|
|
private function mapIconToEmoji(?string $forecast): string
|
|
{
|
|
if (! $forecast) {
|
|
return 'cloud';
|
|
}
|
|
|
|
$forecast = strtolower($forecast);
|
|
|
|
return match (true) {
|
|
str_contains($forecast, 'sunny') || str_contains($forecast, 'clear') => 'sun',
|
|
str_contains($forecast, 'partly') && str_contains($forecast, 'cloud') => 'cloud-sun',
|
|
str_contains($forecast, 'mostly cloudy') => 'cloud',
|
|
str_contains($forecast, 'cloud') => 'cloud',
|
|
str_contains($forecast, 'rain') && str_contains($forecast, 'snow') => 'cloud-snow',
|
|
str_contains($forecast, 'snow') => 'cloud-snow',
|
|
str_contains($forecast, 'rain') || str_contains($forecast, 'shower') => 'cloud-rain',
|
|
str_contains($forecast, 'thunder') || str_contains($forecast, 'storm') => 'cloud-lightning',
|
|
str_contains($forecast, 'fog') || str_contains($forecast, 'mist') => 'cloud-fog',
|
|
str_contains($forecast, 'wind') => 'wind',
|
|
default => 'cloud',
|
|
};
|
|
}
|
|
}
|