Search by

siteapps / knmi-edr

siteapps

PHP 8.4 client for the KNMI Environmental Data Retrieval API.

dev-main 2026-11-04 13:08 UTC

This package is auto-updated.

Last update: 2026-11-04 13:11:20 UTC


README

Composer-library in namespace KMNI\EDR, exact volgens de opgegeven spelling. Package: kmni/edr. De SDK is niet gepubliceerd op Packagist.

Dezelfde opzet als de eerdere OpenHoliday SDK: Client, Configuration, een vervangbaar cURL-transport, getypeerde modellen, enums, aparte *Response-methodes, voorbeelden, dependencyvrije tests en GitHub Actions. Alleen PHP ^8.4, ext-curl en ext-json zijn nodig; geen Guzzle of andere runtimepackages.

Validatiestatus: de volledige KNMI OpenAPI-download werd in de maakomgeving door het netwerk geblokkeerd. De handgeschreven methodes zijn gebaseerd op de officiële KNMI EDR-documentatie en de EDR-responsstructuren. Er is geen volledige, automatisch gegenereerde één-op-één mapping van alle OpenAPI-schema’s en de endpointdekking is niet volledig geverifieerd. Er waren ook geen PHP/Composer-binaries beschikbaar om de meegeleverde tests te draaien. Zie VALIDATION.md. Met composer openapi:fetch kun je de actuele specificatie ophalen; callOperation() biedt aanvullende GET-toegang via de echte operationIds, met generieke JSON-responses.

Installeren

Pak dit project uit en voer in de projectmap uit:

composer installs iteapps/knmi-edr

Je kunt de SDK ook in een eigen Git-repository zetten en die als Composer vcs-repository opnemen. Er is bewust geen composer.lock of vendor in deze library-zip opgenomen.

Beginnen

Vraag een EDR API-key aan via de KNMI API-catalogus. Een Open Data API-key is een andere sleutel. De SDK verstuurt de EDR-key rechtstreeks in Authorization, zonder Bearer.

<?php
require 'vendor/autoload.php';

use KMNI\EDR\Client;
use KMNI\EDR\Configuration;

$key = getenv('KNMI_EDR_API_KEY');
if ($key === false || $key === '') {
    throw new RuntimeException('KNMI_EDR_API_KEY ontbreekt.');
}
$client = new Client(new Configuration(apiKey: $key));

foreach ($client->getCollections()->collections as $collection) {
    echo $collection->id, ': ', $collection->title, PHP_EOL;
}

Stations en meetwaarden

Collecties bepalen welke parameters, querytypes, tijdvakken en formaten beschikbaar zijn. Vraag de metadata op voordat je parametercodes kiest. Namen uit oude KNMI-collecties zijn niet automatisch bruikbaar in nieuwe collecties.

use KMNI\EDR\Query\DataQuery;

$id = '10-minute-in-situ-meteorological-observations';
$metadata = $client->getCollection($id);
$stations = $client->getLocations($id);
$stationId = (string) $stations->features[0]->id;
$parameterName = array_key_first($metadata->parameterNames);

$data = $client->getLocation(
    collectionId: $id,
    locationIds: $stationId,
    query: new DataQuery(
        datetime: '2025-07-19T06:00:00Z/2025-07-19T07:00:00Z',
        parameterNames: [(string) $parameterName],
    ),
);

echo $data->toJson(JSON_PRETTY_PRINT);

Dit voorbeeld veronderstelt dat de metadata stations en parameters bevat. examples/station.php controleert lege resultaten. Controleer of het gekozen tijdvak binnen de actuele collectie-extent valt. Een lege query-uitkomst kan geldig zijn.

Meerdere stations: geef locationIds: ['0-20000-0-06260', '0-20000-0-06310'] op. Deze IDs zijn voorbeelden; de actuele lijst komt uit getLocations().

Gebiedsquery’s en instances

use KMNI\EDR\Query\DataQuery;
use KMNI\EDR\Value\BoundingBox;
use KMNI\EDR\Value\Wkt;

$time = new DataQuery(datetime: '2025-07-19T12:00:00Z');

