Monthly Archives: October 2022

Adressregister INSPIRE Stichtag 01.10.2022 – View für Adressen erzeugen und Indizes anlegen

Das Adressregister liegetbeim BEV als GeoPackage vor. Es handelt sich also um eine sqlite-Datenbank. Der Zugriff mit QGIS, speziell die Filterung nach Attributen, stellt bisweilen eine Geduldsprobe dar. Eine Inspektion der .gpkg-Datei mit dem DB Browser for SQLite zeigt, dass es jenseits der Primärschlüssel keinen Index in der Datenbank gibt.

Mit taktisch günstig gewählten Indizes kann die Geschwindigkeit beim Zugriff deutlich erhöht werden.

Um in einer Atlas-Anwendung Hausnummern in die Karte zeichnen zu können, und eine Liste von Adressen in einem Kartenausschnitt zu erhalten, ist es notwendig die Tabellen in der .gpkg-Datei zu verknüpfen. Es sieht in der Karte schöner aus, wenn die Hausnummern nicht an der Zugangskoordinate sondern am Gebäude gerendert werden. Leider haben nicht alle Adressen ein Gebäude zugeordnet (auch wenn dieses schon vor mehr als einem Jahrzehnt errichtet wurde). Es gilt also einen entsprechenden View zu erzeugen.

Was ist eigentlich eine Adresse?

Rechtsgültige Adressen in Österreich sind aktuell in der Adressregisterverordnung 2016 (BGBl. II Nr. 51/2016) definiert und bestehen aus (hier sinngemäß widergegeben):

  • Straßennamen oder Straßennamen abgekürzt
  • daneben stehend die Orientierungsnummer (Hausnummer) und die Adressdaten des Gebäudes
  • darunter die Postleitzahl und
  • daneben der Zustellort

Diese Informationen finden sich in:

  • AD_ThoroughfareName.text
  • AD_AddressLocatorMatchingTable.addressIdentifierGeneral
  • AD_BuildingGeometry.buildingIdentifier
  • AD_PostalDescriptor.postCode
  • AD_AddressAreaName.text

Der eindeutige Verweis auf eine Gemeinde erfolgt entweder über den geografischen Bezug (Verschneidung mit einer Administrative Boundary) oder über die Gemeindekennzahl GKZ:

  • AD_Municipality.localId_GKZ bzw.
  • AD_Municipality.text

Verknüpft werden diese mit den Punkten aus AD_AddressGeometry und AD_BuildingGeometry über die Tabelle AD_AddressLocatorMatchingTable mit dem Attribut localId_ADRCD.

Indizes

Um den Zugriff auf den Datenbestand zügig zu gestalten sollte zumindest für die verknüpfungsrelevanten Attribute ein Index erstellt werden. Diese sind:

  • AD_AddressLocatorMatchingTable.localId_ADRCD
  • AD_AddressLocatorMatchingTable.inspireID_SKZ
  • AD_AddressLocatorMatchingTable.inspireID_ZO
  • AD_AddressLocatorMatchingTable.inspireID_PLZ
  • AD_AddressLocatorMatchingTable.inspireID_OKZ (nicht zwingend notwendig)
  • AD_AddressLocatorMatchingTable.inspireID_GKZ (nicht zwingend notwendig)
  • AD_AddressAreaName.inspireId_ZO
  • AD_AreaName.inspireId_OKZ (nicht zwingend notwendig)
  • AD_Municipality.inspireId_GKZ (nicht zwingend notwendig)
  • AD_Municipality.localId_GKZ (nicht zwingend notwendig)
  • AD_Municipality.text (nicht zwingend notwendig)
  • AD_PostalDescriptor.inspireId_PLZ
  • AD_ThoroughfareName.inspireId_SKZ
  • AD_AddressGeometry.localId_ADRCD
  • AD_BuildingGeometry.localId_ADRCD
