api

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 16, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package api provides a client for the KMI/IRM weather API.

The client handles authentication, rate limiting, retries, and error handling. It provides methods to fetch weather forecasts and radar data for Belgium and Netherlands.

Basic usage:

client := api.NewClient(
	api.WithLanguage("en"),
	api.WithVerbose(true),
)
forecast, err := client.GetForecast(ctx, 50.8503, 4.3517)

Index

Constants

This section is empty.

Variables

View Source
var WarningTypeToSlug = map[string]string{
	"0":  "wind",
	"1":  "rain",
	"2":  "ice_or_snow",
	"3":  "thunder",
	"7":  "fog",
	"9":  "cold",
	"10": "heat",
	"12": "thunder_wind_rain",
	"13": "thunderstorm_strong_gusts",
	"14": "thunderstorm_large_rainfall",
	"15": "storm_surge",
	"17": "coldspell",
}

WarningTypeToSlug maps warning type IDs to slug names

View Source
var WeatherConditions = map[int]*WeatherCondition{
	0:  {Code: 0, DayIcon: "sunny", NightIcon: "clear_night", Condition: "Clear"},
	1:  {Code: 1, DayIcon: "sunny", NightIcon: "clear_night", Condition: "Clear"},
	2:  {Code: 2, DayIcon: "lightning_rainy", NightIcon: "lightning_rainy", Condition: "Thunderstorm"},
	3:  {Code: 3, DayIcon: "partlycloudy", NightIcon: "partlycloudy", Condition: "Partly cloudy"},
	4:  {Code: 4, DayIcon: "pouring", NightIcon: "pouring", Condition: "Heavy rain"},
	5:  {Code: 5, DayIcon: "lightning_rainy", NightIcon: "lightning_rainy", Condition: "Thunderstorm"},
	6:  {Code: 6, DayIcon: "pouring", NightIcon: "pouring", Condition: "Heavy rain"},
	7:  {Code: 7, DayIcon: "lightning_rainy", NightIcon: "lightning_rainy", Condition: "Thunderstorm"},
	8:  {Code: 8, DayIcon: "snowy_rainy", NightIcon: "snowy_rainy", Condition: "Sleet"},
	9:  {Code: 9, DayIcon: "snowy_rainy", NightIcon: "snowy_rainy", Condition: "Sleet"},
	10: {Code: 10, DayIcon: "lightning_rainy", NightIcon: "lightning_rainy", Condition: "Thunderstorm"},
	11: {Code: 11, DayIcon: "snowy", NightIcon: "snowy", Condition: "Snow"},
	12: {Code: 12, DayIcon: "snowy", NightIcon: "snowy", Condition: "Snow"},
	13: {Code: 13, DayIcon: "lightning_rainy", NightIcon: "lightning_rainy", Condition: "Thunderstorm"},
	14: {Code: 14, DayIcon: "cloudy", NightIcon: "cloudy", Condition: "Cloudy"},
	15: {Code: 15, DayIcon: "cloudy", NightIcon: "cloudy", Condition: "Cloudy"},
	16: {Code: 16, DayIcon: "pouring", NightIcon: "pouring", Condition: "Heavy rain"},
	17: {Code: 17, DayIcon: "lightning_rainy", NightIcon: "lightning_rainy", Condition: "Thunderstorm"},
	18: {Code: 18, DayIcon: "rainy", NightIcon: "rainy", Condition: "Rain"},
	19: {Code: 19, DayIcon: "pouring", NightIcon: "pouring", Condition: "Heavy rain"},
	20: {Code: 20, DayIcon: "snowy_rainy", NightIcon: "snowy_rainy", Condition: "Sleet"},
	21: {Code: 21, DayIcon: "rainy", NightIcon: "rainy", Condition: "Rain"},
	22: {Code: 22, DayIcon: "snowy", NightIcon: "snowy", Condition: "Snow"},
	23: {Code: 23, DayIcon: "snowy", NightIcon: "snowy", Condition: "Snow"},
	24: {Code: 24, DayIcon: "fog", NightIcon: "fog", Condition: "Fog"},
	25: {Code: 25, DayIcon: "fog", NightIcon: "fog", Condition: "Fog"},
	26: {Code: 26, DayIcon: "fog", NightIcon: "fog", Condition: "Fog"},
	27: {Code: 27, DayIcon: "fog", NightIcon: "fog", Condition: "Fog"},
}