if ($metadata->supports('cube')) {
    $area = $client->getCube($id, new BoundingBox([3.0, 51.0, 4.5, 52.0]), $time);
}
if ($metadata->supports('position')) {
    $point = $client->getPosition($id, Wkt::point(3.61, 51.5), $time);
}

Wkt::point() gebruikt x, y, bij de standaard geografische CRS dus lengtegraad, breedtegraad. Wkt::multiPoint() en Wkt::polygon() zijn beschikbaar. Voor andere WKT-vormen kun je rechtstreeks een WKT-string meegeven. WKT wordt niet volledig lokaal geparsed; de server valideert geometrie en collectie-specifieke eisen.

Wanneer de collectie instances aanbiedt, gebruik getInstances(), getInstance() en bij een dataquery de optionele named argument instanceId:. De SDK construeert dan /collections/{collectionId}/instances/{instanceId}/…. Niet iedere collectie biedt instances of ieder querytype aan; er vindt geen automatische capability-request plaats.

QueryType bevat daarnaast Radius, Trajectory en Corridor voor query()/queryResponse(). Dit zijn EDR-patronen, geen bewering dat KNMI ze voor een bepaalde collectie aanbiedt. Gebruik ze uitsluitend als de actuele metadata/spec ze vermeldt, met de daar voorgeschreven parameters.

Datum, hoogte en extra filters

DataQuery accepteert een datum, RFC 3339-tijdstip met tijdzone, DateTimeInterface of een gesloten/open interval. new DateTimeRange(null, '2025-01-01') levert ../2025-01-01. Twee open grenzen en omgekeerde intervallen worden geweigerd. Duration-gebaseerde intervallen worden niet door de typed helper ondersteund; gebruik zo nodig de generieke requestmethode.

$query = new DataQuery(
    datetime: '2025-07-19T06:00:00Z/2025-07-19T07:00:00Z',
    parameterNames: ['een-code-uit-de-metadata'],
    z: '0',
    crs: null,
    extra: ['een-filter-uit-de-specificatie' => 'waarde'],
);

parameterNames wordt parameter-name met kommagescheiden codes. extra biedt toegang tot collectie-specifieke filters. De SDK verzint geen filtercodes en valideert niet de volledige OpenAPI-queryschema’s. Standaardparameters mogen niet dubbel worden opgegeven. Gebruik crs en z alleen indien ondersteund.

CoverageJSON verwerken

Een dataquery geeft een Coverage of CoverageCollection terug. De oorspronkelijke structuur, onbekende uitbreidingen en lege JSON-objecten blijven via toJson() bewaard. toArray() is een associatieve convenience-view; daarin worden lege objecten lege arrays. Zeer grote JSON-integers worden als strings ingelezen.

use KMNI\EDR\Model\CoverageCollection;

$coverages = $data instanceof CoverageCollection ? $data->coverages : [$data];
foreach ($coverages as $coverage) {
    foreach ($coverage->ranges as $name => $range) {
        if (is_string($range)) {
            continue; // Externe range-URL, wordt niet automatisch gevolgd.
        }
        print_r($range->axisNames);
        print_r($range->shape);
        print_r($range->values);
    }
}

NdArray::valueAt(['t' => 0, 'y' => 0, 'x' => 0]) indexeert op asnaam volgens de werkelijke axisNames-volgorde. Geef precies de aanwezige assen op; sommige responses hebben andere assen. null blijft een ontbrekende meetwaarde en wordt niet nul. Parameters kunnen op het CoverageCollection-niveau staan; ze worden niet impliciet in elk kind gekopieerd. De typed range-reader ondersteunt inline NdArray en range-URLs. Voor andere encodings, zoals getegelde ranges, gebruik je de ruwe response.

Ruwe responses en andere formaten

Iedere getypeerde endpointmethode heeft een *Response()-variant met HttpResponse: statusCode, headers, body, getHeader() en getHeaderLine(). Kies een outputformaat dat de collectie daadwerkelijk aanbiedt via DataQuery(format: ...). Typed dataquery-methodes verwachten CoverageJSON; gebruik voor andere formaten de responsevariant.