CREATE INDEX "index_AD_AddressLocatorMatchingTable_localId_ADRCD" ON "AD_AddressLocatorMatchingTable" (
	"localId_ADRCD"
);
CREATE INDEX "index_AD_AddressLocatorMatchingTable_inspireId_SKZ" ON "AD_AddressLocatorMatchingTable" (
	"inspireId_SKZ"
);
CREATE INDEX "index_AD_AddressLocatorMatchingTable_inspireId_ZO" ON "AD_AddressLocatorMatchingTable" (
	"inspireId_ZO"
);
CREATE INDEX "index_AD_AddressLocatorMatchingTable_inspireId_PLZ" ON "AD_AddressLocatorMatchingTable" (
	"inspireId_PLZ"
);
CREATE INDEX "index_AD_AddressLocatorMatchingTable_inspireId_OKZ" ON "AD_AddressLocatorMatchingTable" (
	"inspireId_OKZ"
);
CREATE INDEX "index_AD_AddressLocatorMatchingTable_inspireId_GKZ" ON "AD_AddressLocatorMatchingTable" (
	"inspireId_GKZ"
);

CREATE INDEX "index_AD_AddressAreaName_inspireId_ZO" ON "AD_AddressAreaName" (
	"inspireId_ZO"
);

CREATE INDEX "index_AD_AreaName_inspireId_OKZ" ON "AD_AreaName" (
	"inspireId_OKZ"
);

CREATE INDEX "index_AD_Municipality_inspireId_GKZ" ON "AD_Municipality" (
	"inspireId_GKZ"
);

CREATE INDEX "index_AD_Municipality_localId_GKZ" ON "AD_Municipality" (
	"localId_GKZ"
);

CREATE INDEX "index_AD_Municipality_text" ON "AD_Municipality" (
	"text"
);

CREATE INDEX "index_AD_PostalDescriptor_inspireId_PLZ" ON "AD_PostalDescriptor" (
	"inspireId_PLZ"
);

CREATE INDEX "index_AD_ThoroughfareName_inspireId_SKZ" ON "AD_ThoroughfareName" (
	"inspireId_SKZ"
);

CREATE INDEX "index_AD_AddressGeometry_localId_ADRCD" ON "AD_AddressGeometry" (
	"localId_ADRCD"
);

CREATE INDEX "index_AD_BuildingGeometry_localId_ADRCD" ON "AD_BuildingGeometry" (
	"localId_ADRCD"
);

AddressLocatorMatchingTable_flat

Entsteht durch die Verknüpfung von AD_AddressLocatorMatchingTable mit

  • AD_ThoroughfareName
  • AD_AddressAreaName
  • AD_PostalDescriptor
  • AD_AreaName
  • AD_Municipality
CREATE VIEW AddressLocatorMatchingTable_flat
AS
SELECT
	AD_AddressLocatorMatchingTable.localId_ADRCD,
	AD_AddressLocatorMatchingTable.addressIdentifierGeneral Hausnummer,
	AD_ThoroughfareName.text Strasse,
	AD_AddressAreaName.text Zustellort,
	AD_PostalDescriptor.postCode PLZ,
	AD_AreaName.text Ortsname,
	AD_Municipality.localId_GKZ GKZ,
	AD_Municipality.text Gemeinde
FROM
	AD_AddressLocatorMatchingTable
JOIN
	AD_ThoroughfareName ON AD_ThoroughfareName.inspireId_SKZ = AD_AddressLocatorMatchingTable.inspireId_SKZ
JOIN
	AD_AddressAreaName ON AD_AddressAreaName.inspireId_ZO = AD_AddressLocatorMatchingTable.inspireId_ZO
JOIN
	AD_PostalDescriptor ON AD_PostalDescriptor.inspireId_PLZ = AD_AddressLocatorMatchingTable.inspireId_PLZ
JOIN
	AD_AreaName ON AD_AreaName.inspireId_OKZ = AD_AddressLocatorMatchingTable.inspireId_OKZ
JOIN
	AD_Municipality ON AD_Municipality.inspireId_GKZ = AD_AddressLocatorMatchingTable.inspireId_GKZ

AdressGeometry_flat

