From 757f611a4d738688bc97cc6787ecce8650f25873 Mon Sep 17 00:00:00 2001 From: Petri Hienonen Date: Sun, 1 Feb 2026 17:31:30 +0200 Subject: Second --- converter.go | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 converter.go (limited to 'converter.go') diff --git a/converter.go b/converter.go new file mode 100644 index 0000000..0e2b74c --- /dev/null +++ b/converter.go @@ -0,0 +1,138 @@ +// converter.go - Converts ForecastData to OWMResponse +package main + +import ( + "fmt" + "math" + "time" +) + +// ToOWMResponse converts ForecastData to OWMResponse format +func (fd *ForecastData) ToOWMResponse(mapper *WeatherMapper) (*OWMResponse, error) { + loc, err := time.LoadLocation(fd.Timezone) + if err != nil { + return nil, fmt.Errorf("loading timezone: %w", err) + } + + // Calculate timezone offset + firstTime := time.Unix(fd.Timestamps[0], 0).UTC() + _, offset := firstTime.In(loc).Zone() + + // Create response + response := &OWMResponse{ + Lat: fd.Latitude, + Lon: fd.Longitude, + Timezone: fd.Timezone, + TimezoneOffset: offset, + } + + // Calculate sunrise/sunset for first day + sunCalc := NewSunCalculator(fd.Latitude, fd.Longitude, float64(offset)/3600) + sunrise, sunset, err := sunCalc.Calculate(firstTime) + if err != nil { + // Use defaults if calculation fails + sunrise = firstTime.Add(8 * time.Hour) + sunset = firstTime.Add(16 * time.Hour) + } + + // Convert each forecast point + hourly := make([]Current, 0, len(fd.Timestamps)) + for i, timestamp := range fd.Timestamps { + current, err := fd.convertToCurrent(i, timestamp, loc, sunrise, sunset, mapper) + if err != nil { + return nil, fmt.Errorf("converting point %d: %w", i, err) + } + hourly = append(hourly, current) + } + + response.Current = hourly[0] + response.Current.Sunrise = sunrise.Unix() + response.Current.Sunset = sunset.Unix() + response.Hourly = hourly + + return response, nil +} + +func (fd *ForecastData) convertToCurrent(index int, timestamp int64, loc *time.Location, sunrise, sunset time.Time, mapper *WeatherMapper) (Current, error) { + current := Current{ + Dt: timestamp, + Uvi: 0, // Not available from FMI + } + + // Add ISO 8601 local time + current.LocalTime = time.Unix(timestamp, 0).In(loc).Format(time.RFC3339) + + // Extract temperature + if val, err := fd.getFloatValue(index, "Temperature"); err == nil && !math.IsNaN(val) { + current.Temp = val + } + + // Extract humidity + if val, err := fd.getFloatValue(index, "Humidity"); err == nil && !math.IsNaN(val) { + current.Humidity = int(math.Round(val)) + } + + // Extract pressure + if val, err := fd.getFloatValue(index, "Pressure"); err == nil && !math.IsNaN(val) { + current.Pressure = int(math.Round(val)) + } + + // Extract dew point + if val, err := fd.getFloatValue(index, "dewPoint"); err == nil && !math.IsNaN(val) { + current.DewPoint = val + } + + // Extract wind speed + if val, err := fd.getFloatValue(index, "WindSpeedMS"); err == nil && !math.IsNaN(val) { + current.WindSpeed = val + } + + // Extract wind direction + if val, err := fd.getFloatValue(index, "WindDirection"); err == nil && !math.IsNaN(val) { + current.WindDeg = int(math.Round(val)) + } + + // Extract wind gust + if val, err := fd.getFloatValue(index, "WindGust"); err == nil && !math.IsNaN(val) { + current.WindGust = val + } + + // Extract cloud cover + if val, err := fd.getFloatValue(index, "TotalCloudCover"); err == nil && !math.IsNaN(val) { + current.Clouds = int(math.Round(val)) + } + + // Extract visibility + if val, err := fd.getFloatValue(index, "visibility"); err == nil && !math.IsNaN(val) { + current.Visibility = int(math.Round(val)) + } + + // Calculate feels-like temperature + current.FeelsLike = CalculateFeelsLike(current.Temp, float64(current.Humidity), current.WindSpeed) + + // Handle precipitation + if val, err := fd.getFloatValue(index, "PrecipitationRate"); err == nil && !math.IsNaN(val) && val > 0 { + current.Rain = &Rain{OneH: val} + } + + // Map weather symbol + if val, err := fd.getFloatValue(index, "WeatherSymbol3"); err == nil && !math.IsNaN(val) { + symbol := int(math.Round(val)) + forecastTime := time.Unix(timestamp, 0) + current.Weather = []Weather{mapper.Map(symbol, forecastTime, sunrise, sunset)} + } else { + // Default weather + current.Weather = []Weather{{800, "Clear", "clear sky", "01d"}} + } + + return current, nil +} + +func (fd *ForecastData) getFloatValue(rowIndex int, paramName string) (float64, error) { + if paramIndex, ok := fd.ParamIndex[paramName]; ok { + if rowIndex < len(fd.Values) && paramIndex < len(fd.Values[rowIndex]) { + return fd.Values[rowIndex][paramIndex], nil + } + } + return math.NaN(), fmt.Errorf("parameter %s not found or out of bounds", paramName) +} -- cgit v1.3-1-g0d28