diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..35e16b6 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,20 @@ +name: Go + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout main + uses: actions/checkout@v2 + + - name: Run unit tests + run: | + make test-ci + env: + CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c517cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test +*.html + + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +vendor/ + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b4dc37a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Jakub Jarosz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6e2725c --- /dev/null +++ b/Makefile @@ -0,0 +1,82 @@ +.PHONY: help check cover test tidy + +ROOT := $(PWD) +GO_HTML_COV := ./coverage.html +GO_TEST_OUTFILE := ./c.out +GO_DOCKER_IMAGE := golang:1.17 +GO_DOCKER_CONTAINER := meteo-container +CC_TEST_REPORTER_ID := ${CC_TEST_REPORTER_ID} +CC_PREFIX := github.com/qba73/meteo + + +define PRINT_HELP_PYSCRIPT +import re, sys + +for line in sys.stdin: + match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) + if match: + target, help = match.groups() + print("%-20s %s" % (target, help)) +endef +export PRINT_HELP_PYSCRIPT + +default: help + +help: + @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) + +check: ## Run static check analyzer + staticcheck ./... + +cover: ## Run unit tests and generate test coverage report + go test -v ./... -count=1 -covermode=count -coverprofile=coverage.out + go tool cover -html coverage.out + staticcheck ./... + +test: ## Run unit tests locally + go test -v ./... -count=1 + staticcheck ./... + +# MODULES +tidy: ## Run go mod tidy and vendor + go mod tidy + go mod vendor + + +clean: ## Remove docker container if exist + docker rm -f ${GO_DOCKER_CONTAINER} || true + +testdocker: ## Run unittests inside a container + docker run -w /app -v ${ROOT}:/app ${GO_DOCKER_IMAGE} go test ./... -coverprofile=${GO_TEST_OUTFILE} + docker run -w /app -v ${ROOT}:/app ${GO_DOCKER_IMAGE} go tool cover -html=${GO_TEST_OUTFILE} -o ${GO_HTML_COV} + +lint: ## Run linter inside container + docker run --rm -v ${ROOT}:/data cytopia/golint . + +# Custom logic for code climate +_before-cc: + # Download CC test reported + docker run -w /app -v ${ROOT}:/app ${GO_DOCKER_IMAGE} \ + /bin/bash -c \ + "curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter" + + # Make reporter executable + docker run -w /app -v ${ROOT}:/app ${GO_DOCKER_IMAGE} chmod +x ./cc-test-reporter + + # Run before build + docker run -w /app -v ${ROOT}:/app \ + -e CC_TEST_REPORTER_ID=${CC_TEST_REPORTER_ID} ${GO_DOCKER_IMAGE} \ + ./cc-test-reporter before-build + +_after-cc: + # Handle custom prefix + $(eval PREFIX=${CC_PREFIX}) +ifdef prefix + $(eval PREFIX=${prefix}) +endif + # Upload data to CC + docker run -w /app -v ${ROOT}:/app \ + -e CC_TEST_REPORTER_ID=${CC_TEST_REPORTER_ID} \ + ${GO_DOCKER_IMAGE} ./cc-test-reporter after-build --prefix ${PREFIX} + +test-ci: _before-cc testdocker _after-cc diff --git a/README.md b/README.md new file mode 100644 index 0000000..fd24f96 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +![Go](https://github.com/qba73/meteo/workflows/Go/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/qba73/meteo)](https://goreportcard.com/report/github.com/qba73/meteo) +[![Maintainability](https://api.codeclimate.com/v1/badges/4afc34a390da95ed9327/maintainability)](https://codeclimate.com/github/qba73/meteo/maintainability) +[![Test Coverage](https://api.codeclimate.com/v1/badges/4afc34a390da95ed9327/test_coverage)](https://codeclimate.com/github/qba73/meteo/test_coverage) + + +# meteo + +Meteo is a Go client library for the weather and meteorological forecast from [Yr](https://www.yr.no/en). + +Disclaimer: + +Weather forecast from Yr, delivered by the Norwegian Meteorological Institute and NRK. + +# Usage + +## Preconditions + +You must register your user agent string in the [YR.NO service](https://developer.yr.no/doc/TermsOfService/) and your user name in the [GeoNames service](https://www.geonames.org/login) to use the package. + + +## Installation +``` +$ go get git@github.com:qba73/meteo.git +``` + +## Default + +Export ```GEO_USERNAME``` env var that you registered with GeoNames.org. + +Example: +``` +$ export GEO_USERNAME=Jane123 +``` +Use the ```meteo``` package in your application: +```go +package main + +import ( + "fmt" + "log" + "github.com/qba73/meteo" +) + +func main() { + // Get weather status for Castlebar in Ireland: + weather, err := meteo.GetWeather("Castlebar", "IE") + if err != nil { + log.Println(err) + } + // Print out weather string. + // Example: Lightrain 8.3°C + fmt.Println(weather) +} +``` + diff --git a/cmd/meteo/main.go b/cmd/meteo/main.go new file mode 100644 index 0000000..4c7000e --- /dev/null +++ b/cmd/meteo/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/qba73/meteo" + +func main() { + meteo.RunCLI() +} diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..46db475 --- /dev/null +++ b/doc.go @@ -0,0 +1,3 @@ +// Package meteo is a client library for the weather +// API data provided by the Norwegian Meteorological Institute. +package meteo diff --git a/examples/basic/main.go b/examples/basic/main.go new file mode 100644 index 0000000..49f813c --- /dev/null +++ b/examples/basic/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Example 1") +} diff --git a/examples/default/main.go b/examples/default/main.go new file mode 100644 index 0000000..42cb5b8 --- /dev/null +++ b/examples/default/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "log" + + "github.com/qba73/meteo" +) + +func main() { + // Get weather status for city Castlebar in Ireland: + weather, err := meteo.GetWeather("Castlebar", "IE") + if err != nil { + log.Println(err) + } + + // Print out weather string. + // Example: Lightrain 8.3°C + fmt.Println(weather) +} diff --git a/examples/geoplaces/main.go b/examples/geoplaces/main.go new file mode 100644 index 0000000..e9a9390 --- /dev/null +++ b/examples/geoplaces/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "os" + + "github.com/qba73/meteo" +) + +func main() { + // Get coordinates using a default Geo client + user := os.Getenv("GEO_USERNAME") + + coord, err := meteo.GetCoordinates("Castlebar", "IE", user) + if err != nil { + println(err) + } + + fmt.Printf("Lat: %.2f, Lng: %.2f for %s in country %s\n", coord.Lat, coord.Lng, coord.PlaceName, coord.CountryCode) + // It returns: + // Lat: 53.85, Lng: -9.30 for Castlebar in country IE +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4d51eac --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module github.com/qba73/meteo + +go 1.17 + +require github.com/google/go-cmp v0.5.6 + +require golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..58b75a4 --- /dev/null +++ b/go.sum @@ -0,0 +1,5 @@ +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/meteo.go b/meteo.go new file mode 100644 index 0000000..7751f99 --- /dev/null +++ b/meteo.go @@ -0,0 +1,54 @@ +package meteo + +import ( + "fmt" + "os" + "strings" +) + +const ( + userAgent = "Meteo/0.1 https://github.com/qba73/meteo" +) + +// Weather represents weather conditions +// in a geographical region. +type Weather struct { + Summary string + Temp float64 +} + +// String implements stringer interface. +func (w Weather) String() string { + return fmt.Sprintf("%s %.1f°C", strings.Title(w.Summary), w.Temp) +} + +// NameResolver interface is used by an Meteo Client +// to obtain geo coordinates for given place located in +// a country identified by country id. +type NameResolver interface { + // GetCoordinates takes place and country code + // and returns geo information like lat and lng. + GetCoordinates(placeName, country string) (Place, error) +} + +// RunCLI is a main function that runs the cli machinery. +func RunCLI() { + uname := os.Getenv("GEO_USERNAME") + c, err := NewYrWeatherClient( + WithWikipediaGeoResolver(), + ) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } + c, err := NewNorwayClient(resolver) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + forecast, err := c.GetForecast("Castlebar", "IE") + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + fmt.Println(w) +} diff --git a/meteo_test.go b/meteo_test.go new file mode 100644 index 0000000..1410fac --- /dev/null +++ b/meteo_test.go @@ -0,0 +1,24 @@ +package meteo_test + +import ( + "bytes" + "fmt" + "testing" + + "github.com/qba73/meteo" +) + +func TestWeatherStringFormat(t *testing.T) { + t.Parallel() + w := meteo.Weather{ + Summary: "sunny", + Temp: -3.12, + } + out := bytes.Buffer{} + fmt.Fprint(&out, w) + got := out.String() + want := "Sunny -3.1°C" + if want != got { + t.Errorf("want %s, got %s", want, got) + } +} diff --git a/norway.go b/norway.go new file mode 100644 index 0000000..c3a17fd --- /dev/null +++ b/norway.go @@ -0,0 +1,177 @@ +package meteo + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "os" + "time" +) + +const ( + baseURL = "https://api.met.no" +) + +type norwayForecast struct { + Type string `json:"type"` + Geometry geometry `json:"geometry"` + Properties properties `json:"properties"` +} + +type geometry struct { + Type string `json:"type"` + Coordinates []float64 `json:"coordinates"` + Properties properties `json:"properties"` +} + +type properties struct { + Meta meta `json:"meta"` + Timeseries timeseries `json:"timeseries"` +} + +type meta struct { + UpdatedAt string `json:"updated_at"` + Units map[string]string `json:"units"` +} + +type timeseries []forecastEntry + +type forecastEntry struct { + Time string `json:"time"` + Data data `json:"data"` +} + +type data struct { + Instant struct { + Details struct { + AirTemperature float64 `json:"air_temperature"` + } + } + Next1Hours struct { + Summary struct { + SymbolCode string `json:"symbol_code"` + } + } `json:"next_1_hours"` +} + +type option func(*NorwayClient) error + +// WithUserAgent is a func option and allows to +// customise User-Agent header used internally +// by the NorwayClient. +func WithUserAgent(ua string) option { + return func(nc *NorwayClient) error { + if ua == "" { + return errors.New("user agent not provided") + } + nc.UA = ua + return nil + } +} + +// NorwayClient represents a weather client +// for the Norwegian Meteorological Institute. +type NorwayClient struct { + UA string + BaseURL string + HTTPClient *http.Client + Resolver NameResolver +} + +// NewNorwayClient knows how to construct a new client. +func NewNorwayClient(resolver NameResolver, opts ...option) (*NorwayClient, error) { + c := NorwayClient{ + BaseURL: baseURL, + UA: userAgent, + HTTPClient: &http.Client{ + Timeout: time.Second * 5, + }, + Resolver: resolver, + } + + for _, opt := range opts { + if err := opt(&c); err != nil { + return &NorwayClient{}, err + } + } + return &c, nil +} + +// GetForecast takes place and country code and +// returns weather summary and air temperature. +func (c NorwayClient) GetForecast(place, country string) (Weather, error) { + p, err := c.Resolver.GetCoordinates(place, country) + if err != nil { + return Weather{}, err + } + return c.getForecast(p.Lat, p.Lng) +} + +func (c NorwayClient) getForecast(lat, lon float64) (Weather, error) { + u, err := c.makeURL(lat, lon) + if err != nil { + return Weather{}, err + } + req, err := prepareRequest(u) + if err != nil { + return Weather{}, err + } + res, err := c.HTTPClient.Do(req) + if err != nil { + return Weather{}, err + } + defer res.Body.Close() + + var nf norwayForecast + if err := json.NewDecoder(res.Body).Decode(&nf); err != nil { + return Weather{}, err + } + if len(nf.Properties.Timeseries) < 1 { + return Weather{}, fmt.Errorf("invalid response %+v", nf) + } + + w := Weather{ + Summary: nf.Properties.Timeseries[0].Data.Next1Hours.Summary.SymbolCode, + Temp: nf.Properties.Timeseries[0].Data.Instant.Details.AirTemperature, + } + return w, nil +} + +func (c NorwayClient) makeURL(lat, lon float64) (string, error) { + base, err := url.Parse(c.BaseURL + "/weatherapi/locationforecast/2.0/compact") + if err != nil { + return "", err + } + params := url.Values{} + params.Add("lat", fmt.Sprintf("%.2f", lat)) + params.Add("lon", fmt.Sprintf("%.2f", lon)) + base.RawQuery = params.Encode() + return base.String(), nil +} + +func prepareRequest(u string) (*http.Request, error) { + req, err := http.NewRequest(http.MethodGet, u, nil) + if err != nil { + return nil, err + } + req.Header.Add("Content-Type", "application/json") + req.Header.Add("User-Agent", userAgent) + return req, nil +} + +// GetWeather returns current weather for given +// place and country using default client for the Norwegian +// meteorological Institute. +func GetWeather(place, country string) (Weather, error) { + resolver, err := NewWikipediaClient(os.Getenv("GEO_USERNAME")) + if err != nil { + return Weather{}, err + } + c, err := NewNorwayClient(resolver) + if err != nil { + return Weather{}, err + } + return c.GetForecast(place, country) +} diff --git a/norway_test.go b/norway_test.go new file mode 100644 index 0000000..d69168f --- /dev/null +++ b/norway_test.go @@ -0,0 +1,134 @@ +package meteo_test + +import ( + "io" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/qba73/meteo" +) + +func TestCreateNewMeteoClient(t *testing.T) { + t.Parallel() + var c *meteo.NorwayClient + resolver, err := meteo.NewWikipediaClient("DummyUsername") + if err != nil { + t.Fatal(err) + } + c, err = meteo.NewNorwayClient(resolver) + want := meteo.NorwayClient{ + // maybe other fields here, + Resolver: resolver, + } + if err != nil { + t.Fatal(err) + } + _ = c +} + +func TestCreateNewMeteoClientWithCustomUserAgent(t *testing.T) { + t.Parallel() + resolver, err := meteo.NewWikipediaClient("User") + if err != nil { + t.Fatal(err) + } + c, err := meteo.NewNorwayClient( + resolver, + meteo.WithUserAgent("CustomClient/1.0 https://customclient.com"), + ) + if err != nil { + t.Fatalf("creating client with custom agent, %s\n", err) + } + want := "CustomClient/1.0 https://customclient.com" + got := c.UA + if want != got { + t.Errorf("want %q, got %q", want, got) + } +} + +func TestCreateNewNorwayClientWithInvalidUserAgent(t *testing.T) { + t.Parallel() + resolver, err := meteo.NewWikipediaClient("User") + if err != nil { + t.Fatal(err) + } + _, err = meteo.NewNorwayClient(resolver, meteo.WithUserAgent("")) + if err == nil { + t.Errorf("invalid user agent string should return error") + } +} + +func TestGetForecast(t *testing.T) { + t.Parallel() + mux := http.NewServeMux() + + mux.HandleFunc("/weatherapi/locationforecast/2.0/compact", func(rw http.ResponseWriter, r *http.Request) { + testFile := "testdata/response-compact.json" + wantReqURL := "/weatherapi/locationforecast/2.0/compact?lat=53.86&lon=-9.30" + gotReqURL := r.RequestURI + if wantReqURL != gotReqURL { + t.Errorf("want %q for location compact forecast, got %q", wantReqURL, gotReqURL) + } + + f, err := os.Open(testFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + _, err = io.Copy(rw, f) + if err != nil { + t.Fatalf("copying data from file %s to test HTTP server: %v", testFile, err) + } + }) + + mux.HandleFunc("/wikipediaSearchJSON", func(rw http.ResponseWriter, r *http.Request) { + testFile := "testdata/response-geoname-wikipedia.json" + wantReqURL := "/wikipediaSearchJSON?maxRows=10&q=Castlebar&username=UserName" + gotReqURL := r.RequestURI + if wantReqURL != gotReqURL { + t.Errorf("want %q for wikipedia search JSON, got %q", wantReqURL, gotReqURL) + } + + f, err := os.Open(testFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + _, err = io.Copy(rw, f) + if err != nil { + t.Fatalf("copying data from file %s to test HTTP server: %v", testFile, err) + } + }) + ts := httptest.NewServer(mux) + defer ts.Close() + + resolver, err := meteo.NewWikipediaClient("UserName") + if err != nil { + t.Fatal(err) + } + resolver.BaseURL = ts.URL + + client, err := meteo.NewNorwayClient(resolver) + if err != nil { + t.Fatal(err) + } + client.BaseURL = ts.URL + + got, err := client.GetForecast("Castlebar", "IE") + if err != nil { + t.Errorf("error getting forecast data, %v", err) + } + want := meteo.Weather{ + Summary: "rain", + Temp: 13.7, + } + + if !cmp.Equal(want, got) { + t.Error(cmp.Diff(want, got)) + } +} diff --git a/postal.go b/postal.go new file mode 100644 index 0000000..b103ecd --- /dev/null +++ b/postal.go @@ -0,0 +1,134 @@ +package meteo + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "time" +) + +const ( + baseURLGeoNames = "http://api.geonames.org" +) + +// Place represents response from GeoNames web service. +type Place struct { + Lat float64 + Lng float64 + CountryCode string + PlaceName string +} + +type coordinates struct { + PostalCodes []struct { + Lat float64 `json:"lat"` + Lng float64 `json:"lng"` + CountryCode string `json:"countryCode"` + PlaceName string `json:"placeName"` + } `json:"postalCodes"` +} + +type geoNameOption func(*GeoNamesClient) error + +// WithGeoNamesUserAgent knows how to add custom user agent +// to web requests. +func WithGeoNamesUserAgent(ua string) geoNameOption { + return func(gnc *GeoNamesClient) error { + if ua == "" { + return errors.New("nil user agent") + } + gnc.UA = ua + return nil + } +} + +// GeoNamesClient is a client used for communicating +// with geo name web services. +type GeoNamesClient struct { + UA string + UserName string + BaseURL string + HTTPClient *http.Client +} + +// NewGeoNamesClient knows how to create a client for GeoNames Web services. +func NewGeoNamesClient(username string, opts ...geoNameOption) (*GeoNamesClient, error) { + if username == "" { + return nil, errors.New("missing user name") + } + + c := GeoNamesClient{ + BaseURL: baseURLGeoNames, + UA: userAgent, + UserName: username, + HTTPClient: &http.Client{ + Timeout: time.Second * 5, + }, + } + for _, opt := range opts { + if err := opt(&c); err != nil { + return &GeoNamesClient{}, err + } + } + return &c, nil +} + +// GetCoordinates knows how to retrieve lat and long coordinates +// for the given place name and country. +func (g GeoNamesClient) GetCoordinates(placeName, countryCode string) (Place, error) { + u, err := g.makeURL(placeName, countryCode) + if err != nil { + return Place{}, err + } + req, err := prepareRequest(u) + if err != nil { + return Place{}, err + } + res, err := g.HTTPClient.Do(req) + if err != nil { + return Place{}, err + } + defer res.Body.Close() + + var co coordinates + if err := json.NewDecoder(res.Body).Decode(&co); err != nil { + return Place{}, fmt.Errorf("decoding response %w", err) + } + if len(co.PostalCodes) < 1 { + return Place{}, fmt.Errorf("place %s in country %s not found", placeName, countryCode) + } + + pc := Place{ + Lat: co.PostalCodes[0].Lat, + Lng: co.PostalCodes[0].Lng, + PlaceName: co.PostalCodes[0].PlaceName, + CountryCode: co.PostalCodes[0].CountryCode, + } + + return pc, nil +} + +func (g GeoNamesClient) makeURL(placeName, countryCode string) (string, error) { + base, err := url.Parse(g.BaseURL + "/postalCodeSearchJSON") + if err != nil { + return "", fmt.Errorf("making url %w", err) + } + params := url.Values{} + params.Add("placename", placeName) + params.Add("country", countryCode) + params.Add("username", g.UserName) + base.RawQuery = params.Encode() + return base.String(), nil +} + +// GetCoordinates knows how to get Lat and Long coordinates for +// the given place and country using default geo client. +func GetCoordinates(placename, countryCode, username string) (Place, error) { + c, err := NewGeoNamesClient(username) + if err != nil { + return Place{}, err + } + return c.GetCoordinates(placename, countryCode) +} diff --git a/postal_test.go b/postal_test.go new file mode 100644 index 0000000..745677d --- /dev/null +++ b/postal_test.go @@ -0,0 +1,85 @@ +package meteo_test + +import ( + "io" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/qba73/meteo" +) + +func TestCreateNewGeoNamesClient(t *testing.T) { + t.Parallel() + var c *meteo.GeoNamesClient + c, err := meteo.NewGeoNamesClient("user") + if err != nil { + t.Fatal(err) + } + _ = c +} + +func TestCreateNewGeoNamesClientWithoutUserName(t *testing.T) { + t.Parallel() + _, err := meteo.NewGeoNamesClient("") + if err == nil { + t.Fatal("create client without user should return err") + } +} + +func TestCreateNewGeoNamesClientWithUser(t *testing.T) { + t.Parallel() + c, err := meteo.NewGeoNamesClient("User") + if err != nil { + t.Fatal(err) + } + want := "User" + if want != c.UserName { + t.Errorf("want %s, got %s", want, c.UserName) + } +} + +func TestGetCoordinatesSingleGeoNames(t *testing.T) { + t.Parallel() + + testFile := "testdata/response-geoname-single.json" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wantReqURL := "/postalCodeSearchJSON?country=IE&placename=Castlebar&username=UserName" + gotReqURL := r.RequestURI + if wantReqURL != gotReqURL { + t.Errorf("want %q for wikipedia URL, got %q", wantReqURL, gotReqURL) + } + + f, err := os.Open(testFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + io.Copy(w, f) + })) + defer ts.Close() + + c, err := meteo.NewGeoNamesClient("UserName") + if err != nil { + t.Fatal(err) + } + c.BaseURL = ts.URL + + got, err := c.GetCoordinates("Castlebar", "IE") + if err != nil { + t.Fatalf("GetCoordinates(\"Castlebar\", \"IE\") got err %v", err) + } + want := meteo.Place{ + Lng: -9.3, + Lat: 53.85, + PlaceName: "Castlebar", + CountryCode: "IE", + } + + if !cmp.Equal(want, got) { + t.Errorf("GetCoordinates('Castlebar', 'IE') \n%s", cmp.Diff(want, got)) + } +} diff --git a/testdata/response-compact.json b/testdata/response-compact.json new file mode 100644 index 0000000..ad8a5e4 --- /dev/null +++ b/testdata/response-compact.json @@ -0,0 +1,2871 @@ +{ + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -6.2, + 53.3, + 39 + ] + }, + "properties": { + "meta": { + "updated_at": "2021-10-28T01:28:31Z", + "units": { + "air_pressure_at_sea_level": "hPa", + "air_temperature": "celsius", + "cloud_area_fraction": "%", + "precipitation_amount": "mm", + "relative_humidity": "%", + "wind_from_direction": "degrees", + "wind_speed": "m/s" + } + }, + "timeseries": [ + { + "time": "2021-10-28T05:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 999.7, + "air_temperature": 13.7, + "cloud_area_fraction": 100.0, + "relative_humidity": 94.0, + "wind_from_direction": 180.3, + "wind_speed": 1.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 6.1 + } + } + } + }, + { + "time": "2021-10-28T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 999.3, + "air_temperature": 13.7, + "cloud_area_fraction": 100.0, + "relative_humidity": 93.8, + "wind_from_direction": 162.3, + "wind_speed": 2.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 6.7 + } + } + } + }, + { + "time": "2021-10-28T07:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 998.6, + "air_temperature": 13.5, + "cloud_area_fraction": 100.0, + "relative_humidity": 92.3, + "wind_from_direction": 164.6, + "wind_speed": 2.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.3 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 6.1 + } + } + } + }, + { + "time": "2021-10-28T08:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 998.4, + "air_temperature": 13.5, + "cloud_area_fraction": 100.0, + "relative_humidity": 90.8, + "wind_from_direction": 170.7, + "wind_speed": 2.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.3 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 5.8 + } + } + } + }, + { + "time": "2021-10-28T09:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 997.7, + "air_temperature": 13.7, + "cloud_area_fraction": 100.0, + "relative_humidity": 91.2, + "wind_from_direction": 165.9, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.9 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 5.7 + } + } + } + }, + { + "time": "2021-10-28T10:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 996.8, + "air_temperature": 14.1, + "cloud_area_fraction": 100.0, + "relative_humidity": 90.9, + "wind_from_direction": 184.2, + "wind_speed": 3.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 2.4 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 3.9 + } + } + } + }, + { + "time": "2021-10-28T11:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 996.1, + "air_temperature": 13.9, + "cloud_area_fraction": 100.0, + "relative_humidity": 90.7, + "wind_from_direction": 189.4, + "wind_speed": 2.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.1 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 1.5 + } + } + } + }, + { + "time": "2021-10-28T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.4, + "air_temperature": 14.1, + "cloud_area_fraction": 100.0, + "relative_humidity": 89.6, + "wind_from_direction": 221.4, + "wind_speed": 1.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-28T13:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.5, + "air_temperature": 14.3, + "cloud_area_fraction": 100.0, + "relative_humidity": 89.3, + "wind_from_direction": 249.6, + "wind_speed": 3.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-28T14:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.3, + "air_temperature": 14.4, + "cloud_area_fraction": 100.0, + "relative_humidity": 87.7, + "wind_from_direction": 220.0, + "wind_speed": 3.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-28T15:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.3, + "air_temperature": 14.2, + "cloud_area_fraction": 100.0, + "relative_humidity": 85.5, + "wind_from_direction": 225.8, + "wind_speed": 3.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-28T16:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.7, + "air_temperature": 13.4, + "cloud_area_fraction": 100.0, + "relative_humidity": 83.7, + "wind_from_direction": 248.3, + "wind_speed": 4.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.1 + } + } + } + }, + { + "time": "2021-10-28T17:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.7, + "air_temperature": 12.7, + "cloud_area_fraction": 100.0, + "relative_humidity": 85.8, + "wind_from_direction": 250.6, + "wind_speed": 3.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 1.2 + } + } + } + }, + { + "time": "2021-10-28T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.9, + "air_temperature": 12.0, + "cloud_area_fraction": 100.0, + "relative_humidity": 90.0, + "wind_from_direction": 253.5, + "wind_speed": 2.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 1.6 + } + } + } + }, + { + "time": "2021-10-28T19:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.7, + "air_temperature": 11.6, + "cloud_area_fraction": 100.0, + "relative_humidity": 91.4, + "wind_from_direction": 255.6, + "wind_speed": 2.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 1.6 + } + } + } + }, + { + "time": "2021-10-28T20:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.5, + "air_temperature": 11.3, + "cloud_area_fraction": 100.0, + "relative_humidity": 91.6, + "wind_from_direction": 224.5, + "wind_speed": 1.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 1.8 + } + } + } + }, + { + "time": "2021-10-28T21:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.4, + "air_temperature": 11.2, + "cloud_area_fraction": 100.0, + "relative_humidity": 92.3, + "wind_from_direction": 189.3, + "wind_speed": 1.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.1 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 3.0 + } + } + } + }, + { + "time": "2021-10-28T22:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.3, + "air_temperature": 10.9, + "cloud_area_fraction": 100.0, + "relative_humidity": 95.2, + "wind_from_direction": 213.3, + "wind_speed": 1.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 4.4 + } + } + } + }, + { + "time": "2021-10-28T23:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.1, + "air_temperature": 10.6, + "cloud_area_fraction": 100.0, + "relative_humidity": 95.5, + "wind_from_direction": 198.3, + "wind_speed": 1.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.5 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 4.0 + } + } + } + }, + { + "time": "2021-10-29T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.8, + "air_temperature": 10.7, + "cloud_area_fraction": 100.0, + "relative_humidity": 96.1, + "wind_from_direction": 353.3, + "wind_speed": 0.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 4.7 + } + } + } + }, + { + "time": "2021-10-29T01:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.3, + "air_temperature": 10.5, + "cloud_area_fraction": 100.0, + "relative_humidity": 97.0, + "wind_from_direction": 309.1, + "wind_speed": 0.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 6.3 + } + } + } + }, + { + "time": "2021-10-29T02:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.1, + "air_temperature": 10.4, + "cloud_area_fraction": 100.0, + "relative_humidity": 96.3, + "wind_from_direction": 303.9, + "wind_speed": 0.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 7.7 + } + } + } + }, + { + "time": "2021-10-29T03:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.3, + "air_temperature": 10.4, + "cloud_area_fraction": 100.0, + "relative_humidity": 94.2, + "wind_from_direction": 216.9, + "wind_speed": 0.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.5 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 6.7 + } + } + } + }, + { + "time": "2021-10-29T04:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.5, + "air_temperature": 10.2, + "cloud_area_fraction": 100.0, + "relative_humidity": 95.7, + "wind_from_direction": 306.6, + "wind_speed": 1.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 5.2 + } + } + } + }, + { + "time": "2021-10-29T05:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.0, + "air_temperature": 9.8, + "cloud_area_fraction": 100.0, + "relative_humidity": 98.0, + "wind_from_direction": 321.5, + "wind_speed": 1.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rainshowers_day" + }, + "details": { + "precipitation_amount": 4.6 + } + } + } + }, + { + "time": "2021-10-29T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.4, + "air_temperature": 9.5, + "cloud_area_fraction": 100.0, + "relative_humidity": 95.6, + "wind_from_direction": 290.1, + "wind_speed": 3.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rainshowers_day" + }, + "details": { + "precipitation_amount": 3.4 + } + } + } + }, + { + "time": "2021-10-29T07:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.6, + "air_temperature": 9.7, + "cloud_area_fraction": 100.0, + "relative_humidity": 93.1, + "wind_from_direction": 290.8, + "wind_speed": 4.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.5 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rainshowers_day" + }, + "details": { + "precipitation_amount": 1.8 + } + } + } + }, + { + "time": "2021-10-29T08:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.2, + "air_temperature": 9.6, + "cloud_area_fraction": 100.0, + "relative_humidity": 87.5, + "wind_from_direction": 280.5, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-29T09:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.7, + "air_temperature": 9.5, + "cloud_area_fraction": 100.0, + "relative_humidity": 88.9, + "wind_from_direction": 266.7, + "wind_speed": 3.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T10:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.9, + "air_temperature": 9.9, + "cloud_area_fraction": 92.2, + "relative_humidity": 87.8, + "wind_from_direction": 253.5, + "wind_speed": 3.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T11:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.2, + "air_temperature": 10.7, + "cloud_area_fraction": 21.9, + "relative_humidity": 81.2, + "wind_from_direction": 244.3, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.3, + "air_temperature": 11.5, + "cloud_area_fraction": 18.7, + "relative_humidity": 72.1, + "wind_from_direction": 241.2, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T13:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.1, + "air_temperature": 12.2, + "cloud_area_fraction": 14.1, + "relative_humidity": 65.9, + "wind_from_direction": 236.8, + "wind_speed": 3.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T14:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.7, + "air_temperature": 12.5, + "cloud_area_fraction": 29.7, + "relative_humidity": 63.0, + "wind_from_direction": 226.5, + "wind_speed": 3.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T15:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.4, + "air_temperature": 12.5, + "cloud_area_fraction": 26.6, + "relative_humidity": 63.7, + "wind_from_direction": 204.7, + "wind_speed": 2.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T16:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.3, + "air_temperature": 12.1, + "cloud_area_fraction": 78.1, + "relative_humidity": 66.0, + "wind_from_direction": 186.3, + "wind_speed": 2.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T17:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.1, + "air_temperature": 10.9, + "cloud_area_fraction": 99.2, + "relative_humidity": 75.7, + "wind_from_direction": 169.1, + "wind_speed": 2.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.9, + "air_temperature": 10.1, + "cloud_area_fraction": 99.2, + "relative_humidity": 80.8, + "wind_from_direction": 172.5, + "wind_speed": 3.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T19:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.5, + "air_temperature": 10.0, + "cloud_area_fraction": 100.0, + "relative_humidity": 81.8, + "wind_from_direction": 171.6, + "wind_speed": 3.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T20:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.1, + "air_temperature": 9.7, + "cloud_area_fraction": 100.0, + "relative_humidity": 83.9, + "wind_from_direction": 173.0, + "wind_speed": 4.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T21:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.7, + "air_temperature": 9.4, + "cloud_area_fraction": 100.0, + "relative_humidity": 88.5, + "wind_from_direction": 178.9, + "wind_speed": 3.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T22:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.5, + "air_temperature": 9.2, + "cloud_area_fraction": 100.0, + "relative_humidity": 89.5, + "wind_from_direction": 195.1, + "wind_speed": 3.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T23:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.2, + "air_temperature": 9.0, + "cloud_area_fraction": 100.0, + "relative_humidity": 89.8, + "wind_from_direction": 217.6, + "wind_speed": 3.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 989.7, + "air_temperature": 9.0, + "cloud_area_fraction": 100.0, + "relative_humidity": 89.9, + "wind_from_direction": 231.6, + "wind_speed": 3.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T01:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 989.8, + "air_temperature": 8.7, + "cloud_area_fraction": 93.7, + "relative_humidity": 91.6, + "wind_from_direction": 248.2, + "wind_speed": 3.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T02:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.1, + "air_temperature": 8.3, + "cloud_area_fraction": 83.6, + "relative_humidity": 93.6, + "wind_from_direction": 255.3, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T03:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.4, + "air_temperature": 8.0, + "cloud_area_fraction": 70.3, + "relative_humidity": 93.2, + "wind_from_direction": 254.4, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T04:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.2, + "air_temperature": 7.8, + "cloud_area_fraction": 46.1, + "relative_humidity": 91.5, + "wind_from_direction": 251.8, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T05:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.8, + "air_temperature": 7.5, + "cloud_area_fraction": 35.9, + "relative_humidity": 91.1, + "wind_from_direction": 247.0, + "wind_speed": 3.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.2, + "air_temperature": 7.4, + "cloud_area_fraction": 28.1, + "relative_humidity": 90.8, + "wind_from_direction": 238.9, + "wind_speed": 3.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T07:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.9, + "air_temperature": 7.1, + "cloud_area_fraction": 18.0, + "relative_humidity": 92.8, + "wind_from_direction": 240.3, + "wind_speed": 3.7 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T08:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.7, + "air_temperature": 7.2, + "cloud_area_fraction": 11.7, + "relative_humidity": 90.8, + "wind_from_direction": 238.5, + "wind_speed": 3.6 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T09:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.3, + "air_temperature": 7.8, + "cloud_area_fraction": 11.7, + "relative_humidity": 86.0, + "wind_from_direction": 242.0, + "wind_speed": 3.4 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T10:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.5, + "air_temperature": 8.9, + "cloud_area_fraction": 4.7, + "relative_humidity": 80.1, + "wind_from_direction": 239.6, + "wind_speed": 3.3 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T11:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.0, + "air_temperature": 9.8, + "cloud_area_fraction": 10.2, + "relative_humidity": 74.8, + "wind_from_direction": 243.7, + "wind_speed": 3.7 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.1, + "air_temperature": 10.5, + "cloud_area_fraction": 50.8, + "relative_humidity": 69.6, + "wind_from_direction": 245.2, + "wind_speed": 3.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T13:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.2, + "air_temperature": 11.0, + "cloud_area_fraction": 65.6, + "relative_humidity": 65.2, + "wind_from_direction": 241.9, + "wind_speed": 4.2 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T14:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.3, + "air_temperature": 11.1, + "cloud_area_fraction": 32.8, + "relative_humidity": 63.0, + "wind_from_direction": 242.4, + "wind_speed": 4.4 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T15:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.6, + "air_temperature": 11.2, + "cloud_area_fraction": 20.3, + "relative_humidity": 61.6, + "wind_from_direction": 242.6, + "wind_speed": 4.1 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T16:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.0, + "air_temperature": 10.7, + "cloud_area_fraction": 49.2, + "relative_humidity": 63.6, + "wind_from_direction": 238.4, + "wind_speed": 2.8 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T17:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.4, + "air_temperature": 9.6, + "cloud_area_fraction": 52.3, + "relative_humidity": 74.2, + "wind_from_direction": 204.4, + "wind_speed": 2.0 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.5, + "air_temperature": 8.4, + "cloud_area_fraction": 50.8, + "relative_humidity": 86.9, + "wind_from_direction": 184.0, + "wind_speed": 2.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-31T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.2, + "air_temperature": 10.1, + "cloud_area_fraction": 56.2, + "relative_humidity": 79.6, + "wind_from_direction": 158.7, + "wind_speed": 6.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rainshowers_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrainshowers_night" + }, + "details": { + "precipitation_amount": 7.9 + } + } + } + }, + { + "time": "2021-10-31T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 983.7, + "air_temperature": 10.8, + "cloud_area_fraction": 100.0, + "relative_humidity": 90.3, + "wind_from_direction": 143.5, + "wind_speed": 8.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrainshowers_day" + }, + "details": { + "precipitation_amount": 5.9 + } + } + } + }, + { + "time": "2021-10-31T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 983.5, + "air_temperature": 10.0, + "cloud_area_fraction": 34.4, + "relative_humidity": 77.6, + "wind_from_direction": 248.8, + "wind_speed": 6.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.2 + } + } + } + }, + { + "time": "2021-10-31T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 984.5, + "air_temperature": 8.3, + "cloud_area_fraction": 2.3, + "relative_humidity": 79.3, + "wind_from_direction": 221.1, + "wind_speed": 3.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-01T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 983.3, + "air_temperature": 5.3, + "cloud_area_fraction": 99.2, + "relative_humidity": 97.3, + "wind_from_direction": 286.3, + "wind_speed": 2.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-01T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 985.4, + "air_temperature": 5.4, + "cloud_area_fraction": 100.0, + "relative_humidity": 96.3, + "wind_from_direction": 269.9, + "wind_speed": 5.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-11-01T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 989.7, + "air_temperature": 9.9, + "cloud_area_fraction": 100.0, + "relative_humidity": 77.8, + "wind_from_direction": 297.2, + "wind_speed": 7.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-01T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.7, + "air_temperature": 9.1, + "cloud_area_fraction": 100.0, + "relative_humidity": 77.6, + "wind_from_direction": 294.1, + "wind_speed": 6.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 997.0, + "air_temperature": 7.0, + "cloud_area_fraction": 100.0, + "relative_humidity": 91.5, + "wind_from_direction": 280.6, + "wind_speed": 6.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 999.2, + "air_temperature": 5.8, + "cloud_area_fraction": 25.0, + "relative_humidity": 95.7, + "wind_from_direction": 274.9, + "wind_speed": 5.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1001.4, + "air_temperature": 8.9, + "cloud_area_fraction": 22.7, + "relative_humidity": 74.1, + "wind_from_direction": 291.2, + "wind_speed": 7.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1002.5, + "air_temperature": 7.9, + "cloud_area_fraction": 51.6, + "relative_humidity": 79.5, + "wind_from_direction": 283.1, + "wind_speed": 7.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "lightrainshowers_night" + }, + "details": { + "precipitation_amount": 0.9 + } + } + } + }, + { + "time": "2021-11-03T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1005.4, + "air_temperature": 8.0, + "cloud_area_fraction": 86.7, + "relative_humidity": 83.1, + "wind_from_direction": 299.1, + "wind_speed": 7.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-03T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1010.6, + "air_temperature": 6.7, + "cloud_area_fraction": 7.8, + "relative_humidity": 80.2, + "wind_from_direction": 309.5, + "wind_speed": 7.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-03T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1015.2, + "air_temperature": 8.6, + "cloud_area_fraction": 68.0, + "relative_humidity": 69.6, + "wind_from_direction": 326.0, + "wind_speed": 6.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-03T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1018.0, + "air_temperature": 6.9, + "cloud_area_fraction": 26.6, + "relative_humidity": 79.4, + "wind_from_direction": 304.5, + "wind_speed": 4.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "clearsky_night" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1020.0, + "air_temperature": 3.9, + "cloud_area_fraction": 0.8, + "relative_humidity": 95.6, + "wind_from_direction": 273.0, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "clearsky_night" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1018.5, + "air_temperature": 5.2, + "cloud_area_fraction": 8.6, + "relative_humidity": 87.0, + "wind_from_direction": 242.6, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.9, + "air_temperature": 9.0, + "cloud_area_fraction": 96.1, + "relative_humidity": 82.7, + "wind_from_direction": 248.6, + "wind_speed": 6.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.0, + "air_temperature": 10.8, + "cloud_area_fraction": 100.0, + "relative_humidity": 84.4, + "wind_from_direction": 249.6, + "wind_speed": 5.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.1 + } + } + } + }, + { + "time": "2021-11-05T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.2, + "air_temperature": 10.5, + "cloud_area_fraction": 100.0, + "relative_humidity": 89.2, + "wind_from_direction": 249.6, + "wind_speed": 4.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-11-05T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.1, + "air_temperature": 10.0, + "cloud_area_fraction": 100.0, + "relative_humidity": 90.5, + "wind_from_direction": 242.7, + "wind_speed": 4.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-05T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1013.5, + "air_temperature": 10.4, + "cloud_area_fraction": 100.0, + "relative_humidity": 84.4, + "wind_from_direction": 180.1, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 9.1 + } + } + } + }, + { + "time": "2021-11-05T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1001.3, + "air_temperature": 11.9, + "cloud_area_fraction": 100.0, + "relative_humidity": 84.6, + "wind_from_direction": 191.8, + "wind_speed": 8.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 4.7 + } + } + } + }, + { + "time": "2021-11-06T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 997.7, + "air_temperature": 14.2, + "cloud_area_fraction": 100.0, + "relative_humidity": 88.2, + "wind_from_direction": 255.3, + "wind_speed": 8.8 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-06T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1002.5, + "air_temperature": 10.5, + "cloud_area_fraction": 100.0, + "relative_humidity": 87.9, + "wind_from_direction": 260.1, + "wind_speed": 4.8 + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/testdata/response-complete.json b/testdata/response-complete.json new file mode 100644 index 0000000..3249bbe --- /dev/null +++ b/testdata/response-complete.json @@ -0,0 +1,3519 @@ +{ + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -6.2, + 53.3, + 39 + ] + }, + "properties": { + "meta": { + "updated_at": "2021-10-28T01:24:06Z", + "units": { + "air_pressure_at_sea_level": "hPa", + "air_temperature": "celsius", + "air_temperature_max": "celsius", + "air_temperature_min": "celsius", + "cloud_area_fraction": "%", + "cloud_area_fraction_high": "%", + "cloud_area_fraction_low": "%", + "cloud_area_fraction_medium": "%", + "dew_point_temperature": "celsius", + "fog_area_fraction": "%", + "precipitation_amount": "mm", + "relative_humidity": "%", + "ultraviolet_index_clear_sky": "1", + "wind_from_direction": "degrees", + "wind_speed": "m/s" + } + }, + "timeseries": [ + { + "time": "2021-10-28T05:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 999.7, + "air_temperature": 13.7, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 100.0, + "cloud_area_fraction_medium": 99.2, + "dew_point_temperature": 12.7, + "fog_area_fraction": 0.0, + "relative_humidity": 94.0, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 180.3, + "wind_speed": 1.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 14.1, + "air_temperature_min": 13.5, + "precipitation_amount": 6.1 + } + } + } + }, + { + "time": "2021-10-28T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 999.3, + "air_temperature": 13.7, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 39.1, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 12.7, + "fog_area_fraction": 0.0, + "relative_humidity": 93.8, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 162.3, + "wind_speed": 2.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 14.1, + "air_temperature_min": 13.5, + "precipitation_amount": 6.7 + } + } + } + }, + { + "time": "2021-10-28T07:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 998.6, + "air_temperature": 13.5, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 3.9, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 12.3, + "fog_area_fraction": 0.0, + "relative_humidity": 92.3, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 164.6, + "wind_speed": 2.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.3 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 14.3, + "air_temperature_min": 13.5, + "precipitation_amount": 6.1 + } + } + } + }, + { + "time": "2021-10-28T08:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 998.4, + "air_temperature": 13.5, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 2.3, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 12.0, + "fog_area_fraction": 0.0, + "relative_humidity": 90.8, + "ultraviolet_index_clear_sky": 0.1, + "wind_from_direction": 170.7, + "wind_speed": 2.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.3 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 14.4, + "air_temperature_min": 13.7, + "precipitation_amount": 5.8 + } + } + } + }, + { + "time": "2021-10-28T09:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 997.7, + "air_temperature": 13.7, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 14.8, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 12.3, + "fog_area_fraction": 0.0, + "relative_humidity": 91.2, + "ultraviolet_index_clear_sky": 0.4, + "wind_from_direction": 165.9, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.9 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 14.4, + "air_temperature_min": 13.9, + "precipitation_amount": 5.7 + } + } + } + }, + { + "time": "2021-10-28T10:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 996.8, + "air_temperature": 14.1, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 43.7, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 12.6, + "fog_area_fraction": 0.0, + "relative_humidity": 90.9, + "ultraviolet_index_clear_sky": 0.9, + "wind_from_direction": 184.2, + "wind_speed": 3.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 2.4 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 14.4, + "air_temperature_min": 13.4, + "precipitation_amount": 3.9 + } + } + } + }, + { + "time": "2021-10-28T11:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 996.1, + "air_temperature": 13.9, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 18.0, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 12.4, + "fog_area_fraction": 0.0, + "relative_humidity": 90.7, + "ultraviolet_index_clear_sky": 1.2, + "wind_from_direction": 189.4, + "wind_speed": 2.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.1 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 14.4, + "air_temperature_min": 12.7, + "precipitation_amount": 1.5 + } + } + } + }, + { + "time": "2021-10-28T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.4, + "air_temperature": 14.1, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 99.2, + "cloud_area_fraction_low": 14.8, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 12.3, + "fog_area_fraction": 0.0, + "relative_humidity": 89.6, + "ultraviolet_index_clear_sky": 1.4, + "wind_from_direction": 221.4, + "wind_speed": 1.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 14.4, + "air_temperature_min": 12.0, + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-28T13:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.5, + "air_temperature": 14.3, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 18.0, + "cloud_area_fraction_medium": 10.2, + "dew_point_temperature": 12.5, + "fog_area_fraction": 0.0, + "relative_humidity": 89.3, + "ultraviolet_index_clear_sky": 1.2, + "wind_from_direction": 249.6, + "wind_speed": 3.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 14.4, + "air_temperature_min": 11.6, + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-28T14:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.3, + "air_temperature": 14.4, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 54.7, + "cloud_area_fraction_medium": 17.2, + "dew_point_temperature": 12.3, + "fog_area_fraction": 0.0, + "relative_humidity": 87.7, + "ultraviolet_index_clear_sky": 0.9, + "wind_from_direction": 220.0, + "wind_speed": 3.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 14.2, + "air_temperature_min": 11.3, + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-28T15:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.3, + "air_temperature": 14.2, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 7.0, + "cloud_area_fraction_medium": 13.3, + "dew_point_temperature": 11.8, + "fog_area_fraction": 0.0, + "relative_humidity": 85.5, + "ultraviolet_index_clear_sky": 0.4, + "wind_from_direction": 225.8, + "wind_speed": 3.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 13.4, + "air_temperature_min": 11.2, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-28T16:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.7, + "air_temperature": 13.4, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 50.8, + "cloud_area_fraction_medium": 34.4, + "dew_point_temperature": 10.7, + "fog_area_fraction": 0.0, + "relative_humidity": 83.7, + "ultraviolet_index_clear_sky": 0.1, + "wind_from_direction": 248.3, + "wind_speed": 4.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 12.7, + "air_temperature_min": 10.9, + "precipitation_amount": 0.1 + } + } + } + }, + { + "time": "2021-10-28T17:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.7, + "air_temperature": 12.7, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 7.8, + "cloud_area_fraction_medium": 7.8, + "dew_point_temperature": 10.5, + "fog_area_fraction": 0.0, + "relative_humidity": 85.8, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 250.6, + "wind_speed": 3.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 12.0, + "air_temperature_min": 10.6, + "precipitation_amount": 1.2 + } + } + } + }, + { + "time": "2021-10-28T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.9, + "air_temperature": 12.0, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 3.9, + "cloud_area_fraction_medium": 1.6, + "dew_point_temperature": 10.5, + "fog_area_fraction": 0.0, + "relative_humidity": 90.0, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 253.5, + "wind_speed": 2.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 11.6, + "air_temperature_min": 10.6, + "precipitation_amount": 1.6 + } + } + } + }, + { + "time": "2021-10-28T19:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.7, + "air_temperature": 11.6, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 1.6, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 10.4, + "fog_area_fraction": 0.0, + "relative_humidity": 91.4, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 255.6, + "wind_speed": 2.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 11.3, + "air_temperature_min": 10.5, + "precipitation_amount": 1.6 + } + } + } + }, + { + "time": "2021-10-28T20:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.5, + "air_temperature": 11.3, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 0.0, + "cloud_area_fraction_medium": 75.8, + "dew_point_temperature": 10.1, + "fog_area_fraction": 0.0, + "relative_humidity": 91.6, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 224.5, + "wind_speed": 1.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 11.2, + "air_temperature_min": 10.4, + "precipitation_amount": 1.8 + } + } + } + }, + { + "time": "2021-10-28T21:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.4, + "air_temperature": 11.2, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 0.0, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 10.1, + "fog_area_fraction": 0.0, + "relative_humidity": 92.3, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 189.3, + "wind_speed": 1.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.1 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 10.9, + "air_temperature_min": 10.4, + "precipitation_amount": 3.0 + } + } + } + }, + { + "time": "2021-10-28T22:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.3, + "air_temperature": 10.9, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 26.6, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 10.3, + "fog_area_fraction": 0.0, + "relative_humidity": 95.2, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 213.3, + "wind_speed": 1.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 10.7, + "air_temperature_min": 10.2, + "precipitation_amount": 4.4 + } + } + } + }, + { + "time": "2021-10-28T23:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.1, + "air_temperature": 10.6, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 23.4, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 10.0, + "fog_area_fraction": 0.0, + "relative_humidity": 95.5, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 198.3, + "wind_speed": 1.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.5 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 10.7, + "air_temperature_min": 9.8, + "precipitation_amount": 4.0 + } + } + } + }, + { + "time": "2021-10-29T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.8, + "air_temperature": 10.7, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 0.8, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 10.2, + "fog_area_fraction": 0.0, + "relative_humidity": 96.1, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 353.3, + "wind_speed": 0.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 10.5, + "air_temperature_min": 9.5, + "precipitation_amount": 4.7 + } + } + } + }, + { + "time": "2021-10-29T01:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.3, + "air_temperature": 10.5, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 3.9, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 10.1, + "fog_area_fraction": 0.0, + "relative_humidity": 97.0, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 309.1, + "wind_speed": 0.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 10.4, + "air_temperature_min": 9.5, + "precipitation_amount": 6.3 + } + } + } + }, + { + "time": "2021-10-29T02:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.1, + "air_temperature": 10.4, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 57.8, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 9.9, + "fog_area_fraction": 25.8, + "relative_humidity": 96.3, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 303.9, + "wind_speed": 0.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 10.4, + "air_temperature_min": 9.5, + "precipitation_amount": 7.7 + } + } + } + }, + { + "time": "2021-10-29T03:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.3, + "air_temperature": 10.4, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 4.7, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 9.5, + "fog_area_fraction": 0.0, + "relative_humidity": 94.2, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 216.9, + "wind_speed": 0.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.5 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 10.2, + "air_temperature_min": 9.5, + "precipitation_amount": 6.7 + } + } + } + }, + { + "time": "2021-10-29T04:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.5, + "air_temperature": 10.2, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 45.3, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 9.5, + "fog_area_fraction": 0.0, + "relative_humidity": 95.7, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 306.6, + "wind_speed": 1.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "precipitation_amount": 0.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 9.9, + "air_temperature_min": 9.5, + "precipitation_amount": 5.2 + } + } + } + }, + { + "time": "2021-10-29T05:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.0, + "air_temperature": 9.8, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 100.0, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 9.5, + "fog_area_fraction": 0.0, + "relative_humidity": 98.0, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 321.5, + "wind_speed": 1.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rainshowers_day" + }, + "details": { + "air_temperature_max": 10.7, + "air_temperature_min": 9.5, + "precipitation_amount": 4.6 + } + } + } + }, + { + "time": "2021-10-29T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.4, + "air_temperature": 9.5, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 94.5, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 8.8, + "fog_area_fraction": 0.0, + "relative_humidity": 95.6, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 290.1, + "wind_speed": 3.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.6 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rainshowers_day" + }, + "details": { + "air_temperature_max": 11.5, + "air_temperature_min": 9.5, + "precipitation_amount": 3.4 + } + } + } + }, + { + "time": "2021-10-29T07:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.6, + "air_temperature": 9.7, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 100.0, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 8.6, + "fog_area_fraction": 0.0, + "relative_humidity": 93.1, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 290.8, + "wind_speed": 4.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "precipitation_amount": 1.5 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rainshowers_day" + }, + "details": { + "air_temperature_max": 12.2, + "air_temperature_min": 9.5, + "precipitation_amount": 1.8 + } + } + } + }, + { + "time": "2021-10-29T08:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.2, + "air_temperature": 9.6, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 0.0, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 7.5, + "fog_area_fraction": 0.0, + "relative_humidity": 87.5, + "ultraviolet_index_clear_sky": 0.1, + "wind_from_direction": 280.5, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "lightrain" + }, + "details": { + "precipitation_amount": 0.2 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 12.5, + "air_temperature_min": 9.5, + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-10-29T09:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.7, + "air_temperature": 9.5, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 19.5, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 7.7, + "fog_area_fraction": 0.0, + "relative_humidity": 88.9, + "ultraviolet_index_clear_sky": 0.4, + "wind_from_direction": 266.7, + "wind_speed": 3.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 12.5, + "air_temperature_min": 9.9, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T10:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.9, + "air_temperature": 9.9, + "cloud_area_fraction": 92.2, + "cloud_area_fraction_high": 61.7, + "cloud_area_fraction_low": 11.7, + "cloud_area_fraction_medium": 89.1, + "dew_point_temperature": 7.8, + "fog_area_fraction": 0.0, + "relative_humidity": 87.8, + "ultraviolet_index_clear_sky": 0.7, + "wind_from_direction": 253.5, + "wind_speed": 3.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 12.5, + "air_temperature_min": 10.7, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T11:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.2, + "air_temperature": 10.7, + "cloud_area_fraction": 21.9, + "cloud_area_fraction_high": 3.1, + "cloud_area_fraction_low": 18.7, + "cloud_area_fraction_medium": 7.0, + "dew_point_temperature": 7.5, + "fog_area_fraction": 0.0, + "relative_humidity": 81.2, + "ultraviolet_index_clear_sky": 1.1, + "wind_from_direction": 244.3, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 12.5, + "air_temperature_min": 10.9, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.3, + "air_temperature": 11.5, + "cloud_area_fraction": 18.7, + "cloud_area_fraction_high": 1.6, + "cloud_area_fraction_low": 15.6, + "cloud_area_fraction_medium": 4.7, + "dew_point_temperature": 6.6, + "fog_area_fraction": 0.0, + "relative_humidity": 72.1, + "ultraviolet_index_clear_sky": 1.2, + "wind_from_direction": 241.2, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 12.5, + "air_temperature_min": 10.1, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T13:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.1, + "air_temperature": 12.2, + "cloud_area_fraction": 14.1, + "cloud_area_fraction_high": 3.9, + "cloud_area_fraction_low": 7.8, + "cloud_area_fraction_medium": 6.2, + "dew_point_temperature": 6.0, + "fog_area_fraction": 0.0, + "relative_humidity": 65.9, + "ultraviolet_index_clear_sky": 1.1, + "wind_from_direction": 236.8, + "wind_speed": 3.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 12.5, + "air_temperature_min": 10.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T14:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.7, + "air_temperature": 12.5, + "cloud_area_fraction": 29.7, + "cloud_area_fraction_high": 21.9, + "cloud_area_fraction_low": 7.0, + "cloud_area_fraction_medium": 7.0, + "dew_point_temperature": 5.7, + "fog_area_fraction": 0.0, + "relative_humidity": 63.0, + "ultraviolet_index_clear_sky": 0.7, + "wind_from_direction": 226.5, + "wind_speed": 3.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 12.5, + "air_temperature_min": 9.7, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T15:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.4, + "air_temperature": 12.5, + "cloud_area_fraction": 26.6, + "cloud_area_fraction_high": 25.8, + "cloud_area_fraction_low": 0.8, + "cloud_area_fraction_medium": 0.8, + "dew_point_temperature": 5.9, + "fog_area_fraction": 0.0, + "relative_humidity": 63.7, + "ultraviolet_index_clear_sky": 0.4, + "wind_from_direction": 204.7, + "wind_speed": 2.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 12.1, + "air_temperature_min": 9.4, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T16:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.3, + "air_temperature": 12.1, + "cloud_area_fraction": 78.1, + "cloud_area_fraction_high": 77.3, + "cloud_area_fraction_low": 0.8, + "cloud_area_fraction_medium": 0.8, + "dew_point_temperature": 6.0, + "fog_area_fraction": 0.0, + "relative_humidity": 66.0, + "ultraviolet_index_clear_sky": 0.1, + "wind_from_direction": 186.3, + "wind_speed": 2.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 10.9, + "air_temperature_min": 9.2, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T17:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.1, + "air_temperature": 10.9, + "cloud_area_fraction": 99.2, + "cloud_area_fraction_high": 99.2, + "cloud_area_fraction_low": 2.3, + "cloud_area_fraction_medium": 1.6, + "dew_point_temperature": 6.8, + "fog_area_fraction": 0.0, + "relative_humidity": 75.7, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 169.1, + "wind_speed": 2.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 10.1, + "air_temperature_min": 9.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.9, + "air_temperature": 10.1, + "cloud_area_fraction": 99.2, + "cloud_area_fraction_high": 99.2, + "cloud_area_fraction_low": 7.0, + "cloud_area_fraction_medium": 29.7, + "dew_point_temperature": 6.8, + "fog_area_fraction": 0.0, + "relative_humidity": 80.8, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 172.5, + "wind_speed": 3.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 10.0, + "air_temperature_min": 9.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T19:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.5, + "air_temperature": 10.0, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 21.9, + "cloud_area_fraction_medium": 75.8, + "dew_point_temperature": 6.8, + "fog_area_fraction": 0.0, + "relative_humidity": 81.8, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 171.6, + "wind_speed": 3.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 9.7, + "air_temperature_min": 8.7, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T20:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.1, + "air_temperature": 9.7, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 11.7, + "cloud_area_fraction_medium": 99.2, + "dew_point_temperature": 6.9, + "fog_area_fraction": 0.0, + "relative_humidity": 83.9, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 173.0, + "wind_speed": 4.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 9.4, + "air_temperature_min": 8.3, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T21:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.7, + "air_temperature": 9.4, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 7.0, + "cloud_area_fraction_medium": 93.7, + "dew_point_temperature": 7.4, + "fog_area_fraction": 0.0, + "relative_humidity": 88.5, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 178.9, + "wind_speed": 3.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 9.2, + "air_temperature_min": 8.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T22:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.5, + "air_temperature": 9.2, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 7.8, + "cloud_area_fraction_medium": 94.5, + "dew_point_temperature": 7.5, + "fog_area_fraction": 0.0, + "relative_humidity": 89.5, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 195.1, + "wind_speed": 3.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "air_temperature_max": 9.0, + "air_temperature_min": 7.8, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-29T23:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.2, + "air_temperature": 9.0, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 14.1, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 7.4, + "fog_area_fraction": 0.0, + "relative_humidity": 89.8, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 217.6, + "wind_speed": 3.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "air_temperature_max": 9.0, + "air_temperature_min": 7.5, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 989.7, + "air_temperature": 9.0, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 21.1, + "cloud_area_fraction_medium": 26.6, + "dew_point_temperature": 7.4, + "fog_area_fraction": 0.0, + "relative_humidity": 89.9, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 231.6, + "wind_speed": 3.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "air_temperature_max": 8.7, + "air_temperature_min": 7.4, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T01:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 989.8, + "air_temperature": 8.7, + "cloud_area_fraction": 93.7, + "cloud_area_fraction_high": 74.2, + "cloud_area_fraction_low": 39.8, + "cloud_area_fraction_medium": 6.2, + "dew_point_temperature": 7.4, + "fog_area_fraction": 1.6, + "relative_humidity": 91.6, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 248.2, + "wind_speed": 3.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "air_temperature_max": 8.3, + "air_temperature_min": 7.1, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T02:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.1, + "air_temperature": 8.3, + "cloud_area_fraction": 83.6, + "cloud_area_fraction_high": 10.2, + "cloud_area_fraction_low": 82.0, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 7.3, + "fog_area_fraction": 9.4, + "relative_humidity": 93.6, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 255.3, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "air_temperature_max": 8.0, + "air_temperature_min": 7.1, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T03:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.4, + "air_temperature": 8.0, + "cloud_area_fraction": 70.3, + "cloud_area_fraction_high": 33.6, + "cloud_area_fraction_low": 54.7, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 7.0, + "fog_area_fraction": 0.0, + "relative_humidity": 93.2, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 254.4, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "air_temperature_max": 7.8, + "air_temperature_min": 7.1, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T04:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.2, + "air_temperature": 7.8, + "cloud_area_fraction": 46.1, + "cloud_area_fraction_high": 1.6, + "cloud_area_fraction_low": 45.3, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 6.5, + "fog_area_fraction": 0.0, + "relative_humidity": 91.5, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 251.8, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 8.9, + "air_temperature_min": 7.1, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T05:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 990.8, + "air_temperature": 7.5, + "cloud_area_fraction": 35.9, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 35.9, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 6.2, + "fog_area_fraction": 4.7, + "relative_humidity": 91.1, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 247.0, + "wind_speed": 3.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 9.8, + "air_temperature_min": 7.1, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.2, + "air_temperature": 7.4, + "cloud_area_fraction": 28.1, + "cloud_area_fraction_high": 5.5, + "cloud_area_fraction_low": 24.2, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 6.0, + "fog_area_fraction": 4.7, + "relative_humidity": 90.8, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 238.9, + "wind_speed": 3.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 10.5, + "air_temperature_min": 7.1, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T07:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 991.9, + "air_temperature": 7.1, + "cloud_area_fraction": 18.0, + "cloud_area_fraction_high": 0.8, + "cloud_area_fraction_low": 18.0, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 6.0, + "fog_area_fraction": 1.6, + "relative_humidity": 92.8, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 240.3, + "wind_speed": 3.7 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 11.0, + "air_temperature_min": 7.2, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T08:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.7, + "air_temperature": 7.2, + "cloud_area_fraction": 11.7, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 11.7, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 5.9, + "fog_area_fraction": 0.8, + "relative_humidity": 90.8, + "ultraviolet_index_clear_sky": 0.1, + "wind_from_direction": 238.5, + "wind_speed": 3.6 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 11.1, + "air_temperature_min": 7.8, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T09:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.3, + "air_temperature": 7.8, + "cloud_area_fraction": 11.7, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 11.7, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 5.7, + "fog_area_fraction": 0.0, + "relative_humidity": 86.0, + "ultraviolet_index_clear_sky": 0.4, + "wind_from_direction": 242.0, + "wind_speed": 3.4 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 11.2, + "air_temperature_min": 8.9, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T10:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.5, + "air_temperature": 8.9, + "cloud_area_fraction": 4.7, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 4.7, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 5.7, + "fog_area_fraction": 0.0, + "relative_humidity": 80.1, + "ultraviolet_index_clear_sky": 0.7, + "wind_from_direction": 239.6, + "wind_speed": 3.3 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "clearsky_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 11.2, + "air_temperature_min": 9.8, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T11:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.0, + "air_temperature": 9.8, + "cloud_area_fraction": 10.2, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 10.2, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 5.5, + "fog_area_fraction": 0.0, + "relative_humidity": 74.8, + "ultraviolet_index_clear_sky": 1.0, + "wind_from_direction": 243.7, + "wind_speed": 3.7 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 11.2, + "air_temperature_min": 9.6, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.1, + "air_temperature": 10.5, + "cloud_area_fraction": 50.8, + "cloud_area_fraction_high": 39.8, + "cloud_area_fraction_low": 18.0, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 5.2, + "fog_area_fraction": 0.0, + "relative_humidity": 69.6, + "ultraviolet_index_clear_sky": 1.2, + "wind_from_direction": 245.2, + "wind_speed": 3.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 11.2, + "air_temperature_min": 8.4, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T13:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.2, + "air_temperature": 11.0, + "cloud_area_fraction": 65.6, + "cloud_area_fraction_high": 53.9, + "cloud_area_fraction_low": 25.0, + "cloud_area_fraction_medium": 3.1, + "dew_point_temperature": 4.8, + "fog_area_fraction": 0.0, + "relative_humidity": 65.2, + "ultraviolet_index_clear_sky": 1.1, + "wind_from_direction": 241.9, + "wind_speed": 4.2 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T14:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.3, + "air_temperature": 11.1, + "cloud_area_fraction": 32.8, + "cloud_area_fraction_high": 14.8, + "cloud_area_fraction_low": 21.1, + "cloud_area_fraction_medium": 3.1, + "dew_point_temperature": 4.4, + "fog_area_fraction": 0.0, + "relative_humidity": 63.0, + "ultraviolet_index_clear_sky": 0.7, + "wind_from_direction": 242.4, + "wind_speed": 4.4 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T15:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 994.6, + "air_temperature": 11.2, + "cloud_area_fraction": 20.3, + "cloud_area_fraction_high": 7.8, + "cloud_area_fraction_low": 11.7, + "cloud_area_fraction_medium": 3.1, + "dew_point_temperature": 4.1, + "fog_area_fraction": 0.0, + "relative_humidity": 61.6, + "ultraviolet_index_clear_sky": 0.4, + "wind_from_direction": 242.6, + "wind_speed": 4.1 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T16:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.0, + "air_temperature": 10.7, + "cloud_area_fraction": 49.2, + "cloud_area_fraction_high": 49.2, + "cloud_area_fraction_low": 0.8, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 4.1, + "fog_area_fraction": 0.0, + "relative_humidity": 63.6, + "ultraviolet_index_clear_sky": 0.1, + "wind_from_direction": 238.4, + "wind_speed": 2.8 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T17:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.4, + "air_temperature": 9.6, + "cloud_area_fraction": 52.3, + "cloud_area_fraction_high": 52.3, + "cloud_area_fraction_low": 0.0, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 5.2, + "fog_area_fraction": 0.0, + "relative_humidity": 74.2, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 204.4, + "wind_speed": 2.0 + } + }, + "next_1_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-30T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 995.5, + "air_temperature": 8.4, + "cloud_area_fraction": 50.8, + "cloud_area_fraction_high": 50.8, + "cloud_area_fraction_low": 0.0, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 6.4, + "fog_area_fraction": 0.0, + "relative_humidity": 86.9, + "ultraviolet_index_clear_sky": 0.0, + "wind_from_direction": 184.0, + "wind_speed": 2.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "air_temperature_max": 10.1, + "air_temperature_min": 8.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-10-31T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 992.2, + "air_temperature": 10.1, + "cloud_area_fraction": 56.2, + "cloud_area_fraction_high": 33.6, + "cloud_area_fraction_low": 33.6, + "cloud_area_fraction_medium": 17.2, + "dew_point_temperature": 6.6, + "relative_humidity": 79.6, + "wind_from_direction": 158.7, + "wind_speed": 6.4 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rainshowers_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrainshowers_night" + }, + "details": { + "air_temperature_max": 10.8, + "air_temperature_min": 9.9, + "precipitation_amount": 7.9 + } + } + } + }, + { + "time": "2021-10-31T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 983.7, + "air_temperature": 10.8, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 100.0, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 9.2, + "relative_humidity": 90.3, + "wind_from_direction": 143.5, + "wind_speed": 8.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrainshowers_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrainshowers_day" + }, + "details": { + "air_temperature_max": 10.9, + "air_temperature_min": 9.8, + "precipitation_amount": 5.9 + } + } + } + }, + { + "time": "2021-10-31T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 983.5, + "air_temperature": 10.0, + "cloud_area_fraction": 34.4, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 29.7, + "cloud_area_fraction_medium": 18.7, + "dew_point_temperature": 6.1, + "relative_humidity": 77.6, + "wind_from_direction": 248.8, + "wind_speed": 6.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 10.3, + "air_temperature_min": 8.3, + "precipitation_amount": 0.2 + } + } + } + }, + { + "time": "2021-10-31T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 984.5, + "air_temperature": 8.3, + "cloud_area_fraction": 2.3, + "cloud_area_fraction_high": 1.6, + "cloud_area_fraction_low": 1.6, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 5.0, + "relative_humidity": 79.3, + "wind_from_direction": 221.1, + "wind_speed": 3.1 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "air_temperature_max": 8.3, + "air_temperature_min": 5.3, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-01T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 983.3, + "air_temperature": 5.3, + "cloud_area_fraction": 99.2, + "cloud_area_fraction_high": 97.7, + "cloud_area_fraction_low": 75.8, + "cloud_area_fraction_medium": 82.0, + "dew_point_temperature": 5.0, + "relative_humidity": 97.3, + "wind_from_direction": 286.3, + "wind_speed": 2.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 5.5, + "air_temperature_min": 4.9, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-01T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 985.4, + "air_temperature": 5.4, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 100.0, + "cloud_area_fraction_medium": 33.6, + "dew_point_temperature": 4.9, + "relative_humidity": 96.3, + "wind_from_direction": 269.9, + "wind_speed": 5.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 9.8, + "air_temperature_min": 5.4, + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-11-01T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 989.7, + "air_temperature": 9.9, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 84.4, + "cloud_area_fraction_low": 17.2, + "cloud_area_fraction_medium": 96.9, + "dew_point_temperature": 6.1, + "relative_humidity": 77.8, + "wind_from_direction": 297.2, + "wind_speed": 7.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 10.6, + "air_temperature_min": 9.2, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-01T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 993.7, + "air_temperature": 9.1, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 17.2, + "cloud_area_fraction_medium": 16.4, + "dew_point_temperature": 5.5, + "relative_humidity": 77.6, + "wind_from_direction": 294.1, + "wind_speed": 6.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 9.1, + "air_temperature_min": 7.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 997.0, + "air_temperature": 7.0, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 68.0, + "cloud_area_fraction_low": 3.1, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 5.8, + "relative_humidity": 91.5, + "wind_from_direction": 280.6, + "wind_speed": 6.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "air_temperature_max": 7.0, + "air_temperature_min": 5.8, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 999.2, + "air_temperature": 5.8, + "cloud_area_fraction": 25.0, + "cloud_area_fraction_high": 15.6, + "cloud_area_fraction_low": 8.6, + "cloud_area_fraction_medium": 1.6, + "dew_point_temperature": 5.2, + "relative_humidity": 95.7, + "wind_from_direction": 274.9, + "wind_speed": 5.5 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 8.8, + "air_temperature_min": 5.7, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1001.4, + "air_temperature": 8.9, + "cloud_area_fraction": 22.7, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 22.7, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 4.6, + "relative_humidity": 74.1, + "wind_from_direction": 291.2, + "wind_speed": 7.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 9.7, + "air_temperature_min": 7.9, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-02T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1002.5, + "air_temperature": 7.9, + "cloud_area_fraction": 51.6, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 50.8, + "cloud_area_fraction_medium": 7.0, + "dew_point_temperature": 4.7, + "relative_humidity": 79.5, + "wind_from_direction": 283.1, + "wind_speed": 7.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "lightrainshowers_night" + }, + "details": { + "air_temperature_max": 8.0, + "air_temperature_min": 7.4, + "precipitation_amount": 0.9 + } + } + } + }, + { + "time": "2021-11-03T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1005.4, + "air_temperature": 8.0, + "cloud_area_fraction": 86.7, + "cloud_area_fraction_high": 10.2, + "cloud_area_fraction_low": 35.9, + "cloud_area_fraction_medium": 77.3, + "dew_point_temperature": 5.4, + "relative_humidity": 83.1, + "wind_from_direction": 299.1, + "wind_speed": 7.9 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_night" + }, + "details": { + "air_temperature_max": 8.0, + "air_temperature_min": 6.7, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-03T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1010.6, + "air_temperature": 6.7, + "cloud_area_fraction": 7.8, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 7.8, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 3.5, + "relative_humidity": 80.2, + "wind_from_direction": 309.5, + "wind_speed": 7.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_day" + }, + "details": { + "air_temperature_max": 8.6, + "air_temperature_min": 6.3, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-03T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1015.2, + "air_temperature": 8.6, + "cloud_area_fraction": 68.0, + "cloud_area_fraction_high": 56.2, + "cloud_area_fraction_low": 19.5, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 3.3, + "relative_humidity": 69.6, + "wind_from_direction": 326.0, + "wind_speed": 6.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "fair_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 9.2, + "air_temperature_min": 7.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-03T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1018.0, + "air_temperature": 6.9, + "cloud_area_fraction": 26.6, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 26.6, + "cloud_area_fraction_medium": 2.3, + "dew_point_temperature": 3.6, + "relative_humidity": 79.4, + "wind_from_direction": 304.5, + "wind_speed": 4.0 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "clearsky_night" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "fair_night" + }, + "details": { + "air_temperature_max": 6.9, + "air_temperature_min": 4.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1020.0, + "air_temperature": 3.9, + "cloud_area_fraction": 0.8, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 0.8, + "cloud_area_fraction_medium": 0.0, + "dew_point_temperature": 3.2, + "relative_humidity": 95.6, + "wind_from_direction": 273.0, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "clearsky_night" + }, + "details": { + "air_temperature_max": 5.1, + "air_temperature_min": 3.8, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1018.5, + "air_temperature": 5.2, + "cloud_area_fraction": 8.6, + "cloud_area_fraction_high": 3.1, + "cloud_area_fraction_low": 4.7, + "cloud_area_fraction_medium": 2.3, + "dew_point_temperature": 3.1, + "relative_humidity": 87.0, + "wind_from_direction": 242.6, + "wind_speed": 4.2 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "partlycloudy_day" + }, + "details": { + "air_temperature_max": 9.0, + "air_temperature_min": 5.2, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.9, + "air_temperature": 9.0, + "cloud_area_fraction": 96.1, + "cloud_area_fraction_high": 53.1, + "cloud_area_fraction_low": 29.7, + "cloud_area_fraction_medium": 82.8, + "dew_point_temperature": 6.2, + "relative_humidity": 82.7, + "wind_from_direction": 248.6, + "wind_speed": 6.6 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 11.0, + "air_temperature_min": 9.0, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-04T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.0, + "air_temperature": 10.8, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 0.0, + "cloud_area_fraction_low": 100.0, + "cloud_area_fraction_medium": 98.4, + "dew_point_temperature": 8.2, + "relative_humidity": 84.4, + "wind_from_direction": 249.6, + "wind_speed": 5.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 10.8, + "air_temperature_min": 10.5, + "precipitation_amount": 0.1 + } + } + } + }, + { + "time": "2021-11-05T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.2, + "air_temperature": 10.5, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 7.0, + "cloud_area_fraction_low": 100.0, + "cloud_area_fraction_medium": 29.7, + "dew_point_temperature": 8.7, + "relative_humidity": 89.2, + "wind_from_direction": 249.6, + "wind_speed": 4.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "cloudy" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 10.5, + "air_temperature_min": 10.0, + "precipitation_amount": 0.3 + } + } + } + }, + { + "time": "2021-11-05T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1016.1, + "air_temperature": 10.0, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 88.3, + "cloud_area_fraction_medium": 11.7, + "dew_point_temperature": 8.3, + "relative_humidity": 90.5, + "wind_from_direction": 242.7, + "wind_speed": 4.3 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 10.4, + "air_temperature_min": 9.5, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-05T12:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1013.5, + "air_temperature": 10.4, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 11.7, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 7.7, + "relative_humidity": 84.4, + "wind_from_direction": 180.1, + "wind_speed": 3.8 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "rain" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "heavyrain" + }, + "details": { + "air_temperature_max": 11.9, + "air_temperature_min": 10.4, + "precipitation_amount": 9.1 + } + } + } + }, + { + "time": "2021-11-05T18:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1001.3, + "air_temperature": 11.9, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 97.7, + "cloud_area_fraction_medium": 100.0, + "dew_point_temperature": 9.5, + "relative_humidity": 84.6, + "wind_from_direction": 191.8, + "wind_speed": 8.7 + } + }, + "next_12_hours": { + "summary": { + "symbol_code": "lightrain" + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "rain" + }, + "details": { + "air_temperature_max": 14.4, + "air_temperature_min": 11.9, + "precipitation_amount": 4.7 + } + } + } + }, + { + "time": "2021-11-06T00:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 997.7, + "air_temperature": 14.2, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 99.2, + "cloud_area_fraction_medium": 4.7, + "dew_point_temperature": 12.2, + "relative_humidity": 88.2, + "wind_from_direction": 255.3, + "wind_speed": 8.8 + } + }, + "next_6_hours": { + "summary": { + "symbol_code": "cloudy" + }, + "details": { + "air_temperature_max": 14.2, + "air_temperature_min": 10.5, + "precipitation_amount": 0.0 + } + } + } + }, + { + "time": "2021-11-06T06:00:00Z", + "data": { + "instant": { + "details": { + "air_pressure_at_sea_level": 1002.5, + "air_temperature": 10.5, + "cloud_area_fraction": 100.0, + "cloud_area_fraction_high": 100.0, + "cloud_area_fraction_low": 21.9, + "cloud_area_fraction_medium": 3.1, + "dew_point_temperature": 8.5, + "relative_humidity": 87.9, + "wind_from_direction": 260.1, + "wind_speed": 4.8 + } + } + } + } + ] + } +} \ No newline at end of file diff --git a/testdata/response-geoname-multiple-02.json b/testdata/response-geoname-multiple-02.json new file mode 100644 index 0000000..2c12252 --- /dev/null +++ b/testdata/response-geoname-multiple-02.json @@ -0,0 +1 @@ +{"postalCodes":[{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.1210823059082,"countryCode":"IT","postalCode":"38100","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Trento","lat":46.0678714011874},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.04053,"countryCode":"IT","postalCode":"38068","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Rovereto","lat":45.8904},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.841166973114014,"countryCode":"IT","postalCode":"38066","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Riva Del Garda","lat":45.88576635570338},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.726788814769579,"countryCode":"IT","postalCode":"38079","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Tione Di Trento","lat":46.035497796159675},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.89106,"countryCode":"IT","postalCode":"38069","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Torbole","lat":45.87594},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.032761834383843,"countryCode":"IT","postalCode":"38023","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Cles","lat":46.36294231432275},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.599595740840952,"countryCode":"IT","postalCode":"38037","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Predazzo","lat":46.31140072616998},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.88672161102295,"countryCode":"IT","postalCode":"38062","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Arco","lat":45.917721261594224},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.004321464107697,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Andalo","lat":46.16649169333167},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.109313518335805,"countryCode":"IT","postalCode":"38015","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Lavis","lat":46.14130607650874},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.963002847091856,"countryCode":"IT","postalCode":"38018","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Molveno","lat":46.142380392892676},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.912464512370285,"countryCode":"IT","postalCode":"38027","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Male'","lat":46.353564221896534},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.769288033585699,"countryCode":"IT","postalCode":"38032","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Canazei","lat":46.476274688266884},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.458624916630892,"countryCode":"IT","postalCode":"38033","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Cavalese","lat":46.29047922207462},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.659412112212882,"countryCode":"IT","postalCode":"38035","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Moena","lat":46.376545567146955},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.457564531035661,"countryCode":"IT","postalCode":"38051","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Borgo","lat":46.05118958806449},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.304270447916947,"countryCode":"IT","postalCode":"38056","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Levico Terme","lat":46.01216635399339},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.674181793539258,"countryCode":"IT","postalCode":"38039","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Vigo Di Fassa","lat":46.41897856140535},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.237577456652453,"countryCode":"IT","postalCode":"38057","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Pergine Valsugana","lat":46.064336496441555},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.00458006809729,"countryCode":"IT","postalCode":"38061","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Ala","lat":45.76072450814006},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.763762242707573,"countryCode":"IT","postalCode":"38086","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Pinzolo","lat":46.159762392608975},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.73937,"countryCode":"IT","postalCode":"38088","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Spiazzo","lat":46.103601},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.168202279895429,"countryCode":"IT","postalCode":"38064","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Folgaria","lat":45.915432005689404},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.980516264270328,"countryCode":"IT","postalCode":"38065","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Mori","lat":45.85187056760624},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.912014276751814,"countryCode":"IT","postalCode":"38074","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Dro","lat":45.961209053659836},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.580220222473145,"countryCode":"IT","postalCode":"38089","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Storo","lat":45.84924956447676},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.137232084095917,"countryCode":"IT","postalCode":"38013","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Fondo","lat":46.438795214182406},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.122531194491703,"countryCode":"IT","postalCode":"38016","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Mezzocorona","lat":46.21158649056654},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.800067,"countryCode":"IT","postalCode":"38020","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Mezzana","lat":46.316707},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.673533,"countryCode":"IT","postalCode":"38024","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Peio","lat":46.36278},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.692817586310149,"countryCode":"IT","postalCode":"38024","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Cogolo","lat":46.352524289697826},{"adminCode2":"TN","adminCode3":"022233","adminName3":"Dimaro Folgarida","adminCode1":"17","adminName2":"Trento","lng":10.873415931132008,"countryCode":"IT","postalCode":"38025","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Dimaro","lat":46.32572860981403},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.741097164916356,"countryCode":"IT","postalCode":"38031","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Campitello Di Fassa","lat":46.475785072579754},{"adminCode2":"TN","adminCode3":"022241","adminName3":"Cembra Lisignago","adminCode1":"17","adminName2":"Trento","lng":11.221738027261605,"countryCode":"IT","postalCode":"38034","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Cembra","lat":46.17489332174378},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.687112318563669,"countryCode":"IT","postalCode":"38036","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Pozza Di Fassa","lat":46.428057588522286},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.509459696180498,"countryCode":"IT","postalCode":"38038","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Tesero","lat":46.291840805477406},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.194592928218619,"countryCode":"IT","postalCode":"38041","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Albiano","lat":46.14450792463351},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.457564531035661,"countryCode":"IT","postalCode":"38051","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Borgo Valsugana","lat":46.05118958806449},{"adminCode2":"TN","adminCode3":"022245","adminName3":"Primiero San Martino Di Castrozza","adminCode1":"17","adminName2":"Trento","lng":11.839394,"countryCode":"IT","postalCode":"38054","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Tonadico","lat":46.181113},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.635633011364993,"countryCode":"IT","postalCode":"38055","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Grigno","lat":46.01568645258471},{"adminCode2":"TN","adminCode3":"022248","adminName3":"Vallelaghi","adminCode1":"17","adminName2":"Trento","lng":10.997345886437923,"countryCode":"IT","postalCode":"38096","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Vezzano","lat":46.07867304099094},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.069501028281511,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Fai Della Paganella","lat":46.17801447440701},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.09635988132266,"countryCode":"IT","postalCode":"38017","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Mezzolombardo","lat":46.20773584163479},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.246480941772461,"countryCode":"IT","postalCode":"38042","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Baselga Di Pine'","lat":46.13250462971521},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.243683,"countryCode":"IT","postalCode":"38050","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Calceranica Al Lago","lat":46.004603},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.632466131434317,"countryCode":"IT","postalCode":"38053","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Castello Tesino","lat":46.06301996219218},{"adminCode2":"TN","adminCode3":"022245","adminName3":"Primiero San Martino Di Castrozza","adminCode1":"17","adminName2":"Trento","lng":11.832788358411312,"countryCode":"IT","postalCode":"38054","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Transacqua","lat":46.17366774414386},{"adminCode2":"TN","adminCode3":"022245","adminName3":"Primiero San Martino Di Castrozza","adminCode1":"17","adminName2":"Trento","lng":11.828794,"countryCode":"IT","postalCode":"38054","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Fiera Di Primiero","lat":46.176212},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.95508,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Brentonico","lat":45.819096},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.063508582312211,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Volano","lat":45.91718017944707},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.02388,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Nogaredo","lat":45.912999},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.109080998805426,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Besenello","lat":45.94355528399251},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.000588527708171,"countryCode":"IT","postalCode":"38061","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Pilcante","lat":45.77074844463735},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.939380422524657,"countryCode":"IT","postalCode":"38063","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Avio","lat":45.733961949145296},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.758869,"countryCode":"IT","postalCode":"38080","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Carisolo","lat":46.168803},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.76777,"countryCode":"IT","postalCode":"38086","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Giustino","lat":46.151302},{"adminCode2":"TN","adminCode3":"022248","adminName3":"Vallelaghi","adminCode1":"17","adminName2":"Trento","lng":11.04504510440527,"countryCode":"IT","postalCode":"38096","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Terlago","lat":46.09737070187929},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.1550283432007,"countryCode":"IT","postalCode":"38100","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Povo","lat":46.0669781465587},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.177774,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Ruffre'","lat":46.414813},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.161278,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Faedo","lat":46.192407},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.938567750005088,"countryCode":"IT","postalCode":"38020","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Rabbi","lat":46.38356119424066},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.839568,"countryCode":"IT","postalCode":"38020","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Commezzadura","lat":46.321707},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.01857,"countryCode":"IT","postalCode":"38020","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Rumo","lat":46.441412},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.018802662045728,"countryCode":"IT","postalCode":"38020","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Marcena","lat":46.441207195930666},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.737566,"countryCode":"IT","postalCode":"38026","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Ossana","lat":46.306506},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.731683425861183,"countryCode":"IT","postalCode":"38026","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Fucine","lat":46.311398969856015},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.925869,"countryCode":"IT","postalCode":"38027","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Terzolas","lat":46.361109},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.4172,"countryCode":"IT","postalCode":"38030","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Molina","lat":46.27208},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.458283,"countryCode":"IT","postalCode":"38030","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Varena","lat":46.306713},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.439782,"countryCode":"IT","postalCode":"38033","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Carano","lat":46.291512},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.112151953475646,"countryCode":"IT","postalCode":"38040","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Ravina","lat":46.03944036017364},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.207513809204102,"countryCode":"IT","postalCode":"38040","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Fornace","lat":46.11804907826783},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.25988,"countryCode":"IT","postalCode":"38047","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Segonzano","lat":46.190208},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.264283,"countryCode":"IT","postalCode":"38050","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Tenna","lat":46.015703},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.408238356117364,"countryCode":"IT","postalCode":"38050","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Roncegno","lat":46.049335813618335},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.505688,"countryCode":"IT","postalCode":"38050","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Scurelle","lat":46.064507},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.789193,"countryCode":"IT","postalCode":"38050","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Imer","lat":46.149311},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.96564824166577,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Ronzo","lat":45.88212540554558},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.00918,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Isera","lat":45.887598},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.117884,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Vallarsa","lat":45.782796},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.89106,"countryCode":"IT","postalCode":"38069","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Nago","lat":45.87594},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.89106,"countryCode":"IT","postalCode":"38069","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Nago Torbole","lat":45.87594},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.731239318847656,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Pieve Di Ledro","lat":45.88848473149638},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.092756984434942,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Aldeno","lat":45.97758446593058},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.071976558615326,"countryCode":"IT","postalCode":"38060","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Nomi","lat":45.929232486290694},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.84225058555603,"countryCode":"IT","postalCode":"38075","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Fiave'","lat":46.00457089896917},{"adminCode2":"TN","adminCode3":"022238","adminName3":"Borgo Chiese","adminCode1":"17","adminName2":"Trento","lng":10.593659983393366,"countryCode":"IT","postalCode":"38083","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Condino","lat":45.88166625058836},{"adminCode2":"TN","adminCode3":"022246","adminName3":"Sella Giudicarie","adminCode1":"17","adminName2":"Trento","lng":10.668171,"countryCode":"IT","postalCode":"38087","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Roncone","lat":45.982697},{"adminCode2":"TN","adminCode3":"022248","adminName3":"Vallelaghi","adminCode1":"17","adminName2":"Trento","lng":10.984781329841269,"countryCode":"IT","postalCode":"38096","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Padergnone","lat":46.059815889953576},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.119197938209247,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Romeno","lat":46.39451704006989},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.049125013450833,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Denno","lat":46.27424112753419},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.03277345221915,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Cavedago","lat":46.18485837619542},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.152749217616556,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Ronzone","lat":46.424369337920005},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.048051112877742,"countryCode":"IT","postalCode":"38010","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Spormaggiore","lat":46.21852625382254},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.139338066839956,"countryCode":"IT","postalCode":"38011","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Cavareno","lat":46.40780298527367},{"adminCode2":"TN","adminCode3":"022230","adminName3":"Predaia","adminCode1":"17","adminName2":"Trento","lng":11.109774,"countryCode":"IT","postalCode":"38012","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Smarano","lat":46.34311},{"adminCode2":"TN","adminCode3":"022230","adminName3":"Predaia","adminCode1":"17","adminName2":"Trento","lng":11.0900115966797,"countryCode":"IT","postalCode":"38012","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Coredo","lat":46.34906049944},{"adminCode2":"TN","adminCode3":"022249","adminName3":"Ville d’Anaunia","adminCode1":"17","adminName2":"Trento","lng":11.023063659667969,"countryCode":"IT","postalCode":"38019","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Tuenno","lat":46.3284390873061},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":10.757899033072906,"countryCode":"IT","postalCode":"38020","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Pellizzano","lat":46.30979856892243},{"adminCode2":"TN","adminCode1":"17","adminName2":"Trento","lng":11.105782562087922,"countryCode":"IT","postalCode":"38021","adminName1":"Trentino-Alto Adige","ISO3166-2":"32","placeName":"Brez","lat":46.43220436526391}]} \ No newline at end of file diff --git a/testdata/response-geoname-multiple.json b/testdata/response-geoname-multiple.json new file mode 100644 index 0000000..2fa72a0 --- /dev/null +++ b/testdata/response-geoname-multiple.json @@ -0,0 +1 @@ +{"postalCodes":[{"adminCode1":"L","lng":-6.254537,"countryCode":"IE","postalCode":"D01","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 1","lat":53.353976},{"adminCode1":"L","lng":-6.254295,"countryCode":"IE","postalCode":"D02","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 2","lat":53.339971},{"adminCode1":"L","lng":-6.23776,"countryCode":"IE","postalCode":"D03","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 3","lat":53.364465},{"adminCode1":"L","lng":-6.233526,"countryCode":"IE","postalCode":"D04","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 4","lat":53.333435},{"adminCode1":"L","lng":-6.192128,"countryCode":"IE","postalCode":"D05","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 5","lat":53.384222},{"adminCode1":"L","lng":-6.263126,"countryCode":"IE","postalCode":"D06","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 6","lat":53.308787},{"adminCode1":"L","lng":-6.291792,"countryCode":"IE","postalCode":"D07","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 7","lat":53.361507},{"adminCode1":"L","lng":-6.273257,"countryCode":"IE","postalCode":"D08","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 8","lat":53.33455},{"adminCode1":"L","lng":-6.246501,"countryCode":"IE","postalCode":"D09","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 9","lat":53.381763},{"adminCode1":"L","lng":-6.354476,"countryCode":"IE","postalCode":"D10","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 10","lat":53.340906},{"adminCode1":"L","lng":-6.292976,"countryCode":"IE","postalCode":"D11","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 11","lat":53.389903},{"adminCode1":"L","lng":-6.316477,"countryCode":"IE","postalCode":"D12","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 12","lat":53.32203},{"adminCode1":"L","lng":-6.1495,"countryCode":"IE","postalCode":"D13","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 13","lat":53.394577},{"adminCode1":"L","lng":-6.259331,"countryCode":"IE","postalCode":"D14","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 14","lat":53.295987},{"adminCode1":"L","lng":-6.416518,"countryCode":"IE","postalCode":"D15","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 15","lat":53.383156},{"adminCode1":"L","lng":-6.278967,"countryCode":"IE","postalCode":"D16","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 16","lat":53.341884},{"adminCode1":"L","lng":-6.205763,"countryCode":"IE","postalCode":"D17","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 17","lat":53.400646},{"adminCode1":"L","lng":-6.177386,"countryCode":"IE","postalCode":"D18","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 18","lat":53.246902},{"adminCode1":"L","lng":-6.369332,"countryCode":"IE","postalCode":"D20","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 20","lat":53.35177},{"adminCode1":"L","lng":-6.400591,"countryCode":"IE","postalCode":"D22","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 22","lat":53.32751},{"adminCode1":"L","lng":-6.371327,"countryCode":"IE","postalCode":"D24","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 24","lat":53.285119},{"adminCode1":"L","lng":-6.30119,"countryCode":"IE","postalCode":"D6W","adminName1":"Leinster","ISO3166-2":"L","placeName":"Dublin 6W","lat":53.308651}]} \ No newline at end of file diff --git a/testdata/response-geoname-single.json b/testdata/response-geoname-single.json new file mode 100644 index 0000000..e7623dc --- /dev/null +++ b/testdata/response-geoname-single.json @@ -0,0 +1,14 @@ +{ + "postalCodes": [ + { + "adminCode1": "C", + "lng": -9.3, + "countryCode": "IE", + "postalCode": "F23", + "adminName1": "Connacht", + "ISO3166-2": "C", + "placeName": "Castlebar", + "lat": 53.85 + } + ] +} \ No newline at end of file diff --git a/testdata/response-geoname-wikipedia.json b/testdata/response-geoname-wikipedia.json new file mode 100644 index 0000000..31cef77 --- /dev/null +++ b/testdata/response-geoname-wikipedia.json @@ -0,0 +1,125 @@ +{ + "geonames": [ + { + "summary": "Castlebar is the county town of County Mayo, Ireland. It is in the middle of the county and is its largest town by population. A campus of Galway-Mayo Institute of Technology and the Country Life section of the National Museum of Ireland are two important local amenities (...)", + "elevation": 41, + "geoNameId": 2965654, + "lng": -9.2988, + "countryCode": "IE", + "rank": 100, + "lang": "en", + "title": "Castlebar", + "lat": 53.8608, + "wikipediaUrl": "en.wikipedia.org/wiki/Castlebar" + }, + { + "summary": "County Mayo (meaning \"Plain of the yew trees\") is a county in Ireland. In the West of Ireland, it is part of the province of Connacht and is named after the village of Mayo, now generally known as Mayo Abbey. Mayo County Council is the local authority for the county (...)", + "elevation": 294, + "feature": "adm1st", + "lng": -9.36, + "countryCode": "IE", + "rank": 100, + "lang": "en", + "title": "County Mayo", + "lat": 53.92, + "wikipediaUrl": "en.wikipedia.org/wiki/County_Mayo" + }, + { + "summary": "Westport (historically anglicised as Cahernamart) (see archival records) is a town in County Mayo in Ireland. It is at the south-east corner of Clew Bay, an inlet of the Atlantic Ocean on the west coast of Ireland (...)", + "elevation": 19, + "geoNameId": 2960970, + "feature": "city", + "lng": -9.5225, + "countryCode": "IE", + "rank": 98, + "thumbnailImg": "http://www.geonames.org/img/wikipedia/92000/thumb-91512-100.jpg", + "lang": "en", + "title": "Westport, County Mayo", + "lat": 53.799166666666665, + "wikipediaUrl": "en.wikipedia.org/wiki/Westport%2C_County_Mayo" + }, + { + "summary": "Clew Bay is a natural ocean bay in County Mayo, Republic of Ireland. It contains Ireland's best example of sunken drumlins. The bay is overlooked by Croagh Patrick to the south and the Nephin Range mountains of North Mayo. Clare Island guards the entrance of the bay (...)", + "geoNameId": 2965450, + "feature": "waterbody", + "lng": -9.8, + "rank": 93, + "thumbnailImg": "http://www.geonames.org/img/wikipedia/52000/thumb-51626-100.jpg", + "lang": "en", + "title": "Clew Bay", + "lat": 53.833333333333336, + "wikipediaUrl": "en.wikipedia.org/wiki/Clew_Bay" + }, + { + "summary": "The Battle of Castlebar occurred on 27 August 1798 near the town of Castlebar, County Mayo, during the Irish Rebellion of that year. A combined force of 2,000 French troops and Irish rebels routed a force of 6,000 British militia in what would later become known as the \"Castlebar Races\" or \"Races of (...)", + "elevation": 41, + "lng": -9.2989, + "countryCode": "IE", + "rank": 82, + "lang": "en", + "title": "Battle of Castlebar", + "lat": 53.8608, + "wikipediaUrl": "en.wikipedia.org/wiki/Battle_of_Castlebar" + }, + { + "summary": "Ealing is a major suburban district of west London, England and the administrative centre of the London Borough of Ealing. It is located west of Charing Cross and around from the City of London. It is one of the major metropolitan centres identified in the London Plan (...)", + "elevation": 35, + "geoNameId": 2650567, + "feature": "city", + "lng": -0.3058, + "countryCode": "GB", + "rank": 100, + "lang": "en", + "title": "Ealing", + "lat": 51.5111, + "wikipediaUrl": "en.wikipedia.org/wiki/Ealing" + }, + { + "summary": "Connacht or Connaught (or Cúige Chonnacht) is one of the Provinces of Ireland situated in the west of the country. Up to the 9th century it consisted of several independent major kingdoms (Lúighne, Uí Maine, Iarthar Connacht) (...)", + "elevation": 66, + "feature": "adm1st", + "lng": -9.05, + "countryCode": "IE", + "rank": 100, + "lang": "en", + "title": "Connacht", + "lat": 53.78, + "wikipediaUrl": "en.wikipedia.org/wiki/Connacht" + }, + { + "summary": "Ballina is a town in north County Mayo, Ireland. It lies at the mouth of the River Moy near Killala Bay, in the Moy valley and Parish of Kilmoremoy, with the Ox Mountains to the east and the Nephin Beg mountains to the west (...)", + "elevation": 32, + "geoNameId": 2966778, + "lng": -9.1667, + "countryCode": "IE", + "rank": 98, + "lang": "en", + "title": "Ballina, County Mayo", + "lat": 54.1167, + "wikipediaUrl": "en.wikipedia.org/wiki/Ballina%2C_County_Mayo" + }, + { + "summary": "Longford is the county town of County Longford in Ireland. It has a population of 9,601 according to the 2011 census. It is the biggest town in the county and about one third of the county's population lives there (...)", + "elevation": 53, + "geoNameId": 2962840, + "lng": -7.7998, + "countryCode": "IE", + "rank": 100, + "lang": "en", + "title": "Longford", + "lat": 53.727, + "wikipediaUrl": "en.wikipedia.org/wiki/Longford" + }, + { + "summary": "Castlebar railway station serves the town of Castlebar in County Mayo, Ireland. The station is on the Dublin to Westport Rail service. Passengers to or from Galway travel to Athlone and change trains. Passengers to or from Ballina and Foxford travel to Manulla Junction and change trains. (...)", + "elevation": 42, + "feature": "railwaystation", + "lng": -9.288214, + "rank": 75, + "lang": "en", + "title": "Castlebar railway station", + "lat": 53.8473045, + "wikipediaUrl": "en.wikipedia.org/wiki/Castlebar_railway_station" + } + ] +} \ No newline at end of file diff --git a/wikipedia.go b/wikipedia.go new file mode 100644 index 0000000..368fb96 --- /dev/null +++ b/wikipedia.go @@ -0,0 +1,125 @@ +package meteo + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "strconv" + "time" +) + +const ( + maxRawResults = 10 +) + +type wikipediaPlaces struct { + Geonames []struct { + Lat float64 `json:"lat"` + Lng float64 `json:"lng"` + CountryCode string `json:"countryCode"` + Title string `json:"title"` + } `json:"geonames"` +} + +type wikipediaOption func(wk *WikipediaClient) error + +// WithWikiUserAgent returns a func option for +// setting up a custom User-Agent used in +// http requests. +func WithWikiUserAgent(ua string) wikipediaOption { + return func(wk *WikipediaClient) error { + wk.UA = ua + return nil + } +} + +// WithWikiHTTPClient returns a func option for +// configuring a custom http client. +func WithWikiHTTPClient(hc *http.Client) wikipediaOption { + return func(wk *WikipediaClient) error { + wk.HTTPClient = hc + return nil + } +} + +// WikipediaClient implements Nameresolver interface. +type WikipediaClient struct { + UA string + UserName string + BaseURL string + HTTPClient *http.Client +} + +// NewWikipediaClient knows how to create a new client. +// The client impements NameResolver interface. +func NewWikipediaClient(username string) (*WikipediaClient, error) { + if username == "" { + return nil, errors.New("nil username") + } + c := WikipediaClient{ + UA: userAgent, + UserName: username, + BaseURL: baseURLGeoNames, + HTTPClient: &http.Client{ + Timeout: 5 * time.Second, + }, + } + return &c, nil +} + +// GetCoordinates knows how to retrive geo coordinates for +// the given place name and country code. +func (w WikipediaClient) GetCoordinates(placeName, country string) (Place, error) { + u, err := w.makeURL(placeName) + if err != nil { + return Place{}, err + } + req, err := prepareRequest(u) + if err != nil { + return Place{}, err + } + res, err := w.HTTPClient.Do(req) + if err != nil { + return Place{}, err + } + defer res.Body.Close() + + var wp wikipediaPlaces + if err := json.NewDecoder(res.Body).Decode(&wp); err != nil { + return Place{}, fmt.Errorf("decoding response %w", err) + + } + if len(wp.Geonames) < 1 { + return Place{}, fmt.Errorf("place %s in country %s not found", placeName, country) + } + return lookupPlace(wp, placeName, country), nil +} + +func (w WikipediaClient) makeURL(placeName string) (string, error) { + base, err := url.Parse(w.BaseURL + "/wikipediaSearchJSON") + if err != nil { + return "", err + } + params := url.Values{} + params.Add("q", placeName) + params.Add("maxRows", strconv.Itoa(maxRawResults)) + params.Add("username", w.UserName) + base.RawQuery = params.Encode() + return base.String(), nil +} + +func lookupPlace(places wikipediaPlaces, placeName, country string) Place { + for _, pl := range places.Geonames { + if pl.Title == placeName && pl.CountryCode == country { + return Place{ + Lat: pl.Lat, + Lng: pl.Lng, + CountryCode: pl.CountryCode, + PlaceName: pl.Title, + } + } + } + return Place{} +} diff --git a/wikipedia_test.go b/wikipedia_test.go new file mode 100644 index 0000000..fc4ee9e --- /dev/null +++ b/wikipedia_test.go @@ -0,0 +1,85 @@ +package meteo_test + +import ( + "io" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/qba73/meteo" +) + +func TestNewWikipediaClient(t *testing.T) { + t.Parallel() + var err error + var c *meteo.WikipediaClient + c, err = meteo.NewWikipediaClient("UserName") + if err != nil { + t.Fatal(err) + } + _ = c +} + +func TestNewWikipediaClientWithoutUserName(t *testing.T) { + t.Parallel() + _, err := meteo.NewWikipediaClient("") + if err == nil { + t.Fatal(err) + } +} + +func TestNewWikipediaClientWithUserName(t *testing.T) { + t.Parallel() + got, err := meteo.NewWikipediaClient("UserName") + if err != nil { + t.Fatal(err) + } + want := "UserName" + if want != got.UserName { + t.Errorf("want %s, got %s", want, got.UserName) + } +} + +func TestGetCoordinatesSingleGeoName(t *testing.T) { + t.Parallel() + + testFile := "testdata/response-geoname-wikipedia.json" + + ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + wantReqURL := "/wikipediaSearchJSON?maxRows=10&q=Castlebar&username=UserName" + gotReqURL := r.RequestURI + if wantReqURL != gotReqURL { + t.Errorf("want %q for wikipedia URL, got %q", wantReqURL, gotReqURL) + } + f, err := os.Open(testFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + io.Copy(rw, f) + })) + defer ts.Close() + + c, err := meteo.NewWikipediaClient("UserName") + if err != nil { + t.Fatal(err) + } + c.BaseURL = ts.URL + + got, err := c.GetCoordinates("Castlebar", "IE") + if err != nil { + t.Fatalf("GetCoordinates('Castlebar', 'IE') got err %v", err) + } + want := meteo.Place{ + Lng: -9.2988, + Lat: 53.8608, + PlaceName: "Castlebar", + CountryCode: "IE", + } + + if !cmp.Equal(want, got) { + t.Errorf("GetCoordinates('Castlebar', 'IE') \n%s", cmp.Diff(want, got)) + } +}