Entsteht durch die Verknüpfung von AddressLocatorMatchingTable_flat mit AD_AddressGeometry. Für das Endergebnis nicht relevant, aber für Analysen hilfreich.

CREATE VIEW AddressGeometry_flat
AS
SELECT
AD_AddressGeometry.id,
AD_AddressGeometry.geom,
AddressLocatorMatchingTable_flat.*
FROM
AD_AddressGeometry
JOIN
AddressLocatorMatchingTable_flat ON AddressLocatorMatchingTable_flat.localId_ADRCD = AD_AddressGeometry.localId_ADRCD

BuildingGeometry_flat

Entsteht durch die Verknüfung von AddressLocatorMatchingTable_flat mit AD_BuildingGeometry. Für das Endergebnis nicht relevant, aber für Analysen hilfreich.

CREATE VIEW BuildingGeometry_flat
AS
SELECT
	AD_BuildingGeometry.id,
	AD_BuildingGeometry.geom,
	AD_BuildingGeometry.buildingIdentifier,
	AddressLocatorMatchingTable_flat.*
FROM
	AD_BuildingGeometry
JOIN
	AddressLocatorMatchingTable_flat ON AddressLocatorMatchingTable_flat.localId_ADRCD = AD_BuildingGeometry.localId_ADRCD

Address_flat: Finale Adressen für die Kartografie

Für Adressen ohne Gebäude soll die Koordinate aus AD_AddressGeometry verwendet werden. Andernfalls die Koordinate aus AD_BuildingGeometry. Gibt es eine Koordinate in AD_BuildingGeometry, dann ist auch der buildingIdentifier Teil der Adresse.

CREATE VIEW Address_flat
AS
SELECT
	CASE WHEN AD_BuildingGeometry.geom IS NOT NULL THEN
		AD_BuildingGeometry.geom
	ELSE
		AD_AddressGeometry.geom
	END AS geom,
	AD_AddressGeometry.localId_ADRCD,
	AD_BuildingGeometry.localId_ADRCD_SUBCD,
	CASE WHEN AD_BuildingGeometry.buildingIdentifier IS NOT NULL THEN
		AddressLocatorMatchingTable_flat.Hausnummer || ' ' || AD_BuildingGeometry.buildingIdentifier
	ELSE
		AddressLocatorMatchingTable_flat.Hausnummer
	END AS HausUndGebNummer,
	AddressLocatorMatchingTable_flat.*
FROM
	AD_AddressGeometry
LEFT JOIN
	AD_BuildingGeometry ON AD_AddressGeometry.localId_ADRCD = AD_BuildingGeometry.localId_ADRCD
JOIN
	AddressLocatorMatchingTable_flat ON AD_AddressGeometry.localId_ADRCD = AddressLocatorMatchingTable_flat.localId_ADRCD

Damit der View als Geometrie erkannt wird muss der GeoPackage-Spezifikation noch genüge getan werden und gpkg_contents und gpkg_geometry_columns angepasst werden.

INSERT INTO gpkg_contents
	("table_name",
	"data_type", "identifier",
	"min_x",
	"min_y",
	"max_x",
	"max_y",
	"srs_id")
VALUES
	('Address_flat',
	'features',
	'Address_flat',
	(SELECT MIN(min_x) from gpkg_contents),
	(SELECT MIN(min_y) from gpkg_contents),
	(SELECT MAX(max_x) from gpkg_contents),
	(SELECT MAX(max_y) from gpkg_contents),
	(SELECT srs_id FROM gpkg_contents WHERE table_name = 'AD_AddressGeometry')
	);
INSERT INTO gpkg_geometry_columns
	("table_name",
	"column_name",
	"geometry_type_name",
	"srs_id",
	"z",
	"m")
VALUES
	('Address_flat',
	'geom',
	(SELECT geometry_type_name FROM gpkg_geometry_columns WHERE table_name = 'AD_AddressGeometry'),
	(SELECT srs_id FROM gpkg_geometry_columns WHERE table_name = 'AD_AddressGeometry'),
	0,
	0
	);