WeatherConditions maps ww codes to their icon/condition pairs

Functions

func ExitCode

func ExitCode(err error) int

ExitCode extracts the exit code from API error types

func GetWeatherIcon

func GetWeatherIcon(wwCode int, dayNight string) string

GetWeatherIcon returns the appropriate icon based on ww code and day/night

Types

type APIError

type APIError struct {
	StatusCode int
	Message    string
	Err        error
}

APIError wraps HTTP errors from the API

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) ExitCode

func (e *APIError) ExitCode() int

func (*APIError) Unwrap

func (e *APIError) Unwrap() error

type Animation

type Animation struct {
	LocalisationLayer       string            `json:"localisationLayer"`
	LocalisationLayerRatioX float64           `json:"localisationLayerRatioX"`
	LocalisationLayerRatioY float64           `json:"localisationLayerRatioY"`
	Speed                   float64           `json:"speed"` // seconds per frame
	Type                    string            `json:"type"`  // "10min" (BE) or "5min" (NL)
	Unit                    map[string]string `json:"unit"`  // mm/10min or mm/h
	Country                 string            `json:"country"`
	Sequence                []*RadarFrame     `json:"sequence"`
	Threshold               []interface{}     `json:"threshold"` // Usually empty
	SequenceHint            map[string]string `json:"sequenceHint"`
}

Animation contains rain radar animation data

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the KMI API client

func NewClient

func NewClient(opts ...ClientOption) *Client

NewClient creates a new KMI API client

func (*Client) GetForecast

func (c *Client) GetForecast(ctx context.Context, lat, lon float64) (*ForecastResponse, error)

GetForecast fetches weather forecast for coordinates

func (*Client) GetRadar

func (c *Client) GetRadar(ctx context.Context, lat, lon float64) (*Animation, error)

GetRadar fetches radar animation data from forecast

type ClientOption

type ClientOption func(*Client)

ClientOption configures the client

func WithHTTPClient

func WithHTTPClient(client *http.Client) ClientOption

WithHTTPClient sets a custom HTTP client

func WithLanguage

func WithLanguage(lang string) ClientOption

WithLanguage sets the language for API responses (nl, fr, en, de)

func WithNoCache

func WithNoCache(noCache bool) ClientOption

WithNoCache disables ETag caching

func WithNoLimit

func WithNoLimit(noLimit bool) ClientOption

WithNoLimit disables rate limiting

func WithVerbose

func WithVerbose(verbose bool) ClientOption

WithVerbose enables verbose logging

type DailyForecast

type DailyForecast struct {
	DayName        map[string]string `json:"dayName"`
	Period         string            `json:"period"`
	DayNightPeriod string            `json:"day_night"` // "0" night, "1" day
	DayNight       string            `json:"dayNight"`  // "d" or "n"
	Text           map[string]string `json:"text"`
	// Belgium specific - seconds from midnight
	DawnRiseSeconds *string `json:"dawnRiseSeconds"`
	DawnSetSeconds  *string `json:"dawnSetSeconds"`
	// Netherlands specific - ISO timestamp
	Timestamp *string `json:"timestamp"`
	// Temperature
	TempMin *int `json:"tempMin"`
	TempMax *int `json:"tempMax"`
	// Weather conditions
	WW1    int  `json:"ww1"`    // Primary weather code (int)
	WW2    *int `json:"ww2"`    // Secondary weather code
	WwEvol *int `json:"wwevol"` // 0=one_way, 1=two_ways, null=stable
	// Wind - Beaufort scale
	FF1    int               `json:"ff1"`
	FF2    *int              `json:"ff2"`
	FFEvol *int              `json:"ffevol"`
	DD     int               `json:"dd"` // Encoded direction (add 180° for meteorological)
	DDText map[string]string `json:"ddText"`
	// Wind details
	Wind *Wind `json:"wind"`
	// Netherlands specific
	WindSpeedKm *int     `json:"windSpeedKm"`
	UVIndex     *float64 `json:"uvIndex"`
	SunRiseUTC  *int     `json:"sunRiseUtc"` // UTC seconds
	SunSetUTC   *int     `json:"sunSetUtc"`
	SunRise     *int     `json:"sunRise"` // Local seconds
	SunSet      *int     `json:"sunSet"`
	// Precipitation
	PrecipChance   json.Number `json:"precipChance"`   // int in daily, string in hourly - stored as Number
	PrecipQuantity json.Number `json:"precipQuantity"` // Can be string or number
}

