STA-Applikation - Technische Dokumentation

Die Applikation besteht aus einem Server (numis-hzn-cld-nbg1,angemietet bei Hetzner, Standort RZ Nürnberg). Der Server wird per Configuration Management auf Ansible-Basis konfiguriert. Die Systemaktualisierung erfolgt laufend. Ein Reboot des Servers erfolgt nur, wenn dies z.B. nach einem Kernel Update erforderlich ist.

Komponenten

Auf dem Server sind folgende Komponenten installiert:

FROST-Server

Der FROST-Server v.2.3.2. (https://github.com/FraunhoferIOSB/FROST-Server), eine Server-Implementierung der OGC SensorThings API (https://www.ogc.org/standards/sensorthings) (API-Version 1.1).

Zugriff über HTTP

https://sta.niedersachsen.de/v1.1

API des FROST-Server. Lesende Zugriffe sind ohne Anmeldung möglich. Schreibende Zugriffe nur bei vorheriger Anmeldung möglich.

https://sta.niedersachsen.de/FROST-Server/

Startseite des FROST Servers. Der Zugang ist über BASIC Authentication abgesichert.

https://sta.niedersachsen.de/grafana/

Grafana Instanz für die Visualisierung von Daten aus dem STA Server. Der Zugang ist über BASIC Authentication abgesichert.

https://sta.niedersachsen.de/pgadmin/

Datenbankverwaltungstool (PgAdmin 4) für die PostGreSQL Datenbank. Der Zugang ist über BASIC Authentication und ein internes Loginportal abgesichert.

Dokumentation der SensorThings API, Version 1.1: https://docs.ogc.org/is/18-088/18-088.html

Die Parametrisierung der einzelnen Endpoints ist hier dokumentiert: https://github.com/FraunhoferIOSB/FROST-Server/wiki/Features

Beispiele für Abfragen finden sich hier: https://github.com/FraunhoferIOSB/FROST-Server/wiki/Example-queries

Basic Authentication

Seit dem Update auf FROST-Server v1.14.1 und der Umstellung auf die STA-API v1.1 wird die Basic Authentication (https://fraunhoferiosb.github.io/FROST-Server/settings/auth.html) des FROST-Servers eingesetzt.

Nach Aktivierung der Basic Authentication in der docker-compose.yml werden in der Datenbank des FROST-Servers die Tabellen USERS und USER_ROLES erstellt. Hier wurden die User „read“ und „write“ gelöscht, sowie der User „admin“ in „sensorthings“ umbenannt und das Datenbank-Passwort für ihn gesetzt. Nur der User „sensorthings“ darf Änderungen an der Datenbank durchführen. Bei anonymem Zugriff sind ausschließlich Lesevorgänge möglich.

MQTT

Der Message Queuing Telemetry Transport (MQTT) wurde aktiviert. Hiermit ist auch das Abonnieren von „Topics“ (wie z.B. ‚v1.1/Datastreams(3)/Observations‘) möglich. Dazu ist wie beim lesenden Zugriff über HTTP keine Authentifizierung mehr notwendig. Hierfür wurde „auth_allowAnonymousRead=true“ in der docker-compose.yml gesetzt. Ein schreibender Zugriff ist über MQTT nur mit Benutzernamen und Passwort möglich.

Der MQTT-Dienst ist unter „mqtt://sta.niedersachsen.de:1883“ zu erreichen.

Apache Webserver

Der Apache Webserver dient als Proxy für die installierten Komponenten. Der Zugriffsschutz ist hier implementiert.

PostGreSQL

Datenbank für die Zeitreihendaten. (Es wird Version 16-3.4 eingesetzt.)

PgAdmin 4

Datenbankverwaltungstool für PostGeSQL-Datenbank. (Es wird Version 8.9 eingesetzt.)

Grafana

Plattformübergreifende Webanwendung für Analyse und interaktive Visualisierung. (Es wird Version 9.5.20 eingesetzt.)

Data Importer

Importer für LÜN-, NLWKN-, Grundwasser- und ODL-Daten, geschrieben in Kotlin. Der Importer ist auf dem Server sta.niedersachsen.de per docker installiert.

Messnetze

Luftüberwachung Niedersachsen (LÜN)

Der Import der LÜN-Daten basiert auf der Datenquelle https://numis.niedersachsen.de/data/luenm.json und wird stündlich durch einen Webhook ausgelöst.

Folgende Abbildung wird vorgenommen:

STA Property

JSON Data

Thing.name

‚luen_‘ + stationen->kennung

Thing.description

stationen->name

Thing.properties.id

stationen->kennung

Thing.properties.eoi

stationen->EOI-kennung

Thing.properties.address.street

stationen->strasse

Thing.properties.address.city

stationen->ort

Thing.properties.measuring_network.name

Lufthygienisches Überwachungssystem Niedersachsen (LÜN)

Thing.properties.measuring_network.id

NI_LUEN

Thing.properties.urls.url

[0] https://numis.niedersachsen.de/messwertsuche#/?networks=luen, [1] https://www.umwelt.niedersachsen.de/startseite/themen/luftqualitat/lufthygienische_uberwachung_niedersachsen/

Thing.properties.urls.title

[0] Messdaten Visualisierung im Nds. Umweltportal, [1] Weiterführende Informationen

Thing.properties.urls.type

[0] VIEW, [1] INFORMATION

Thing.properties.operator.name

Lufthygienisches Überwachungssystem Niedersachsen (LÜN)

Thing.properties.operator.url

https://www.luen-ni.de

Thing.properties.extras.gebiet

stationen->gebiet

Thing.properties.extras.art

stationen->art

Thing.properties.extras.reprasentativitaet

stationen->repräsentativitaet

Location.name, Location.description

stationen->name[, stationen->ort][, stationen->strasse]

Location.encodingType

application/vnd.geo+json

Location.location

GeoJSON Point: {type: Feature, geometry: {type: Point, coordinates: [stationen->koordinaten->laenge, stationen->koordinaten->breite]}}

ObservedProperty.name

komponenten->kennung

ObservedProperty.description

komponenten->name

ObservedProperty.definition

http://unitsofmeasure.org/ucum.html

ObservedProperty.properties.unit

komponenten->einheit

Sensor.name

komponenten->kennung

Sensor.description

komponenten->name[ [komponenten->einheit]]

Sensor.encodingType

text/html

Sensor.metadata

https://www.umwelt.niedersachsen.de/themen/luft/LUEN/

Datastream.name, Datastream.description

messwerte->kennung / messwerte->messstellen->kennung

Datastream.unitOfMeasurement

instanceof UnitOfMeasurement

Datastream.unitOfMeasurement.name

komponenten->kennung

Datastream.unitOfMeasurement.symbol

komponenten->einheit

Datastream.unitOfMeasurement.definition

http://unitsofmeasure.org/ucum.html

Datastream.observationType

http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement

Datastream.properties.temporal_resolution

z.B. P1D (1 Tag), P1H (1 Stunde), P0.5Y (6 Monate), P1Y (1 Jahr), PT8H (8 Stunden)

Datastream.properties.type

z.B. MEAN (Mittelwert), MIN (Minimum), MAX (Maximum), SUM (Summe), MEAN_M24A (gleitender 24h-Mittelwert), MEAN_M8A (gleitender 8h-Mittelwert)

Datastream.properties.explanation

(erkärender Text zum jeweiligen Datastream)

Datastream.thing

instanceof Thing

Datastream.sensor

instanceof Sensor

Datastream.observedProperty

instanceof ObservedProperty

Observation.resultTime

Zeitpunkt berechnet aus ->datum, ->zeit, ->zeitzone und Index des Messwertes

Observation.datastream

instanceof Datastream

Observation.result

Werte aus Array messwerte->messstellen->verlauf | wenn Wert mit ‚<‘ startet dann 0.0 | wenn Wert mit ‚*‘ startet, dann ‚n/a‘ | wenn Wert keine Zahl ist, dann ‚n/a‘

Observation.phenomenonTime

instanceof TimeObject mit Zeitpunkt berechnet aus ->datum, ->zeit, ->zeitzone und Index des Messwertes

Qualitätsindices der Luftstoffe

Qualitätsindices werden anders behandelt. Wenn sie vorhanden aus der Messstationen sind, werden Datastreams dafür erzeugt. Die Folgenden sind Sonderheiten von diesen Datastreams im Vergleich mit dem Standard-Datastream:

STA Property

JSON Data

Beispiel

Datastreams.name

stationen/kennung/QI*

HRVS/NO2/QI

unitOfMeasurement.name

Kurzzeit-Luftqualitätsindex (LQI)*

unitOfMeasurement.symbol

Index [1-6]*

ObservedProperty.description

einheit, Kurzzeit-Luftqualitätsindex (LQI)*

Stickstoffdioxid [µg/m³], Kurzzeit-Luftqualitätsindex (LQI)

ObservedProperty.name

kennung/QI*

NO2/QI

Observations.result

qualitätsindex

3.218

* statisch

Webservices von NLWKN-PegelOnline

Der Import der NLWKN-Daten basiert auf dem Webservices:

https://bis.azure-api.net/PegelonlinePublic/REST/stammdaten/stationen/All?key=9dc05f4e3b4a43a9988d747825b39f43

Über diese URL werden alle Stationen und deren zugehörigen (Mess-)Parametern entnommen.

Anhand den Stationen und den zugehörigen Parametern wird der Webservice erneut angefragt um die Messdaten für die Station-Parameter-Kombinaton zu erhalten.

Hierbei wird folgende URL als Template verwendet:

https://bis.azure-api.net/PegelonlinePublic/REST/station/{STA_ID}/datenspuren/parameter/{PAT_ID}/tage/{tage}?key=9dc05f4e3b4a43a9988d747825b39f43
  • {STA_ID} eindeutige ID der Station

  • {PAT_ID} eindeutige ID der Station

  • {tage} Anzahl der zurückliegenden Tage ab vom Aufruf der URL: -1: Der Import eines Tages erfolgt stündlich, um xx:15; -30: Der Import von 30 Tagen erfolgt wöchentlich, samstags um 3:50.

Folgende Abbildung wird vorgenommen:

spurenResult->Parameter->Datenspuren->Pegestaende->DatumUTC“

„Observation.datastream“, „instanceof Datastream“ „Observation.result“, „getPegelDatenspurenResult->Parameter->Datenspuren->Pegestaende->Wert“ „Observation.phenomenonTime“, „getPegelDatenspurenResult->Parameter->Datenspuren->Pegestaende->DatumUTC“

Grundwasserdaten Niedersachsen

Der Import der Grundwasserdaten Niedersachsen basiert auf dem Webservice:

https://bis.azure-api.net/GrundwasserstandonlinePublic/REST/stammdaten/stationen/allegrundwasserstationen?key=9dc05f4e3b4a43a9988d747825b39f43

Über diese URL werden alle Stationen abgefragt.

Mit Hilfe dieser Stationen wird der Webservice erneut angefragt um die Messdaten für die einzelnen Stationen zu erhalten. Hierzu wird die Stations-ID („STA_ID“) aus der obigen Anfrage sowie als PAT_ID die ID für den Grundwasserstand (563) verwendet:

https://bis.azure-api.net/GrundwasserstandonlinePublic/REST/station/{STA_ID}/datenspuren/parameter/{PAT_ID}/tage/{tage}?key=9dc05f4e3b4a43a9988d747825b39f43

Der Import erfolgt täglich, um 6:38h.

Folgende Abbildung wird vorgenommen:

STA Property

JSON Data

Thing.name

‚gw_‘ + {STA_ID}

Thing.description

{Ort}/{Name}

Thing.properties.id

{STA_ID}

Thing.properties.measuring_network.id

NI_GW

Thing.properties.measuring_network.name

Grundwassermessstellen Niedersachsen

Thing.properties.address.city

{Ort}

Thing.properties.operator.name

{Betreiber}

Datastream.name

THING.name + ‚_‘ + OBSERVED_PROPERTY.name + ‚_‘ + {properties.temporal_resolution} + ‚_‘ + {properties.type}

Datastream.description

THING.description + ‚: ‚ + {Stationsart_Text}/{Stationsart}: {WebDisplayName}

Datastream.observationType

„http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement“

Datastream.unitOfMeasurement

instanceof UnitOfMeasurement

Datastream.unitOfMeasurement.name

{Parameter.ParameterName}

Datastream.unitOfMeasurement.symbol

{Parameter.ParameterEinheit}

Datastream.unitOfMeasurement.definition

„https://ucum.org/ucum.html#para-30“

Datastream.properties.origin

datastream_name: WebDisplayName der Datenspur

Datastream.properties.temporal_resolution

Zuordnung des ersten Teils des WebDisplayNames zu den möglichen Werten: P1D | P1M | P0.5Y | P0.5Y | P1Y

Datastream.properties.type

Zuordnung des Webdisplaynames zu den möglichen Werten: MEAN | MIN | MAX | MIN.LONG_TERM | MAX.LONG_TERM | MEAN.LONG_TERM | Q85 | Q75 | Q25 | Q15

Datastream.thing

instanceof Thing

Datastream.sensor

instanceof Sensor

Datastream.observedProperty

instanceof ObservedProperty

Location.name

THING.name

Location.description

THING.description

Location.encodingType

„application/vnd.geo+json“

Location.location

GeoJSON Point: {type: Feature, geometry: {type: Point, coordinates: [{Longitude}, {Latitude}]}}

ObservedProperty.name

{Stationsart_Text}

ObservedProperty.definition

„http://unitsofmeasure.org/ucum.html“

ObservedProperty.description

{Parameter.Name}

ObservedProperty.properties.unit

{Parameter.Einheit}

Sensor.name

OBSERVED_PROPERTY.name

Sensor.encodingType

„text/html“

Sensor.description

{Parameter.ParameterName}

Sensor.metadata

„n/a“

FeaturesOfInterest.name

DATASTREAM.name

FeaturesOfInterest.description

DATASTREAM.description

FeaturesOfInterest.encodingType

„application/vnd.geo+json“

FeaturesOfInterest.feature

{type: Point, coordinates: [{Longitude}, {Latitude}]}

Ortsdosisleistung (ODL) des Bundesamts für Strahlenschutz

Der Import der ODL-Daten basiert auf dem Webservice:

https://www.imis.bfs.de/ogc/opendata/ows?service=WFS&version=1.1.0&request=GetFeature&typeName=opendata:odlinfo_odl_1h_latest&outputFormat=application/json

Über diese URL werden alle Daten zu den Stationen entnommen.

Anhand der Stationen wird der Webservice erneut angefragt, um die Messwerte zu erhalten.

Hierbei wird folgende URL als Template verwendet:

https://www.imis.bfs.de/ogc/opendata/ows?service=WFS&version=1.1.0&request=GetFeature&typeName=opendata:odlinfo_timeseries_odl_1h&outputFormat=application/json&viewparams=kenn:{station_id}

{station_id}

eindeutige Messstellenkennung

Der Import der ODL-Daten erfolgt stündlich, um xx:00.

Folgende Abbildung wird vorgenommen:

STA Property

JSON Data

Thing.name

‚odl_‘ + Stations->properties.id

Thing.description

Stations->properties.name

Thing.properties.measuring_network.id

ODL

Thing.properties.measuring_network.name

Gamma-Ortsdosisleistung (ODL) des BfS

Thing.properties.id

Stations->properties.id

Thing.properties.eoi

Stations->properties.kenn

Thing.properties.zip

Stations->properties.plz

Thing.properties.city

Stations->properties.name

Thing.properties.state

site_status: 1 (in Betrieb) → OPERATIONAL, 2 (defekt) → NOT_OPERATIONAL, 3 (Testbetrieb) → TEST_OPERATION

Thing.properties.urls.url

[0] https://odlinfo.bfs.de/ODL/DE/themen/wo-stehen-die-sonden/karte/_documents/Messstelle.html?id=[kenn]

Thing.properties.urls.title

[0] Informationen zur Messstelle

Thing.properties.urls.type

[0] INFORMATION

Thing.properties.operator.name

Bundesamt für Strahlenschutz

Thing.properties.operator.url

https://odlinfo.bfs.de/ODL/DE/home/home_node.html

Thing.properties.extra.kenn

Stations->properties.kenn

Location.name

‚odl_‘ + Stations->properties.id

Location.description

Stations->properties.name

Location.encodingType

„application/vnd.geo+json“

Location.location

GeoJSON Point: {type: Feature, geometry: {type: Point, coordinates: [Stations->geometry.coordinates[0], Stations->geometry.coordinates[1], Stations->properties.height_above_sea]}}

ObservedProperty.name

Stations->properties.nuclide

ObservedProperty.definition

„https://odlinfo.bfs.de/ODL/DE/themen/wie-wird-gemessen/interpretation/interpretation_node.html“

ObservedProperty.description

Stations->properties.nuclide

ObservedProperty.properties.unit

Stations->properties.unit

Sensor.name

Stations->properties.nuclide

Sensor.encodingType

„text/html“

Sensor.description

Stations->properties.nuclide [Stations->properties.unit]

Sensor.metadata

„n/a“

Datastream.name

Stations->properties.nuclide/Stations->properties.id

Datastream.description

Stations->properties.nuclide/Stations->properties.name

Datastream.observationType

„http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement“

Datastream.unitOfMeasurement.name

Stations->properties.nuclide

Datastream.unitOfMeasurement.symbol

Stations->properties.unit

Datastream.unitOfMeasurement.definition

„https://ucum.org/ucum.html#para-30“

Datastream.unitOfMeasurement

instanceof UnitOfMeasurement

Datastream.properties.temporal_resolution

PT1H (1 Stunde)

Datastream.properties.type

SUM (Summe)

Datastream.properties.origin

Stations->properties.nuclide/Stations->properties.duration

Datastream.thing

instanceof Thing

Datastream.sensor

instanceof Sensor

Datastream.observedProperty

instanceof ObservedProperty

Observation.resultTime

Measurements->properties.end_measure

Observation.datastream

instanceof Datastream

Observation.result

Measurements->properties.value

Observation.phenomenonTime

Measurements->properties.start_measure/Measurements->properties.end_measure

Observation.resultQuality

Measurements->properties.validated

Lufqualitätsindes (LQI) des Umweltbundesamtes

Der Import der LQI-Daten basiert auf dem Webservice:

https://www.umweltbundesamt.de/api/air_data/v3/stations/json?use=airquality&lang=de&date_from=“{DATE_FROM}“&date_to=“{DATE_TO}“ Hierbei werden alle Stationen für den Zeitraum von {DATE_FROM} (= heutiges Datum - 3 Tage) bis {DATE_TO} = (heutiges Datum) abgerufen.

(Schnittstellenbeschreibung siehe https://www.umweltbundesamt.de/daten/luft/luftdaten/doc)

Anhand der Stationen wird der Webservice erneut angefragt, um die Werte des LQI und der Schadstoffe aus denen er vestimmt wird abzurufen.

Hierbei wird folgende URL als Template verwendet:

https://www.umweltbundesamt.de/api/air_data/v3/airquality/json?&date_from={DATE_FROM}&date_to={DATE_TO}&time_from={HOUR_NOW}&time_to={HOUR_NOW}&station=<STATION_CODE>“ Hierbei müss zusätliche zum Datum auch die Zeit (hier die aktuelle Stunde {HOUR}) und der Stationscode ({STATION_CODE} aus der obigen Abfrage) gesetzt werden.

Der Import der LQI-Daten erfolgt stündlich, um xx:25.

Folgende Abbildung wird vorgenommen:

STA Property

JSON Data

Thing.name

‚uba_‘ + [networkCode] + _ + station->code (z.B. uba_RP_DERP019)

Thing.description

station->name/station->city (z.B. Kaiserslautern-Rathausplatz/Kaiserslautern)

Thing.properties.measuring_network.id

UBA_LQI

Thing.properties.measuring_network.name

Umweltbundesamt Luftdaten [Bundesland]

Thing.properties.id

station->code (z.B. DERP019)

Thing.properties.address.street

station->street station station->street_number (z.B. Rathausplatz 1)

Thing.properties.address.zip

station->zip_code (z.B. 67659)

Thing.properties.address.city

station->city (z.B. Kaiserslautern)

Thing.properties.address.country

DE

Thing.properties.state

zur Zeit nicht genutzt: OPERATIONAL wenn ‚Active to‘ null ist. NOT_OPERATIONAL wenn ‚Active to‘ gesetzt ist (Bei Station DEBB067 steht hier allerdings ein Datum-aber die Station bekommt aktuelle Werte, deswegen wird auf diesen Wert verzichtet.)

Thing.properties.operator.name

Umweltbundesamt Luftdaten [Bundesland]

Thing.properties.operator.url

https://www.umweltbundesamt.de/daten/luft/luftdaten/luftqualitaet

Thing.properties.extra.gebiet

station->settingShortName (z.B städtisch)

Thing.properties.extra.art

station->typeName (z.B. Hintergrund)

Location.name

wie Thing.name

Location.description

wie Thing.description

Location.encodingType

„application/geo+json“

Location.location

GeoJSON Point: {type: Feature, geometry: {type: Point, coordinates: [station->latitude, station->longitude]}}

ObservedProperty.name

z.B. LQI, LQI_PM10, LQI_O3, …

ObservedProperty.definition

http://unitsofmeasure.org/ucum.html

ObservedProperty.description

z.B. ‚Luftqualitätsindex (LQI)‘, ‚Feinstaub (PM₁₀)‘

ObservedProperty.properties.unit

‚Index‘ oder ‚μg/m³‘

Sensor.name

z.B: LQI_PM10

Sensor.encodingType

„text/html“

Sensor.description

z.B. Particulate matter PM₁₀

Sensor.metadata

„n/a“

Datastream.name

wie Thing.name + LQI oder LQI_PM10 usw. (z.B: uba_RP_DERP019_LQI)

Datastream.description

wie Thing.description + LQI oder LQI_PM10 usw. (z.B. Kaiserslautern-Rathausplatz/Kaiserslautern: LQI)

Datastream.unitOfMeasurement.name

LQI oder LQI_PM10 usw.

Datastream.unitOfMeasurement.symbol

(index) bei LQI, bei Schadstoffen: µg/m³

Datastream.unitOfMeasurement.definition

„https://ucum.org/ucum.html#para-30“

Datastream.unitOfMeasurement

instanceof UnitOfMeasurement

Datastream.observationType

„http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement“

Datastream.properties.temporal_resolution

P1H (1 Stunde)

Datastream.properties.type

‚INDEX‘ oder ‚MEAN‘

Datastream.thing

instanceof Thing

Datastream.sensor

instanceof Sensor

Datastream.observedProperty

instanceof ObservedProperty

Observation.resultTime

observation->endDate

Observation.datastream

instanceof Datastream

Observation.result

observation->value

Observation.phenomenonTime

observation->endDate

Deployment

Alle Komponenten werden über docker (docker-compose) auf sta.niedersachsen.de deployt.

Löschen von Objekten

Objekte können mit Hilfe der HTTP-Methode DELETE gelöscht werden.

Wenn ein ‚Datastream‘ gelöscht wird, werden auch alle zugehörigen ‚Observations‘ gelöscht. Wenn ein ‚Thing‘ gelöscht wird, werden ebenso die zugehörigen ‚Datastreams‘ mit allen ‚Observations‘ gelöscht.

Das Löschen kann nur mit Authentifizierung erfolgen.

Beispiel mit curl:

curl -u {username}:{password} -X DELETE -H "Content-Type: application/json" "https://sta.niedersachsen.de/v1.1/Things(30662)"