Fazit

Leider erkennt QGIS noch immer nicht das CRS (srs_id), das kann man aber bei einem eingefügten Layer auch noch nachträglich festlegen.
Unangenehmer ist die Tatsache, dass die Geometrie durch den Wahlausdruck scheinbar den View verlangsamt.

Future Work: Address_flat als tatsächliche Tabelle ggf. in ein separates .gpkg-File exportieren.

Verwaltungsgrenzen (VGD) INSPIRE Stichtag 01.04.2022 – Zugriff beschleunigen (Indizes anlegen)

Die Verwaltungsgrenzen liegen beim BEV als GeoPackage vor. Es handelt sich also um eine sqlite-Datenbank. Der Zugriff mit QGIS, speziell die Filterung nach Attributen, stellt bisweilen eine Geduldsprobe dar. Eine Inspektion der .gpkg-Datei mit dem DB Browser for SQLite zeigt, dass es jenseits der Primärschlüssel keinen Index in der Datenbank gibt.

Mit taktisch günstig gewählten Indizes kann die Geschwindigkeit beim Zugriff deutlich erhöht werden.

AU_flat_view

Um den Umriss einer einzelnen Gemeinde zu erhalten ist es notwendig im AU_flat_view nach “nationalLevelName” sowie “text” zu filtern. Der View ist wie folgt definiert:

CREATE VIEW "AU_flat_view" AS 
SELECT
AU_AdministrativeUnit.geom,
AU_AdministrativeUnit.inspireId,
AU_Attribute.country,
AU_Attribute.nationalCode,
AU_Attribute.nationalLevel,
AU_Attribute.beginLifespanVersion,
AU_Attribute.nationalLevelName,
AU_Attribute.residenceOfAuthority,
AU_Attribute.upperLevelUnit,
AU_Name.text,
AU_Name.script,
AU_Name.language,
AU_Name.nativeness,
AU_Name.nameStatus,
AU_Name.sourceOfName
FROM
AU_AdministrativeUnit
INNER JOIN AU_Attribute ON AU_AdministrativeUnit.inspireId = AU_Attribute.inspireId
INNER JOIN AU_Name ON AU_AdministrativeUnit.inspireId = AU_Name.inspireId

Für die im JOIN-Statement verwendeten Attribute ist auch kein Index definiert. Es gilt also einen Index zu definieren für:

  • AU_AdministrativeUnit.inspireId
  • AU_Attribute.inspireId
  • AU_Name.inspireId
  • AU_Attribute.nationalLevel
  • AU_Name.text
CREATE INDEX "index_AU_AdministrativeUnit_inspireId" ON "AU_AdministrativeUnit" (
	"inspireId"
);
CREATE INDEX "index_AU_Attribute_inspireId" ON "AB_Attribute" (
	"inspireId"
);
CREATE INDEX "index_AU_Name_inspireId" ON "AU_Name" (
	"inspireId"
);
CREATE INDEX "index_AU_Attribute_nationalLevel" ON "AU_Attribute" (
	"nationalLevel"
);
CREATE INDEX "index_AU_Name_text" ON "AU_Name" (
	"text"
);

OPA – Remix

Unter https://www.team122.at/projekte/opa-ortsplan-adressen/ gibt es ein Projekt welches Adressdaten und Kartenkacheln für Gemeinden aus Salzburg anbietet. Für die Steiermark ist mir leider nichts dergleichen bekannt. Dies ist der Versuch etwas Vergleichbares zu implementieren.

Datenquellen und Ressourcen

  • Grundkarte: https://basemap.at
  • Verwaltungsgrenzen: AT-INSPIRE_AU_Administrative_Units_SHP 01.04.2021.zip via https://bev.gv.at
  • Adressen: Adresse-Relationale_Tabellen_Stichtagsdaten_01102022.zip via https://bev.gv.at
  • Atlas-Erstellung: Claas Leiner “Tricks und Kniffe rund um den QGIS-Atlas”, https://media.ccc.de/v/fossgis2021-9004-tricks-und-kniffe-rund-um-den-qgis-atlas