DailyForecast represents a daily (or twice-daily) forecast

func (*DailyForecast) ParsePrecipChance

func (d *DailyForecast) ParsePrecipChance() *int

ParsePrecipChance converts PrecipChance to int (handles both int and string)

func (*DailyForecast) ParsePrecipQuantity

func (d *DailyForecast) ParsePrecipQuantity() *float64

ParsePrecipQuantity converts PrecipQuantity to float64

type Forecast

type Forecast struct {
	Daily   []*DailyForecast  `json:"daily"`
	Hourly  []*HourlyForecast `json:"hourly"`
	Warning []*Warning        `json:"warning"`
	Graph   *Graph            `json:"graph"`
}

Forecast contains daily and hourly forecast data plus warnings

type ForecastResponse

type ForecastResponse struct {
	CityName      string       `json:"cityName"`
	Country       string       `json:"country"`
	Obs           *Observation `json:"obs"`
	For           *Forecast    `json:"for"`
	Module        []Module     `json:"module"`
	Animation     *Animation   `json:"animation"`
	TodayObsCount int          `json:"todayObsCount"`
}

ForecastResponse is the top-level response from getForecasts endpoint

type Graph

type Graph struct {
	SVG []*SVGChart `json:"svg"`
}

Graph contains forecast chart URLs

type HourlyForecast

type HourlyForecast struct {
	Hour              string            `json:"hour"` // Local time hour as string
	Temp              int               `json:"temp"`
	WW                json.Number       `json:"ww"`             // Weather code as string (e.g., "3")
	PrecipChance      json.Number       `json:"precipChance"`   // String
	PrecipQuantity    json.Number       `json:"precipQuantity"` // Can be int or float (mm)
	Pressure          json.Number       `json:"pressure"`       // hPa (can be int or string in NL)
	WindSpeedKm       json.Number       `json:"windSpeedKm"`    // Can be int or string in NL
	WindPeakSpeedKm   *int              `json:"windPeakSpeedKm"`
	WindDirection     json.Number       `json:"windDirection"` // Encoded, can be int or string
	WindDirectionText map[string]string `json:"windDirectionText"`
	DayNight          string            `json:"dayNight"` // "d" or "n"
	DateShow          *string           `json:"dateShow"` // Date label on day transitions
	DateShowLocalized map[string]string `json:"dateShowLocalized"`
	// Netherlands only
	HourUTC *string `json:"hourUtc"`
}

HourlyForecast represents hourly forecast data

func (*HourlyForecast) ParsePrecipChance

func (h *HourlyForecast) ParsePrecipChance() *int

ParsePrecipChance converts PrecipChance to int

func (*HourlyForecast) ParsePrecipQuantity

func (h *HourlyForecast) ParsePrecipQuantity() *float64

ParsePrecipQuantity converts PrecipQuantity to float64

func (*HourlyForecast) ParsePressure

func (h *HourlyForecast) ParsePressure() *int

ParsePressure converts Pressure to int

func (*HourlyForecast) ParseWW

func (h *HourlyForecast) ParseWW() *int

ParseWW converts WW weather code to int (handles both int and string)

func (*HourlyForecast) ParseWindDirection

func (h *HourlyForecast) ParseWindDirection() *int

ParseWindDirection converts WindDirection to int

func (*HourlyForecast) ParseWindSpeedKm

func (h *HourlyForecast) ParseWindSpeedKm() *int

ParseWindSpeedKm converts WindSpeedKm to int

type Module

type Module struct {
	Type string          `json:"type"` // "svg", "uv", "observation"
	Data json.RawMessage `json:"data"` // Raw JSON, parsed based on type
}

Module represents additional data (pollen, UV, observation count, ephemerides)

func (*Module) IsEphemerides

func (m *Module) IsEphemerides() bool

IsEphemerides checks if this SVG module is an ephemerides chart

