summaryrefslogtreecommitdiffstats
path: root/parser.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--parser.go129
1 files changed, 129 insertions, 0 deletions
diff --git a/parser.go b/parser.go
new file mode 100644
index 0000000..6cb67e8
--- /dev/null
+++ b/parser.go
@@ -0,0 +1,129 @@
+// parser.go - XML parsing and data extraction
+package main
+
+import (
+ "encoding/xml"
+ "fmt"
+ "math"
+ "strconv"
+ "strings"
+)
+
+// ParseForecastXML parses XML response into ForecastData
+func ParseForecastXML(xmlData []byte) (*ForecastData, error) {
+ var fc FeatureCollection
+ if err := xml.Unmarshal(xmlData, &fc); err != nil {
+ return nil, fmt.Errorf("unmarshaling XML: %w", err)
+ }
+
+ if len(fc.Members) == 0 {
+ return nil, fmt.Errorf("no data found in response")
+ }
+
+ mpc := fc.Members[0].GridSeriesObservation.Result.MultiPointCoverage
+
+ // Parse positions
+ lat, lon, times, err := parsePositions(mpc.DomainSet.SimpleMultiPoint.Positions)
+ if err != nil {
+ return nil, fmt.Errorf("parsing positions: %w", err)
+ }
+
+ // Parse parameters
+ params := make([]string, 0, len(mpc.RangeType.DataRecord.Fields))
+ paramIndex := make(map[string]int)
+ for i, f := range mpc.RangeType.DataRecord.Fields {
+ params = append(params, f.Name)
+ paramIndex[f.Name] = i
+ }
+
+ // Parse values
+ values, err := parseTupleList(mpc.RangeSet.DataBlock.TupleList, len(times), len(params))
+ if err != nil {
+ return nil, fmt.Errorf("parsing values: %w", err)
+ }
+
+ return &ForecastData{
+ Latitude: lat,
+ Longitude: lon,
+ Timezone: "Europe/Helsinki", // Default, could be parsed from XML
+ Timestamps: times,
+ Parameters: params,
+ Values: values,
+ ParamIndex: paramIndex,
+ }, nil
+}
+
+func parsePositions(positionsStr string) (lat, lon float64, times []int64, err error) {
+ lines := strings.Split(strings.TrimSpace(positionsStr), "\n")
+
+ for i, line := range lines {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ continue
+ }
+
+ parts := strings.Fields(line)
+ if len(parts) != 3 {
+ return 0, 0, nil, fmt.Errorf("invalid position format at line %d: %s", i, line)
+ }
+
+ parsedLat, err := strconv.ParseFloat(parts[0], 64)
+ if err != nil {
+ return 0, 0, nil, fmt.Errorf("parsing latitude: %w", err)
+ }
+
+ parsedLon, err := strconv.ParseFloat(parts[1], 64)
+ if err != nil {
+ return 0, 0, nil, fmt.Errorf("parsing longitude: %w", err)
+ }
+
+ timestamp, err := strconv.ParseInt(parts[2], 10, 64)
+ if err != nil {
+ return 0, 0, nil, fmt.Errorf("parsing timestamp: %w", err)
+ }
+
+ if i == 0 {
+ lat, lon = parsedLat, parsedLon
+ } else if math.Abs(parsedLat-lat) > 0.001 || math.Abs(parsedLon-lon) > 0.001 {
+ return 0, 0, nil, fmt.Errorf("inconsistent location at line %d", i)
+ }
+
+ times = append(times, timestamp)
+ }
+
+ if len(times) == 0 {
+ return 0, 0, nil, fmt.Errorf("no positions found")
+ }
+
+ return lat, lon, times, nil
+}
+
+func parseTupleList(tupleStr string, numPoints, numParams int) ([][]float64, error) {
+ tupleStr = strings.TrimSpace(tupleStr)
+ valuesStr := strings.Fields(tupleStr)
+
+ if len(valuesStr) != numPoints*numParams {
+ return nil, fmt.Errorf("data mismatch: expected %d values, got %d",
+ numPoints*numParams, len(valuesStr))
+ }
+
+ values := make([][]float64, numPoints)
+ for i := 0; i < numPoints; i++ {
+ row := make([]float64, numParams)
+ for j := 0; j < numParams; j++ {
+ valStr := valuesStr[i*numParams+j]
+ if valStr == "NaN" {
+ row[j] = math.NaN()
+ } else {
+ val, err := strconv.ParseFloat(valStr, 64)
+ if err != nil {
+ return nil, fmt.Errorf("parsing value at [%d][%d]: %w", i, j, err)
+ }
+ row[j] = val
+ }
+ }
+ values[i] = row
+ }
+
+ return values, nil
+}