Ziele

  • Regelmäßiger Blattschnitt
  • Übersichtsseite mit Blattschnitt
  • Sinnvoller Maßstab für die Blätter (OPA verwendet 1:3500)
  • Liste von Adressen in der Gemeinde mit
    • Blattnummer
    • Koordinaten die geeignet sind innerhalb eines Blattes die Adresse zu identifizieren
  • Hausnummern in größerer Schrift der basemap überlagert
  • Projektion in EPSG:31256 (ETRS89, passend zur basemap und den Adressen in meinem Heimatort)
  • Raster in EPSG:4326 (WGS84, passend zu GPS-Koordinaten in lat/lon decimal degrees wie in osm.org/maps.google.com etc)
  • Raster in EPSG:4326 aber dargestellt in degrees, decimal minutes (wie die Funkgeräte in der Feuerwehr)

Prozess

Basemap High DPI und AU_AdministrativeUnit als Layer hinzufügen. Projekt-CRS auf EPSG:31256 festlegen. AU-Layer filtern auf die entsprechende Gemeinde (“inspireId” LIKE ‘%.PG.61761%’) wobei die Gemeindekennzahl entsprechend zu wählen ist. Die Gemeindekennzahl findet man bei Bedarf in der Datei GEMEINDE.csv der Adressdaten.

Blattschnittgitter erzeugen

Mit “Create grid” aus der Processing Toolbox ein Gitter erzeugen. Als type “Rectangle”, extent den (gefilterten) AU-Layer, Horizontal spacing von 560, Vertical spacing 850, overlay 0 und als CRS EPSG:31256 auswählen. Die gewählte Größe ergibt im gewählten Atlas-Layout bei einem Maßstab von 1:3500 passend viel Rand um auf die Nachbarblätter zu verweisen.

Da das erzeugte Grid deckt einen rechteckigen Bereich um die Gemeinde ab, nicht alle erzeugten Felder liegen aber auch innerhalb der Gemeinde. Das kann man manuell korrigieren, oder mit “Extract by location” das erzeugte Gitter mit den Gemeindegrenzen via “intersect” verschneiden.

Dieser Vorgang wurde mit dem “Model Designer” umgesetzt und der erzeugte Layer manuell als GeoPackage gespeichert.

Blattschnittgitter stylen

Für einen gefälligen Atlas wurde der Ansatz aus dem referenzierten Vortrag von Claas Leiner verfolgt. Mit einer rule-based symbology sowie label und der Regel “id” = @atlas_pagename wird das zentrale Element der Atlas-Seite neutral gestaltet, d.h. ohne Füllung, Rahmen und Text während die übrigen Elemente (ELSE) eine zu 50% deckende weiße Füllung und eine Beschriftung erhalten.

Layout erstellen

Das A4-Hochformat Layout erhält eine Karte “Controlled by Atlas” mit einer “Fixed scale”. Die Karte erhält auch zwei Grids aus dem EPSG:4326. Eines mit Koordinaten im Format “Degree, Minute” und einem Interval von 1/60/10. Ein Zweites mit Koordinaten im Format “Degree” und einem Interval von 1/1000. Um die Beiden unterscheidbar zu halten sind die Koordinaten einmal innerhalb des Rahmens, einmal außerhalb sowie mit einem “Solid” Grid bzw. “Cross” ausgeführt.

Zwei Scalebar-Elemente (eines vom Stil “Single Box”, eines “Numeric”) helfen beim Abschätzen von Entfernungen in der Karte.

Ein Label mit dem Inhalt “Blatt [% @atlas_pagename %]” sorgt für die Identifizierung des Blattes.

Ein weiteres Label mit Lizenzhinweisen rundet das Blatt ab.

Ergebnis

Ein Blatt des erstellten Atlas

TODO

  • Adressen importieren und darstellen
  • Adressen mit Blattschnitt verschneiden und Liste erzeugen
  • Übersichtskarte (Blattschnitt) erstellen