func (*Module) IsPollen

func (m *Module) IsPollen() bool

IsPollen checks if this SVG module is a pollen chart

func (*Module) ParseModuleData

func (m *Module) ParseModuleData() interface{}

ParseModuleData unmarshals the generic Data field based on type

type NetworkError

type NetworkError struct {
	Err error
}

NetworkError wraps network/connection errors

func (*NetworkError) Error

func (e *NetworkError) Error() string

func (*NetworkError) ExitCode

func (e *NetworkError) ExitCode() int

func (*NetworkError) Unwrap

func (e *NetworkError) Unwrap() error

type Observation

type Observation struct {
	Temp      int    `json:"temp"`
	Timestamp string `json:"timestamp"`
	WW        int    `json:"ww"`
	DayNight  string `json:"dayNight"` // "d" or "n"
	// Netherlands only
	WindSpeedKm       *int              `json:"windSpeedKm"`
	WindDirection     *int              `json:"windDirection"`
	WindDirectionText map[string]string `json:"windDirectionText"`
	MunicipalityCode  *string           `json:"municipality_code"`
	Municipality      *string           `json:"municipality"`
}

Observation represents current weather conditions

type ObservationModuleData

type ObservationModuleData struct {
	Count int               `json:"count"`
	Title map[string]string `json:"title"`
}

ObservationModuleData represents observation count module

type ParseError

type ParseError struct {
	Body []byte
	Err  error
}

ParseError indicates JSON parsing failure

func (*ParseError) Error

func (e *ParseError) Error() string

func (*ParseError) ExitCode

func (e *ParseError) ExitCode() int

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

type RadarFrame

type RadarFrame struct {
	Time           string      `json:"time"`     // ISO 8601
	URI            string      `json:"uri"`      // Image URL (getIncaImage or KNMI CDN)
	Value          json.Number `json:"value"`    // Precipitation value (int or float)
	Position       float64     `json:"position"` // Normalized position (0-1) for timeline
	PositionLower  float64     `json:"positionLower"`
	PositionHigher float64     `json:"positionHigher"`
}

RadarFrame represents a single radar frame in the animation

func (*RadarFrame) ParseTimestamp

func (r *RadarFrame) ParseTimestamp() (*time.Time, error)

ParseTimestamp parses the ISO 8601 timestamp

func (*RadarFrame) ParseValue

func (r *RadarFrame) ParseValue() *float64

ParseValue converts Value to float64

type RateLimitError

type RateLimitError struct {
	RetryAfter time.Duration
	Err        error
}

RateLimitError indicates rate limit exceeded

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

func (*RateLimitError) ExitCode

func (e *RateLimitError) ExitCode() int

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter controls request rate to the API using a token bucket algorithm. It is safe for concurrent use.

func NewRateLimiter

func NewRateLimiter(rate int, interval time.Duration) *RateLimiter

NewRateLimiter creates a rate limiter with the specified rate and interval. rate: number of requests allowed per interval interval: time duration for the rate window Example: NewRateLimiter(10, time.Second) allows 10 requests per second

func (*RateLimiter) Allow

func (r *RateLimiter) Allow() bool

Allow returns true if a request can be made immediately without blocking. If true is returned, a token has been consumed.

func (*RateLimiter) Wait

func (r *RateLimiter) Wait(ctx context.Context) error

Wait blocks until a request can be made, respecting the rate limit. Returns nil if a request can be made, or an error if the context is cancelled.

type RetryTransport

type RetryTransport struct {
	Base       http.RoundTripper
	MaxRetries int
	BaseDelay  time.Duration
}

RetryTransport wraps http.RoundTripper with retry logic for resilience. Retries on 429 (rate limit) and 5xx errors with exponential backoff + jitter.

func NewRetryTransport

func NewRetryTransport(base http.RoundTripper) *RetryTransport

NewRetryTransport creates a RetryTransport with sensible defaults. Uses http.DefaultTransport as the base if not provided.

func (*RetryTransport) RoundTrip