$response = $client->getCubeResponse($id, new BoundingBox([3.0, 51.0, 4.5, 52.0]), $time);
file_put_contents('metingen.json', $response->body);
echo $response->getHeaderLine('X-KNMI-Deprecation');

Voor aanvullende routes zijn request('/relatief/pad', $query) en requestResponse(...) beschikbaar. Alleen paden binnen de ingestelde API-root worden geaccepteerd. Queryarrays worden kommagescheiden geserialiseerd; herhaalde querysleutels en complexe OpenAPI deepObject-serialisatie zijn niet geïmplementeerd.

Actuele OpenAPI-specificatie

composer openapi:fetch
php examples/openapi.php

openapi:fetch haalt de werkelijke specificatie op, valideert OpenAPI 3.x en schrijft openapi/openapi.json plus een SHA-256-bronmanifest. Dit gebeurt alleen als je het commando uitvoert. Het genereert geen extra PHP-klassen.

use KMNI\EDR\OpenApi\Document;

$document = Document::fromJson(file_get_contents('openapi/openapi.json'));
foreach ($document->getOperations() as $operation) {
    echo $operation->id, ' ', $operation->method, ' ', $operation->path, PHP_EOL;
}

// Vul bestaande operationId en exacte parameternamen uit de specificatie in:
// $result = $client->callOperation($document, $operationId, $pathParameters, $queryParameters);
// $raw = $client->callOperationResponse($document, $operationId, $pathParameters, $queryParameters);

De operation-router ondersteunt GET, interne $ref-verwijzingen, vereiste queryparameters en encoded padparameters. Hij voert geen volledige schema- of responsevalidatie uit, ondersteunt geen POST/body-operaties en volgt geen externe $ref-URLs. Responses zijn JsonDocument of HttpResponse, geen automatisch gegenereerde responseklasse per operationId. Operaties zonder operationId kun je via het relatieve pad aanroepen. Een opgegeven servers-URL wordt niet automatisch gevolgd; Configuration::baseUri bepaalt altijd de bestemming.

Fouten en transport

  • ApiException: HTTP buiten 200–299, met volledige response, getDetails(), getStatusCode() en getRetryAfterSeconds().
  • TransportException: cURL-/verbindingsfout of timeout.
  • DecodingException: ongeldige JSON of een niet-passende getypeerde respons, met response indien beschikbaar.
  • InvalidArgumentException: ongeldige lokale invoer.

De SDK volgt geen redirects en doet geen automatische retries. Plan retries zelf, met name bij 429, rekening houdend met Retry-After. TLS-verificatie staat aan. Responses worden volledig in geheugen gelezen; vraag grote datasets in beperkte tijd-/gebiedsblokken op. Configuration biedt timeout, connectTimeout, baseUri, userAgent en aanvullende headers. De API-key staat niet in URLs en wordt gemaskeerd in debug-output van Configuration.

TransportInterface::get(string $url, array $headers): HttpResponse is injecteerbaar via new Client($configuration, $transport). Dit is dezelfde transportopzet als de OpenHoliday SDK. Alternatieve transports moeten alle HTTP-statuscodes teruggeven en alleen transportproblemen als TransportException melden.

Tests en voorbeelden

composer check                 # PHP-lint, unit tests, echte cURL-tests tegen localhost
composer test:integration      # 3 live metadata/schema-requests; slaat over zonder API-key
php examples/basic.php
php examples/station.php
php examples/spatial.php
php examples/openapi.php
php examples/errors.php

De 16 unit-testgroepen gebruiken synthetische fixtures. Die zijn geen opgeslagen KNMI-responses. De transporttest start een lokale PHP-webserver en test headers, URL-parameters, redirects, foutstatussen en timeouts. GitHub Actions bevat PHP 8.4 en 8.5. Live tests draaien niet automatisch in CI en testen geen volledige dataquerymatrix.

Bronnen

De bron-URL en de beperkingen van deze oplevering staan ook in openapi/source.json. Dit is een onafhankelijke SDK, geen officieel KNMI-package.