Add NOAA NWS weather fallback
This commit is contained in:
@@ -40,6 +40,7 @@ bool UAgrarianWeatherProviderSubsystem::RequestWeatherForTile(FName TileId, floa
|
||||
Request->SetURL(Url);
|
||||
Request->SetVerb(TEXT("GET"));
|
||||
Request->SetHeader(TEXT("Accept"), TEXT("application/json"));
|
||||
Request->SetHeader(TEXT("User-Agent"), TEXT("AgrarianGameMVP/0.1"));
|
||||
Request->OnProcessRequestComplete().BindUObject(this, &UAgrarianWeatherProviderSubsystem::OnOpenMeteoResponse, TileId, Latitude, Longitude);
|
||||
return Request->ProcessRequest();
|
||||
}
|
||||
@@ -55,6 +56,42 @@ FString UAgrarianWeatherProviderSubsystem::BuildOpenMeteoForecastUrl(FName TileI
|
||||
ClampedLongitude);
|
||||
}
|
||||
|
||||
bool UAgrarianWeatherProviderSubsystem::RequestNoaaNwsFallbackForTile(FName TileId, float Latitude, float Longitude)
|
||||
{
|
||||
if (!bEnableNoaaNwsFallbackForUSTiles || TileId == NAME_None || !IsNoaaNwsEligibleCoordinate(Latitude, Longitude))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
if (!World || !World->GetAuthGameMode())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = FHttpModule::Get().CreateRequest();
|
||||
Request->SetURL(BuildNoaaNwsPointsUrl(Latitude, Longitude));
|
||||
Request->SetVerb(TEXT("GET"));
|
||||
Request->SetHeader(TEXT("Accept"), TEXT("application/geo+json"));
|
||||
Request->SetHeader(TEXT("User-Agent"), NoaaNwsUserAgent);
|
||||
Request->OnProcessRequestComplete().BindUObject(this, &UAgrarianWeatherProviderSubsystem::OnNoaaNwsPointsResponse, TileId, Latitude, Longitude);
|
||||
return Request->ProcessRequest();
|
||||
}
|
||||
|
||||
bool UAgrarianWeatherProviderSubsystem::IsNoaaNwsEligibleCoordinate(float Latitude, float Longitude) const
|
||||
{
|
||||
return Latitude >= 18.0f && Latitude <= 72.0f && Longitude >= -180.0f && Longitude <= -64.0f;
|
||||
}
|
||||
|
||||
FString UAgrarianWeatherProviderSubsystem::BuildNoaaNwsPointsUrl(float Latitude, float Longitude) const
|
||||
{
|
||||
return FString::Printf(
|
||||
TEXT("%s/%.4f,%.4f"),
|
||||
*NoaaNwsPointsEndpoint,
|
||||
FMath::Clamp(Latitude, -90.0f, 90.0f),
|
||||
FMath::Clamp(Longitude, -180.0f, 180.0f));
|
||||
}
|
||||
|
||||
bool UAgrarianWeatherProviderSubsystem::ApplySnapshotToGameState(const FAgrarianWeatherProviderSnapshot& Snapshot, AAgrarianGameState* GameState) const
|
||||
{
|
||||
if (!Snapshot.bIsValid || !GameState || !GameState->HasAuthority())
|
||||
@@ -112,6 +149,63 @@ void UAgrarianWeatherProviderSubsystem::OnOpenMeteoResponse(FHttpRequestPtr Requ
|
||||
ApplySnapshotToGameState(Snapshot, GameState);
|
||||
}
|
||||
|
||||
void UAgrarianWeatherProviderSubsystem::OnNoaaNwsPointsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude)
|
||||
{
|
||||
if (!bWasSuccessful || !Response.IsValid() || Response->GetResponseCode() < 200 || Response->GetResponseCode() >= 300)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TSharedPtr<FJsonObject> RootObject;
|
||||
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString());
|
||||
if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const TSharedPtr<FJsonObject>* PropertiesObject = nullptr;
|
||||
if (!RootObject->TryGetObjectField(TEXT("properties"), PropertiesObject) || !PropertiesObject || !PropertiesObject->IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FString ForecastGridDataUrl;
|
||||
if (!(*PropertiesObject)->TryGetStringField(TEXT("forecastGridData"), ForecastGridDataUrl) || ForecastGridDataUrl.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LastNoaaNwsForecastGridDataUrl = ForecastGridDataUrl;
|
||||
|
||||
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> GridRequest = FHttpModule::Get().CreateRequest();
|
||||
GridRequest->SetURL(ForecastGridDataUrl);
|
||||
GridRequest->SetVerb(TEXT("GET"));
|
||||
GridRequest->SetHeader(TEXT("Accept"), TEXT("application/geo+json"));
|
||||
GridRequest->SetHeader(TEXT("User-Agent"), NoaaNwsUserAgent);
|
||||
GridRequest->OnProcessRequestComplete().BindUObject(this, &UAgrarianWeatherProviderSubsystem::OnNoaaNwsGridDataResponse, TileId, Latitude, Longitude);
|
||||
GridRequest->ProcessRequest();
|
||||
}
|
||||
|
||||
void UAgrarianWeatherProviderSubsystem::OnNoaaNwsGridDataResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude)
|
||||
{
|
||||
if (!bWasSuccessful || !Response.IsValid() || Response->GetResponseCode() < 200 || Response->GetResponseCode() >= 300)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FAgrarianWeatherProviderSnapshot Snapshot;
|
||||
if (!ParseNoaaNwsGridData(Response->GetContentAsString(), TileId, Latitude, Longitude, Snapshot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LastSnapshot = Snapshot;
|
||||
|
||||
UWorld* World = GetWorld();
|
||||
AAgrarianGameState* GameState = World ? World->GetGameState<AAgrarianGameState>() : nullptr;
|
||||
ApplySnapshotToGameState(Snapshot, GameState);
|
||||
}
|
||||
|
||||
bool UAgrarianWeatherProviderSubsystem::ParseOpenMeteoForecast(const FString& ResponseContent, FName TileId, float Latitude, float Longitude, FAgrarianWeatherProviderSnapshot& OutSnapshot) const
|
||||
{
|
||||
TSharedPtr<FJsonObject> RootObject;
|
||||
@@ -181,3 +275,63 @@ bool UAgrarianWeatherProviderSubsystem::ParseOpenMeteoForecast(const FString& Re
|
||||
OutSnapshot.bIsValid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UAgrarianWeatherProviderSubsystem::ParseNoaaNwsGridData(const FString& ResponseContent, FName TileId, float Latitude, float Longitude, FAgrarianWeatherProviderSnapshot& OutSnapshot) const
|
||||
{
|
||||
TSharedPtr<FJsonObject> RootObject;
|
||||
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseContent);
|
||||
if (!FJsonSerializer::Deserialize(Reader, RootObject) || !RootObject.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const TSharedPtr<FJsonObject>* PropertiesObject = nullptr;
|
||||
if (!RootObject->TryGetObjectField(TEXT("properties"), PropertiesObject) || !PropertiesObject || !PropertiesObject->IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ReadFirstGridValue = [](const TSharedPtr<FJsonObject>& Properties, const TCHAR* FieldName, double& OutValue) -> bool
|
||||
{
|
||||
const TSharedPtr<FJsonObject>* FieldObject = nullptr;
|
||||
if (!Properties->TryGetObjectField(FieldName, FieldObject) || !FieldObject || !FieldObject->IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const TArray<TSharedPtr<FJsonValue>>* Values = nullptr;
|
||||
if (!(*FieldObject)->TryGetArrayField(TEXT("values"), Values) || !Values || Values->Num() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const TSharedPtr<FJsonObject> FirstValue = (*Values)[0]->AsObject();
|
||||
return FirstValue.IsValid() && FirstValue->TryGetNumberField(TEXT("value"), OutValue);
|
||||
};
|
||||
|
||||
double TemperatureC = 0.0;
|
||||
if (!ReadFirstGridValue(*PropertiesObject, TEXT("temperature"), TemperatureC))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double PrecipitationPercent = 0.0;
|
||||
double WindSpeedKmh = 0.0;
|
||||
ReadFirstGridValue(*PropertiesObject, TEXT("probabilityOfPrecipitation"), PrecipitationPercent);
|
||||
ReadFirstGridValue(*PropertiesObject, TEXT("windSpeed"), WindSpeedKmh);
|
||||
|
||||
OutSnapshot.TileId = TileId;
|
||||
OutSnapshot.Latitude = FMath::Clamp(Latitude, -90.0f, 90.0f);
|
||||
OutSnapshot.Longitude = FMath::Clamp(Longitude, -180.0f, 180.0f);
|
||||
OutSnapshot.Provider = TEXT("noaa-nws");
|
||||
OutSnapshot.ProviderTimestamp = FDateTime::UtcNow().ToIso8601();
|
||||
OutSnapshot.CurrentTemperatureC = static_cast<float>(TemperatureC);
|
||||
OutSnapshot.DailyLowTemperatureC = static_cast<float>(TemperatureC - 4.0);
|
||||
OutSnapshot.DailyHighTemperatureC = static_cast<float>(TemperatureC + 4.0);
|
||||
OutSnapshot.PrecipitationMm = PrecipitationPercent > 30.0 ? 0.1f : 0.0f;
|
||||
OutSnapshot.WindSpeedKmh = static_cast<float>(WindSpeedKmh);
|
||||
OutSnapshot.WeatherCode = PrecipitationPercent > 30.0 ? 61 : 0;
|
||||
OutSnapshot.MappedWeather = MapOpenMeteoWeatherCode(OutSnapshot.WeatherCode, OutSnapshot.PrecipitationMm, OutSnapshot.WindSpeedKmh);
|
||||
OutSnapshot.bIsValid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user