func (t *RetryTransport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements http.RoundTripper with retry logic. Returns the response on success, or the last error if all retries are exhausted.

type SVGChart

type SVGChart struct {
	URL   map[string]string `json:"url"`
	Ratio float64           `json:"ratio"`
}

SVGChart represents a single SVG chart with multilingual URLs

type SVGModuleData

type SVGModuleData struct {
	URL   map[string]string `json:"url"`
	Ratio float64           `json:"ratio"`
}

SVGModuleData represents SVG-based modules (pollen, ephemerides)

type UVModuleData

type UVModuleData struct {
	LevelValue float64           `json:"levelValue"`
	Level      map[string]string `json:"level"`
	Title      map[string]string `json:"title"`
}

UVModuleData represents UV index module

type Warning

type Warning struct {
	IconCountry   string            `json:"icon_country"`
	WarningType   *WarningType      `json:"warningType"`
	WarningLevel  string            `json:"warningLevel"` // "1", "2", "3"
	Text          map[string]string `json:"text"`
	FromTimestamp string            `json:"fromTimestamp"` // ISO 8601
	ToTimestamp   string            `json:"toTimestamp"`
}

Warning represents a weather warning

type WarningType

type WarningType struct {
	ID   string            `json:"id"`
	Name map[string]string `json:"name"`
}

WarningType contains warning classification

func (*WarningType) Slug

func (w *WarningType) Slug() string

Slug returns the slug name for this warning type

type WeatherCode

type WeatherCode int

WeatherCode represents a WMO weather code (ww field)

const (
	// Clear conditions
	CodeClearSky WeatherCode = iota
	CodeMainlyClear
	CodePartlyCloudy
	CodeOvercast

	CodeFog
	CodeDepositingRimeFog

	// Drizzle
	CodeLightDrizzle

	CodeModerateDrizzle

	CodeDenseDrizzle
	CodeLightFreezingDrizzle
	CodeDenseFreezingDrizzle

	// Rain
	CodeSlightRain

	CodeModerateRain

	CodeHeavyRain
	CodeLightFreezingRain
	CodeHeavyFreezingRain

	// Snow
	CodeSlightSnow

	CodeModerateSnow

	CodeHeavySnow
	CodeSnowGrains

	// Rain showers
	CodeSlightRainShowers
	CodeModerateRainShowers
	CodeViolentRainShowers

	// Snow showers
	CodeSlightSnowShowers
	CodeHeavySnowShowers

	// Thunderstorm
	CodeThunderstorm
	CodeThunderstormSlightHail

	CodeThunderstormHeavyHail
)

func (WeatherCode) Description

func (w WeatherCode) Description(lang string) string

Description returns human-readable description in the specified language. Falls back to English if language not found.

func (WeatherCode) Icon

func (w WeatherCode) Icon() string

Icon returns an emoji representation of the weather code. Returns a default emoji if code is not mapped.

func (WeatherCode) IsPrecipitation

func (w WeatherCode) IsPrecipitation() bool

IsPrecipitation returns true if this code indicates precipitation (drizzle, rain, snow, hail, or thunderstorm).

func (WeatherCode) IsSevere

func (w WeatherCode) IsSevere() bool

IsSevere returns true if this is severe weather (heavy rain, heavy snow, violent showers, or thunderstorm).

func (WeatherCode) IsValid

func (w WeatherCode) IsValid() bool

IsValid returns true if the weather code is a recognized WMO code.

func (WeatherCode) String

func (w WeatherCode) String() string

String returns the description in English (implements fmt.Stringer).

type WeatherCondition

type WeatherCondition struct {
	Code      int
	DayIcon   string
	NightIcon string
	Condition string
}

WeatherCode maps the ww codes to condition names The actual rendering depends on both ww code and dayNight ("d" or "n")

func GetWeatherCondition

func GetWeatherCondition(wwCode int) *WeatherCondition

GetWeatherCondition returns the condition for a ww code

type Wind

type Wind struct {
	Speed     int               `json:"speed"`     // km/h
	PeakSpeed json.Number       `json:"peakSpeed"` // Can be null, int, or string - stored as Number
	Dir       int               `json:"dir"`       // Encoded direction
	DirText   map[string]string `json:"dirText"`
}

Wind represents detailed wind information

func (*Wind) ParsePeakSpeed

func (w *Wind) ParsePeakSpeed() *int

ParsePeakSpeed converts PeakSpeed to int (handles null, int, string)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL