- Fachgebiet Datenbanken und Informationssysteme

Werbung
Modellierung von ATKIS-Daten mit der Spatial
Cartridge von Oracle 8.0.5
Studienarbeit von
Sascha Grobe
im Rahmen des Studiengangs
Mathematik mit der Studienrichtung
Informatik
an der Universität Hannover
Betreuer:
Prof. Dr. Udo Lipeck,
Carsten Kleiner
Hannover, den 11. Juni 1999
Inhaltsverzeichnis
1 Einleitung
1
2 ATKIS
2.1 Strukturierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 EER-Schema der ATKIS-Daten . . . . . . . . . . . . . . . . . . . . . . .
2.3 Relationenschema der ATKIS-Daten . . . . . . . . . . . . . . . . . . . .
3
3
4
5
3 Oracle8 Spatial Cartridge
3.1 Geometrische Datentypen . . . . . . . .
3.2 Datenmodell . . . . . . . . . . . . . . .
3.3 Datenbankstrukturen . . . . . . . . . . .
3.3.1 SDOLAYER-Tabelle . . . . . . . . .
3.3.2 SDODIM-Tabelle . . . . . . . . . .
3.3.3 SDOGEOM-Tabelle . . . . . . . . .
3.3.4 SDOINDEX-Tabelle . . . . . . . . .
3.4 Raumindex . . . . . . . . . . . . . . . .
3.5 Laden von Daten . . . . . . . . . . . . .
3.6 Erzeugung des Raumindex . . . . . . . .
3.7 Anfragen . . . . . . . . . . . . . . . . .
3.7.1 Primärfilter . . . . . . . . . . . .
3.7.2 Sekundärfilter . . . . . . . . . . .
3.8 Fensterfunktionen (Window Functions) .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4 Übertragung der ATKIS-Daten in die Spatial Cartridge
4.1 Grundsätzliche Überlegungen . . . . . . . . . . . . . . . . .
4.2 Das Programm ’atkis2sc’ . . . . . . . . . . . . . . . . . .
4.3 Erzeugung der Tabellen SDOLAYER, SDODIM und SDOGEOM . .
4.4 Laden der Daten . . . . . . . . . . . . . . . . . . . . . . . .
4.5 Ermittlung der maximalen Ausdehnung . . . . . . . . . . .
4.6 Berechnung des Tiling-Levels . . . . . . . . . . . . . . . . .
4.7 Erstellen der Indextabelle . . . . . . . . . . . . . . . . . . .
4.8 Generierung des Raumindexes . . . . . . . . . . . . . . . . .
4.9 Erstellen der Statistiken für den Optimierer . . . . . . . . .
iii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
7
8
8
8
9
9
11
11
12
13
13
13
14
.
.
.
.
.
.
.
.
.
15
15
16
18
19
19
20
21
22
23
iv
5 Testanfragen und deren Optimierung
5.1 Testumgebung . . . . . . . . . . . . . . . . . .
5.2 Welche Landesstraßen überqueren die Ihme? . .
5.3 Welche Straßen schneiden Schienen? . . . . . .
5.4 Welche Flächen berühren die Leine? . . . . . .
5.5 Um welche Bahnhöfe liegen Vegetationsflächen?
INHALTSVERZEICHNIS
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
25
25
25
28
31
35
6 Abschließende Bewertung
41
A Listings
A.1 Quellcode cr_schiene.sql . . . . . . . . . . . . . . . . . . . . . . . . .
A.2 Quellcode atkis2sc . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
A.3 Quellcode atkis2sc.mk . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
43
45
57
Abbildungsverzeichnis
60
Tabellenverzeichnis
61
Literaturverzeichnis
62
Kapitel 1
Einleitung
Durch den Einzug neuer Verfahren zur Bearbeitung von Bild-, Ton und Videodaten
in die Informationsverarbeitung, durch die stetig steigende Bedeutung des Internets
und auch durch die Entwicklung spezieller Software für den Umgang mit mehrdimensionalen Daten, wie z.B. CAD- und CAM-Anwendungen, sind auch für DBMS neue
Herausforderungen entstanden, denen herkömmliche relationale DBMS nicht mehr gewachsen waren. Komplexe, und auch selbst definierbare, Objekte mußten verwaltet
werden können. Es entstanden die objektrelationalen DBMS. So ist es z.B. möglich
geworden Bilddateien zu speichern und später anhand von speziellen Eigenschaften wie
dem Rotgehalt eines Bildes nach Bildern mit Sonnenuntergängen zu suchen oder aufgrund von Gitarrenklängen Tondateien, zu selektieren. Es ist aber auch möglich sich
das Zugriffsverhalten interessierter Leser auf HTML-Dokumente via Internet zu merken
und den Leser bei erneutem Zugriff gleich zu seinem Interessengebiet weiterzuleiten.
In dieser Studienarbeit geht es um die Implementierung von geowissenschaftlichen
Daten mit Hilfe der Spatial Cartridge von Oracle8. Die Spatial Cartridge ist ein optionaler Aufsatz, der die Speicherung und Verarbeitung von zweidimensionale geometrische
Daten ermöglicht. Für die Durchführung stehen geowissenschaftliche Daten des Amtlich
Topographisch Kartographischen Informationssystems (ATKIS) zur Verfügung, die in
[Kor97] in ein relationales Schema übertragen worden sind.
Zunächst wird im zweiten Kapitel das ATKIS-Datenmodell beschrieben. Dabei werden die Objektstruktur sowie einige inhaltliche Aspekte erläutert. Das dritte Kapitel
behandelt die Spatial Cartridge von Oracle8. Hier wird näher auf das Schichtenmodell und die Anfragegestaltung der Spatial Cartridge eingegangen. Das vierte Kapitel
befaßt sich mit der Übertragung der Daten in die Spatial Cartridge und deren Implementierung. Abschließend werden im fünften Kapitel die Funktionalität und Effizienz
der Spatial Cartridge anhand von Testanfragen untersucht. Dabei werden die Anfragen
und die Datenstruktur optimiert und mit den Ergebnissen aus [Kas97] verglichen. Im
letzten Kapitel stehen einige abschließende Bemerkungen.
1
Kapitel 2
ATKIS
ATKIS ist ein digitales, objektorientiertes Erdoberflächenmodell, das geometrische Informationen über grundrißbezogene Objekte enthält. Es wurde in den siebziger Jahren von der Arbeitsgemeinschaft der Vermessungsverwaltungen der Bundesrepublik
Deutschland (AdV) eingeführt. Für die Digitalisierung wurde die BRD mit einenm
Gitternetz überdeckt. Jedes Gitter stellt ein 100km2 großen Bezirk dar, den Numerierungsbezirk (NBZ). In dieser Arbeit handelt es sich um Daten des NBZ Hannover.
2.1
Strukturierung
Die Objekte werden mit Hilfe eines hierarchisch aufgebauten Objektartenkatalogs strukturiert und ihre Objektart durch eine vierstellige Nummer kodiert. Die erste Ziffer
bestimmt den Objektbereich, die zweite die Objektgruppe und die dritte und vierte
die Objektart. So gehört z.B. die Objektart ’3101’ (Straße) zur Objektgruppe ’31xx’
(Straßenverkehr) und die wiederum zum Objektbereich ’3xxx’ (Verkehr). Alle Objekte
einer Objektgruppe werden sogenannten Folien zugeordnet. Dies spielt in Kapitel 4 eine
wesentliche Rolle.
Objekte werden außerdem aufgrund ihrer Beschaffenheit in drei verschiedene Objekttypen unterteilt: punkt-, linien-, und flächenförmige Objekte. Die Lage aller drei
Typen wird durch eine Objektkoordinate bestimmt. Punktobjekte repräsentieren in diesem Datenbestand allerdings ausschließlich Namenszüge von Objekten, genauer gesagt,
die Positionierung von Orts- bzw. Städtenamen im digitalen Kartenwerk. Alle anderen
Objekte, wie Flüsse, Straßen, Gewässer aber auch Bahnhofsanlagen werden aufgrund
der Genauigkeit durch Linien- und Flächenobjekte dargestellt. Diese können aus mehreren Teilobjekten bestehen, die sich wiederum aus mehreren nach ihrer Reihenfolge
durchnumerierten Linienelementen bzw. Vektoren zusammensetzen können. Vektoren
werden durch einen Anfangspunkt, einen Endpunkt sowie eventuell existierende Stützpunkte bestimmt. Man spricht hier von der Geometrie eines Vektors. Weitere Merkmale
von Linienelementen sind deren Orientierung und deren Lage. Die Orientierung eines
Linienelements gibt an, in welcher Richtung sich das Linienelement an das vorherige
anschließt. Die Lage legt fest, auf welcher Seite des Linienelementes sich das von ihm
umschlossene Flächenobjekt befindet.
Schließlich gibt es noch komplexe Objekte, wie es teilweise Straßen sind, die sich
aus den Objekten Fahrbahn und Seitenstreifen zusammensetzen.
3
4
KAPITEL 2. ATKIS
2.2
EER-Schema der ATKIS-Daten
FOLIE
FolienNr: integer
Beschreibung: string
ObjektArt: integer
Folie:
ObjektArt:
gehört
zu
Modelltyp: string
OBJEKTART
Koordinten: koordinaten
Beschreibung
OBJEKT
ObjektNr: string
Objekttyp: string
ObjektTeil: Set
Name: Set
Typ: string
Typ: string
NAME
Name: string
ATTRIBUT
OBJEKTTEIL
ObjektTeilNr: string
Wert: string
: hat oben
Attribute: set
=
PUNKT
LINIE
Koordinten: koordinaten
Elemente: List
:hat unten
FLÄCHE
ÜBERFÜHRUNG
Elemente: List
Vektorkoordinaten: Strecke
Orientierung: boolean
LINIENELEMENT
Vektor:
VEKTOR
Lage: boolean
GeometrieArt: string
Parameter: Set
PARAMETER
GEOMETRIE
:Geometrie
Koordinten: koordinaten
Abbildung 2.1: EER–Diagramm zu ATKIS[Koch94]
2.3. RELATIONENSCHEMA DER ATKIS-DATEN
2.3
5
Relationenschema der ATKIS-Daten
Aus dem EER-Diagramm der ATKIS-Daten wurde in [Kor97] folgendes Relationenschema entwickelt:
OBJEKT(ObjNr, NBZ, X Koord, Y Koord, ModTyp, ObjTyp, Folie, ObjArt,
Kompl Obj→OBJEKT)
NAME(ObjNr→OBJEKT, Typ, Name)
ATTRIBUT(ObjNr→OBJEKT, ObjTeilNr, Typ, Wert)
OBJEKTART (ObjArt, Text)
FOLIE (Folie, Text)
KOMPLEX OBJ (Komplex ObjNr→OBJEKT, ObjNr→OBJEKT)
UEBERFUEHRUNG ((ObjNr Oben, ObjTNr Oben)→LINIE/FLAECHE,
(ObjNr Unten, ObjTNr Unten)→LINIE/FLAECHE)
PUNKT (ObjNr→OBJEKT, ObjTeilNr, NBZ, X Koord, Y Koord)
LINIE (ObjNr→OBJEKT, ObjTeilNr, ListNr, Richtung, VektorNr→VEKTOR)
FLAECHE (ObjNr→OBJEKT, ObjTeilNr, ListNr, Richtung, Lage, VektorNr→VEKTOR)
VEKTOR (VektoNr, AnfPkt NBZ, AnfPkt X, AnfPkt Y, EndPkt NBZ, EndPkt X,
EndPkt Y, Geometrie)
GEOMETRIE (VektorNr→VEKTOR, PunktNr, NBZ, X Koord, Y Koord)
Alle Koordinatenwerte werden als 6-stellige Zahl gespeichert und beziehen sich immer
auf einen NBZ, der als Zeichenkette dargestellt wird. Objekte, die in verschiedenen
NBZ liegen, können also die gleichen Koordinaten haben. Bundesweit eindeutige Koordinaten enstehen durch das richtige Zusammensetzen der Objektkoordinaten mit dem
NBZ. Z.B. ergeben der NBZ ’12345 6 ’ und das Koordinatenpaar (888888, 999999) das
bundesweit eindeutige Objektkoordinatenpaar (125888888, 346999999). Bei der Übertragung der ATKIS-Daten in die Spatial Cartridge werden vom Programm atkis2sc
alle Koordinaten umgerechnet, da es Objekte gibt, die bis in einen benachbarten NBZ
hineinragen.
Kapitel 3
Oracle8 Spatial Cartridge
Die Oracle Spatial Cartridge ist ein objektrelationaler Aufsatz der Oracle8 Datenbank,
der es ermöglicht, zweidimensionale räumliche Daten zu speichern, zu verarbeiten, zu
analysieren und zu manipulieren. Dafür stehen dem Benutzer eine Reihe an Funktionen
und Prozeduren zur Verfügung. Besonders eignet sich die Spatial Cartridge laut [SC98]
für die Verwaltung von Daten ’Geographischer Informationssysteme’ (GIS).
3.1
Geometrische Datentypen
Die Spatial Cartridge unterstützt drei einfache geometrische Typen:
Punkt
Linienzug
Polygon
Abbildung 3.1: Einfache geometrische Typen[SC98]
Punkte bestehen aus einer einzigen Koordinate. Linienzüge bestehen aus einem
oder mehreren Liniensegmenten die von Koordinatenpaaren definiert werden. Polygone werden ebenfalls durch mehrere Liniensegmente gebildet. Allerdings muß die letzte
Koordinate des letzten Liniensegments mit der ersten Koordinate des ersten Liniensegments übereinstimmen. Linienzüge bilden im Gegensatz zu Polygonen niemals eine
Fläche, auch nicht, wenn sie sich selbst kreuzen.
3.2
Datenmodell
Das Datenmodell der Spatial Cartridge ist hierarchisch aufgebaut. Einfache Typen (Elemente) können zu geometrischen Objekten (Geometrien) zusammengefaßt werden, die
innerhalb einer Schicht (Layer) durch einen eindeutigen Identifier (GID) bestimmt sind.
7
8
KAPITEL 3. ORACLE8 SPATIAL CARTRIDGE
Mit der Hilfe von Schichten können geometrische Objekte nach bestimmten Kriterien
strukturiert werden. So wurde z.B. in dieser Arbeit bei der Transformation der ATKISDaten deren Folienstruktur nahezu eins zu eins in die Spatial Cartridge umgesetzt.
Dabei entsprechen die geometrischen Objekte in den Schichten den Objektteilen der
ATKIS-Daten.
3.3
Datenbankstrukturen
Jede Schicht besteht aus vier Tabellen, die zur Speicherung der Daten sowie deren
Indizierung benötigt werden. Die Tabellennamen setzen sich aus dem Namen der Schicht
(Layer) und ihrer eigenen Bezeichnung zusammen. So besteht z.B. die Schicht ’VEGFL’
(Vegetationsflächen des ATKIS-Datenbestandes) aus den Tabellen VEGFL_SDOLAYER,
VEGFL_SDODIM, VEGFL_SDOGEOM und VEGFL_SDOINDEX.
3.3.1
SDOLAYER-Tabelle
Die SDOLAYER-Tabelle beschreibt die Beschaffenheit der Schicht und beinhaltet nur eine
einzige Zeile.
SDO_ORDCNT
<number>
SDO_LEVEL
<number>
SDO_NUMTILES
<number>
SDO_COORDSYS
<varchar>
Tabelle 3.1: SDOLAYER-Tabelle
• SDO_ORDCNT bestimmt die Anzahl der Ordinaten, die pro Zeile in der SDOGEOMTabelle gespeichert werden können. Dieser Wert sollte so gewählt werden, daß
möglichst keine großen Speicherlöcher in der SDOGEOM-Tabelle entstehen. Dieser Wert hat ansonsten aber keinen bedeutenden Einfluß auf die Erzeugung des
Raumindexes oder die Anfrageoptimierung.
• SDO_LEVEL speichert wie oft der in der SDODIM-Tabelle definierte Raum bei der Erzeugung des Raumindexes geviertelt wird, also in wieviele Kacheln der Raum unterteilt wird. Diese Zahl kann mit Hilfe der SDO_TUNE.ESTIMATE_TILING_LEVEL()
(siehe 4.6) Prozedur der Spatial Cartridge ermittelt werden. Dies ist ein entscheidener Faktor für die Anfrageoptimierung.
• SDO_NUMTILES wird nur bei einer variablen Kachelgröße benötigt.
• SDO_COORDSYS ist optional. Hier kann ein Name für das Koordinatensystem festgelegt werden.
3.3.2
SDODIM-Tabelle
In der SDODIM-Tabelle werden die Dimensionen, die Ausdehnung des Raumes und die
Genauigkeit der Daten festgelegt. In dieser Tabelle stehen meist zwei Zeilen, für jede
Dimension eine.
3.3. DATENBANKSTRUKTUREN
SDO_DIMNUM
<number>
SDO_LB
<number>
SDO_UB
<number>
9
SDO_TOLERANCE
<number>
SDO_DIMNAME
<varchar>
Tabelle 3.2: SDODIM-Tabelle
• SDO_DIMNUM ist der Identifier der Dimension. Der Startwert beträgt eins.
• SDO_LB beschreibt die untere Schranke während
• SDO_UB die obere Schranke aller Ordinaten dieser Dimension darstellt.
Die maximale Ausdehnung der geometrischen Objekte einer Schicht kann mit der
Funktion SDO_TUNE.EXTENT_OF() nach dem Laden der Daten in die SDOGEOMTabelle ermittelt werden. So wird der Raum durch die Werte in SDO_LB und
SDO_UB möglichst eng eingegrenzt.
• SDO_TOLERANCE gibt die Genauigkeit an. Dieser Wert ist der Abstand den zwei
Objekte voneinander haben können um noch als ein Objekt erkannt zu werden.
• SDO_DIMNAME ist der Name der Dimension.
3.3.3
SDOGEOM-Tabelle
In der SDOGEOM-Tabelle werden die geometrischen Objekte inklusive ihrer Koordinaten
abgelegt.
SDO_GID
<number>
SDO_ESEQ
<number>
SDO_ETYPE
<number>
SDO_SEQ
<number>
SDO_X1..N
<number>
SDO_Y1..N
<number>
Tabelle 3.3: SDOGEOM-Tabelle
• SDO_GID ist der eindeutige Identifier für ein geometrisches Objekt.
• SDO_ESEQ ist eine sequenzielle Nummer, die die einzelnen Elemente numeriert.
• SDO_ETYPE beschreibt die Art des Elements. Es gibt die Typen POINT TYPE
= 1, LINESTRING TYPE = 2 und POLYGON TYPE = 3. Setz man den Wert
auf Null wird dieses Element ignoriert.
• SDO_SEQ speichert die Reihenfolge der Zeilen, falls ein Element aufgrund der Anzahl der Ordinaten nicht mehr in einer Zeile gespeichert werden kann.
• SDO_X1..N und SDO_Y1..N sind die Werte der N Koordinaten des Elements. Hier
muß bei einem Zeilenumbruch darauf geachtet werden, daß die erste Koordinate
der neuen Zeile mit der letzten Koordinate der vorherigen Zeile übereinstimmt.
Dadurch werden viele Koordinaten redundant gespeichert.
3.3.4
SDOINDEX-Tabelle
In der SDOINDEX-Tabelle wird der Raumindex abgelegt. Die Größe dieser Tabelle ist
sehr stark abhängig von dem Wert in SDO_LEVEL. Denn für jeden Schnitt eines geometrischen Objekts mit einer Kachel wird bei der Erzeugung des Raumindexes ein Eintrag
vorgenommen.
10
KAPITEL 3. ORACLE8 SPATIAL CARTRIDGE
SDO_GID
<number>
SDO_CODE
<raw>
SDO_MAXCODE
<raw>
SDO_GROUPCODE
<raw>
SDO_META
<raw>
Tabelle 3.4: SDOINDEX-Tabelle
• SDO_GID ist ein Fremdschlüssel auf die SDO_GEOM-Tabelle.
• SDO_CODE stellt den kodierten Identifier einer Kachel (Tile) dar. Die Anzahl der
Bytes dieser Spalte kann durch die Funktion SDO_ADMIN.CODE_SIZE() ermittelt
werden.
• SDO_MAXCODE, SDO_GROUPCODE und SDO_META werden nur beim Gebrauch einer
variablen Kachelgröße benötigt.
3.4. RAUMINDEX
3.4
11
Raumindex
Um die geometrischen Objekte aufgrund ihrer Lage im durch die SDODIM-Tabelle definierten Raum zu indizieren, wird der Raum sukzessive in immer kleiner werdende
Viertel (Kacheln) unterteilt. Kacheln der ersten Stufe bedecken also ein Viertel der
Gesamtfläche, Kacheln der zweiten Stufe ein Sechzehntel usw. In der SDOINDEX-Tabelle
wird dann der Raumindex der geometrischen Objekte gespeichert, genauer gesagt in
welchen Kacheln die Objekte liegen. Wird im gesamten Raum die gleiche Stufe und
damit Kachelgröße verwendet, spricht man von einer konstanten Kachelgröße. Wird
die Unterteilung an einigen Stellen bis zu verschiedenen Stufen fortgeführt, arbeitet
man mit einer variablen Kachelgröße. Oracle weist jedoch explizit daraufhin, daß der
Umgang mit variablen Kachelgrößen nicht für professionelle Anwendungen geeignet ist,
da sich die Implementierung noch in einem eher experimentellen Zustand befindet. In
dieser Studienarbeit wurde deshalb nicht mit variablen Kachelgrößen experimentiert,
sondern stets konstante Kachelgrößen bei der Erzeugung eines Raumindexes verwendet.
Der Raumindex ist in Z-Ordnung sortiert. Dadurch ergeben sich die Kachelbezeichnungen in der folgenden Abbildung.
T0
T1
T102
T12
Q
Kacheln 2. Stufe
T032
P
T200
D
T321 T330
T2
T3
Konstante Kachelgröße
T23
Kachel 1. Stufe
Variable Kachelgröße
Abbildung 3.2: Unterteilung (Tessellation) des Raumes
3.5
Laden von Daten
Es gibt drei Möglichkeiten um Daten von geometrischen Objekten in die SDOGEOMTabelle zu laden. Für kleinere Datenmengen ist es am einfachsten die Prozeduren
SDO_GEOM.INIT_ELEMENT() und SDO_GEOM.ADD_NODES() der Spatial Cartridge zu verwenden, da man hier nicht beachten muß, ob das Objekt eventuell auf mehrere Zeilen
veteilt werden muß, sondern die Prozeduren dieses Problem selbständig lösen. Allerdings nimmt diese Überprüfung anscheinend eine Menge Zeit in Anspruch. So dauerten
erste Experimente mit diesen Prozeduren, bei denen sämtliche Linienobjektteile aus
den ATKIS-Daten (9955 Stück) in eine Linienschicht geladen werden sollten, bis zu 14
Stunden.
Will man einzelne geometrische Objekte zu einer Schicht hinzufügen reichen auch
12
KAPITEL 3. ORACLE8 SPATIAL CARTRIDGE
simple SQL-Insert-Anweisungen. Dabei muß genau darauf geachtet werden, ob Zeilenumbrüche notwendig sind. Ein 10-eckiges Polygon muß z.B. bei einem SDO_ORDCNTWert von 8 auf vier Zeilen verteilt werden:
GID
1
1
1
1
ESEQ
1
1
1
1
ETYPE
3
3
3
3
SEQ
0
1
2
3
O1
X1
X4
X7
X10
O2
Y1
Y4
Y7
Y10
O3
X2
X5
X8
X1
O4
Y2
Y5
Y8
Y1
O5
X3
X6
X9
O6
Y3
Y6
Y9
O7
X4
X7
X10
O8
Y4
Y7
Y10
Abbildung 3.3: Polygon in SDOGEOM-Tabelle
Genauso aufmerksam müssen die Daten für die dritte Möglichkeit, dem Bulk-Loading,
aufbereitet werden. Das erfordert sicherlich eine nicht unerhebliche Vorarbeit. Doch gerade für große Datenmengen und die Automatisierung des Ladeprozesses bedeutet das
eine erhebliche Beschleunigung des Vorgangs. Dazu muß eine Daten- und eine Kontrolldatei für den SQL-Loader erzeugt werden. Zur Aufbereitung der ATKIS-Daten
wurde ein C-Programm entwickelt, das diese Aufgabe erledigt. Dies wird in Kapitel
4.2 beschrieben. So dauerte z.B. das Entladen sämtlicher ATKIS-Objektteile und das
anschließende Laden in eine zuvor erstellte Atkisschicht weniger als 5 Minuten. Dies ist
nur ein Bruchteil der 14 Stunden mit den Spatial Cartridge Funktionen.
3.6
Erzeugung des Raumindex
Erzeugt wird der Raumindex mit den Spatial Cartridge Prozeduren SDO_ADMIN.POPULAT
E_INDEX_FIXED() und SDO_ADMIN.UPADTE_INDEX_FIXED(). Die Indizierung ist stark
abhängig von dem Tiling-Level, der mit der Prozedur SDO_TUNE.ESTIMATE_TILING_
LEVEL() geschätzt werden kann. Dieser Wert ist für die Anfragegeschwindigkeit von
entscheidener Bedeutung. Wird er zu groß gewählt bläht sich die Indextabelle unnötig
auf, da für jeden Schnitt einer Kachel mit einem Objekt ein Eintrag in der Tabelle
erforderlich ist. Ist der Wert zu klein gewählt wird beim Stellen von Anfragen zwar
der Primärfilter beschleunigt der sowieso zeitintensivere Sekundärfilter jedoch erheblich langsamer. Hier muß man also einen guten Kompromiß finden. Folgende Einträge
stehen symbolisch für die Abbildung 3.2:
SDO_GID
P
Q
Q
Q
Q
Q
Q
SDO_CODE
T032
T031
T120
T121
T033
T122
T123
SDO_GID
Q
Q
Q
D
D
D
D
SDO_CODE
T211
T300
T301
T210
T211
T213
T212
Abbildung 3.4: Ausschnitt aus SDOINDEX-Tabelle
3.7. ANFRAGEN
3.7
13
Anfragen
In diesem Abschnitt wird erläutert wie die Strukturen der Spatial Cartridge genutzt
werden können, um Anfragen an räumliche Daten zu stellen. Eine solche Anfrage besteht aus zwei Teilen, dem Primär- und dem Sekundärfilter. Der kostengünstige Primärfilter hat die Funktion den sehr teuren Sekundärfilter vor einem großen Input zu
schützen.
Large
Input
Row
Source
Primary
Filter
Smaller
Row
Source
Secondary
Filter
Exact
Result
Row
Abbildung 3.5: Query Model[SC98]
3.7.1
Primärfilter
Der Primärfilter liefert ein Zwischenergebnis, das vom Sekundärfilter weiterverarbeitet wird. Dieses Zwischenergebnis beinhaltet bereits mindestens alle für die Lösung
der Anfrage in Frage kommenden Objekte. Es werden indexunterstützt alle Objekte
ausgewählt, die die richtigen Kacheln berühren. Bei einer Anfrage der Art ’Suche alle
Straßen mit Bahnübergängen!’ würden durch den Primärfilter alle Straßen-SchienenObjektpaare selektiert, die dieselben Kacheln berühren. Ob die Objekte sich tatsächlich
schneiden wird vom Sekundärfilter ermittelt. In Abblidung 3.2 berühren z.B. die Objekte D und Q dieselbe Kachel T211. Der Primärfilter würde dieses Objektpaar selektieren
und an den Sekundärfilter weiterleiten. Dieser würde dann feststellen, daß die beiden
Objekte disjunkt sind.
select stri.sdo_gid gid_str, schi.sdo_gid gid_sch,
str.objnr str_objnr, sch.objnr sch_objnr
from strasse_sdoindex stri, schiene_sdoindex schi,
strasse_sdogeom str, schiene_sdogeom sch
where stri.sdo_code = schi.sdo_code
and schi.sdo_gid = sch.sdo_gid
and stri.sdo_gid = str.sdo_gid
Abbildung 3.6: Anfrage für Primärfilter
3.7.2
Sekundärfilter
Der Sekundärfilter liefert schließlich das exakte Ergebnis der Anfrage. Er ist sehr kostenintensiv kann aber nun auf einer stark reduzierten Datenmenge arbeiten. Durch den
Sekundärfilter wird mit der Spatial Cartridge Prozedur SDO_GEOM.RELATE() die genaue
14
KAPITEL 3. ORACLE8 SPATIAL CARTRIDGE
Lage der Objektpaare zueinander bestimmt. Folgende Objektbeziehungen können mit
dieser Prozedur erkannt werden: ANYINTERACT, CONTAINS, COVERDBY, COVERS, DISJOINT, EQUAL, INSIDE, OVERLAPBDYDISJOINT, OVERLAPBDYINTERSECT und TOUCH.
select distinct str_objnr as strasse,
sch_objnr as schiene
from <Primärfilter>
where sdo_geom.relate(’STRASSE’, gid_str,
’ANYINTERACT’, ’SCHIENE’, gid_sch)
= ’TRUE’;
Abbildung 3.7: Anfrage für Sekundärfilter
3.8
Fensterfunktionen (Window Functions)
Das SDO_WINDOW-Paket wird nicht automatisch bei der Installation der Spatial Cartridge installiert. Es sollte von einem Benutzer mit Schreibrechten auf der Datenbank eingerichtet werden, z.B. von dem Datenbankbenutzer MDSYS. So kann der Datenbankadministrator die Benutzung der Funktionen besser kontrollieren. Denn nicht jeder Benutzer
sollte Schreibrechte auf die Datenbank besitzen. So wird das Paket installiert:
SQL> @$ORACLE_HOME/md/admin/sdowin.sql
SQL> @$ORACLE_HOME/md/admin/prvtwin.plb
Mit diesem Paket können nun geometrische Objekte in der Datenbank temporär gespeichert, indiziert und mit anderen gespeicherten Objekten verglichen werden (siehe
5.9). Dies geschieht, indem zunächst eine Schicht für das Anfragefenster erzeugt wird.
Anschließend wird dieses Fenster in der Schicht abgelegt. Nun kann das Fenster mit
allen Objekten anderer Schichten verglichen werden. Nach Gebrauch wird die Schicht
wieder gelöscht.
Kapitel 4
Übertragung der ATKIS-Daten
in die Spatial Cartridge
Zur kompletten Installation der ATKIS-Daten steht das Makefile atk2sc.mk zur Verfügung. Die ATKIS-Daten wurden mit dem Programm ’atkis2sc’ für den SQL-Loader
aufbereitet. Die Daten- und Kontrolldateien für die Spatial Cartridge Schichten sollten
vor dem Start des Makefiles in den Verzeichnissen $HOME/ATKIS_SC/DAT/ bzw. $HOME/
ATKIS_SC/CTL/ plaziert werden. Die Log-Dateien werden ins Verzeichnis $HOME/ATKIS_
SC/LOG/ geschrieben. Mit dem Aufruf
make atk2sc.mk alles
werden alle Tabellen erzeugt, Indexe angelegt, Raumindexe generiert und Statistiken
für den Optimierer erstellt. Durch den Befehl
make atk2sc.mk loesche_alles
wird alles wieder gelöscht. Außerdem stehen noch weitere Aufrufparameter zur Verfügung. (siehe A.2).
Im Folgenden werden die Schritte im einzelnen beschrieben, die letztlich zu dem
Aufbau des Makefiles geführt haben.
4.1
Grundsätzliche Überlegungen
Die Stukturierung der ATKIS-Daten durch das Relationenschema und der konzeptionelle Aufbau der Spatial Cartridge bilden die grundlegenden Säulen für die Überlegungen
wie die Übertragung der geometrischen Objekte erfolgen könnte. Im ursprünglichen
Relationenschema werden die Objekte folgendermaßen auf nach inhaltlichen Gesichtspunkten geordnete Folien verteilt:
FOLIE
101
102
103
104
105
TEXT
Baulich gepraegte Flaechen
Siedlungsfreiflaechena
Bauwerke und sonstige Einrichtungen
Strassenverkehr
Schienenverkehr
15
ANZAHL
2433
240
77
2620
103
16KAPITEL 4. ÜBERTRAGUNG DER ATKIS-DATEN IN DIE SPATIAL CARTRIDGE
107
108
109
111
113
117
151
154
155
Schiffsverkehr
Anlagen und Bauwerke fuer
Verkehr, Transport und Kommunikation
Vegetationsflaechen
Wasserflaechen
Einrichtungen und Bauwerke an Gewaessern
Geographische Gebietseinheiten
Politische Grenzen
Gemeindenamen
Wohnplatznamen
3
261
494
140
5
1
6
1
32
In der Spatial Cartridge werden die Objekte der Folien wie folgt auf verschiedene Schichten verteilt:
Schicht
BAUFL
GEBAEUDE
GEOPOLGRZ
NAMEN
SCHIENE
SIEDLFRFL
STRASSE
VEGFL
WASSER
Folien
101
103, 108, 113
117, 151
154, 155
105
102
104
109
107, 111
Ein unbedeutender Nachteil ergibt sich durch die neue Strukturierung. Die Geometrie
von Linienelementen, die im alten Schema mehreren Objekten zugeordnet waren und
damit nur einmal gespeichert werden mußte, wird nun für jedes Objekt gespeichert.
Ist man sich über den konzeptionellen Aufbau der Schichten innerhalb der Spatial
Cartridge im Klaren können nun die Schichten erzeugt werden. Als Muster dient das
Skript ’crlayer.sql’ im Verzeichnis $ORACLE_HOME/md/admin/. Außerdem können mit
dem Programm ’atkis2sc’ die Daten- und Kontrolldateien für den SQL-Loader erstellt
werden. Mit dem Programm können die Daten mehrerer Folien zusammengefaßt und
für das Laden in eine Schicht aufbereitet werden.
4.2
Das Programm ’atkis2sc’
’atkis2sc’ wurde entwickelt, um die Geometrien der Objekte aus dem ursprünglichen
relationalen Schema zu entladen und so aufzubereiten, daß sie mit Hilfe des Oracle
SQL-Loaders in die ATKIS-Schichten der Spatial Cartridge geladen werden können.
Im Programm gibt man an welche Folien in welche Schicht übertragen werden sollen. Mit diesen Informationen erstellt das Programm anschließend für jede Schicht zwei
Dateien. Eine Datei mit dem Namen ’<schichtname>.dat’, die die Daten enthält und
eine Datei namens ’<schichtname>.ctl, die Informationen über die Datei ’<schichtname>.dat’ enthält. Diese beiden Dateien benötigt der Oracle SQL-Loader zum Laden
von Daten. Wie die Daten geladen werden wird in Abschnitt 4.4 beschrieben.
4.2. DAS PROGRAMM ’ATKIS2SC’
17
18KAPITEL 4. ÜBERTRAGUNG DER ATKIS-DATEN IN DIE SPATIAL CARTRIDGE
4.3
Erzeugung der Tabellen SDOLAYER, SDODIM und SDOGEOM
Rem Create the SDOLAYER table
create table schiene_sdolayer
(
sdo_ordcnt
NUMBER(4),
sdo_level
NUMBER(4),
sdo_numtiles NUMBER(4)
);
Rem Create the SDODIM table
create table schiene_sdodim
(
sdo_dimnum
NUMBER(4),
sdo_lb
NUMBER(9),
sdo_ub
NUMBER(9),
sdo_tolerance NUMBER(10,10),
sdo_dimname
VARCHAR2(10)
);
create table schiene_sdogeom
(
objnr
VARCHAR2(7) constraint fk_schiene_obj
references objekt(objnr),
sdo_gid
NUMBER(10),
sdo_eseq
NUMBER(10),
sdo_etype NUMBER(4),
sdo_seq
NUMBER(10),
sdo_x1
NUMBER(9),
sdo_y1
NUMBER(9),
.
.
.
sdo_x16
NUMBER(9),
sdo_y16
NUMBER(9)
);
insert into schiene_sdolayer
(sdo_ordcnt,sdo_level,sdo_numtiles)
values (32,0,null);
insert into schiene_sdodim
(sdo_dimnum, sdo_lb, sdo_ub, sdo_tolerance, sdo_dimname)
values (1, 000000000, 999999999, 0.5, ’X’);
insert into schiene_sdodim
(sdo_dimnum, sdo_lb, sdo_ub, sdo_tolerance, sdo_dimname)
values (2, 000000000, 999999999, 0.5, ’Y’);
create index idx_schiene_gid on schiene_sdogeom(sdo_gid);
Abbildung 4.1: Skript für die Erzeugung der Tabellen SCHIENE_SDOLAYER, SCHIENE_SDODIM und SCHIENE_SDOGEOM
4.4. LADEN DER DATEN
19
Hier wird am Beispiel der ’SCHIENE’-Schicht das weitere Vorgehen beschrieben. Zunächst müssen die Tabellen SCHIENE_SDOLAYER, SCHIENE_SDODIM und SCHIENE_SDOGEOM
erzeugt werden.
Um den Bezug zu den Daten des ursprünglichen Relationenschemas nicht zu verlieren wurde die Tabelle SCHIENE_SDOGEOM um die Spalte OBJNR erweitert, die die Schicht
SCHIENE mit der Tabelle OBJEKT des ursprünglichen Relationenschemas verknüpft. Das
ist notwendig um nicht die Informationen über die Fachdaten der geometrischen Objekte zu verlieren.
Außerdem wird in dem Skript ein Index auf die Spalte SDO_GID angelegt, der später
die Generierung des Raumindex beschleunigt. Z.B dauerte die Generierung des Raumindex der Schicht Straße ohne diesen Index 18:11,11 min mit diesem Index nur 3:22,46
min.
4.4
Laden der Daten
Nun werden mit dem SQL-Loader die Daten in die Tabelle SCHIENE_SDOGEOM geladen.
Folgender Befehl ist auf Betriebssytemebene abzugeben:
:> sqlldr user/password@database CONTROL=schiene.ctl DATA=schiene.dat
Voraussetzung hierfür ist selbstverständlich das vorherige Erstellen der beiden Dateien
’schiene.ctl und schiene.dat mit dem C-Programm ’atkis2sc’ (4.2).
4.5
Ermittlung der maximalen Ausdehnung
Die Prozedur MDSYS.SDO_TUNE.EXTENT_OF() berechnet die maximale Ausdehnung der
Objekte einer Schicht. Bei der Erzeugung der SCHIENE_SDODIM-Tabelle wurden die unteren bzw. oberen Schranken mit den Werten 000000000 und 999999999 initialisiert.
Das würde einem Raum von 1000 mal 1000 Kilometern entsprechen. Der Numerierungsbezirk Hannover ist aber nur ein 10 mal 10 Kilometer großes Gebiet. Durch die
Ermittlung der maximalen Ausdehnung aller Schichten kann der zu betrachtende Raum
also erheblich eingeschränkt werden.
declare
minx number(9);
miny number(9);
maxx number(9);
maxy number(9);
begin
mdsys.sdo_tune.extent_of(’SCHIENE32’, minx, maxx, miny, maxy);
dbms_output.put_line(minx || ’ ’ || maxx || ’ ’ || miny || ’ ’ || maxy);
end;
/
Abbildung 4.2: Skript für Berechnung der maximalen Ausdehnung der Objekte in der
Schicht SCHIENE
20KAPITEL 4. ÜBERTRAGUNG DER ATKIS-DATEN IN DIE SPATIAL CARTRIDGE
Folgende Schranken wurden für alle geometrischen Objekte aller Schichten berechnet:
X_LB: 354175215 untere Schranke der X-Dimension
X_UB: 355574842 obere Schranke der X-Dimension
Y_LB: 579914033 untere Schranke der Y-Dimension
Y_UB: 581071566 obere Schranke der Y-Dimension
Für den Vergleich der Lage von Objekten, die in unterschiedlichen Schichten liegen ist
es notwendig, daß die Dimensionierung des Raumes aller Schichten identisch ist. Daher
werden die berechneten Schranken ein wenig nach unten und oben ab- bzw. aufgerundet, um eventuelle nachträgliche Eintragungen zu ermöglichen, und anschließend in die
SDODIM-Tabellen aller ATKIS-Schichten eingetragen. Der Raum aller Schichten ist nun
auf ein 30 mal 40 Kilometer großes Gebiet eingegrenzt.
update table schiene_sdodim
where sdo_dimname = ’X’;
update table schiene_sdodim
where sdo_dimname = ’X’;
update table schiene_sdodim
where sdo_dimname = ’Y’;
update table schiene_sdodim
where sdo_dimname = ’Y’;
4.6
set sdo_lb = 353000000
set sdo_ub = 356000000
set sdo_lb = 578000000
set sdo_ub = 582000000
Berechnung des Tiling-Levels
Der Tiling-Level, die Anzahl wie oft der Raum bei der Indexgenerierung unterteilt
werden soll, kann mit der Prozedur MDSYS.SDO_TUNE.ESTIMATE_TILING_LEVEL() berechnet werden. Als erster Anhalt sei empfohlen, daß ein durchschnittlich großes Objekt
maximal 8 Kacheln berühren darf. Folgendes Skript berechnet den Tiling-Level für diese
Bedingung.
declare
tiling_level integer;
begin
tiling_level := mdsys.sdo_tune.estimate_tiling_level(’SCHIENE’,
8, ’AVG_GID_EXTENT’);
dbms_output.put_line(’Value is ’ || tiling_level);
end;
/
Abbildung 4.3: Skript für Berechnung des Tiling-Levels der Schicht SCHIENE
Der berechnete Wert wird anschließend in die Spalte SDO_LEVEL der Tabelle SCHIEN
E_SDOLAYER eingetragen. Auch hier ist es sinnvoll die Räume der Schichten in gleich
4.7. ERSTELLEN DER INDEXTABELLE
21
große Kacheln zu unterteilen. Dadurch wird die Gestaltung von Anfragen, die sich auf
unterschiedliche Schichten beziehen, vereinfacht. Nur bei einer extremen Abweichung
des Tiling-Levels einer oder mehrerer Schichten von den anderen sollte dieser beibehalten werden. Für die Schichten der ATKIS-Daten wurde ein Tiling-Level von 8 ermittelt.
Der Raum wird also in 48 = 65536 Kacheln unterteilt. Damit ergibt sich für den im
vorherigen Abschnitt ermittelten Raum eine Kachelgröße von 117 mal 160 Metern.
Der dritte Parameter im Aufruf der Prozedur SDO_TUNE.ESTIMATE_TILING_LEVEL()
ist der Typ der Schätzung (type of estimate). Insgesamt gibt es drei Typen: LAYER_EXT
ENT, ALL_GID_EXTENT und AVG_GID_EXTENT. Der zweite Parameter des Programmaufrufes, die maximale Anzahl der Kacheln, bezieht sich immer auf den Typ der Schätzung.
Wählt man also den Typ LAYER_EXTENT so müßte man für den zweiten Parameter den
Wert 65536 verwenden, um den gleichen Tiling-Level wie im Beispiel zu erhalten, da
der gesamte Raum, also das gesamte Koordinatensystem, in 65536 Kacheln unterteilt
wird. Benutzt man hingegen den Typ ALL_GID_EXTENT, so gibt der zweite Parameter
an in wie viele Kacheln der Raum unterteilt werden soll, der gerade alle Objekte der
Schicht umschließt.
4.7
Erstellen der Indextabelle
Bevor die Tabelle SCHIENE_SDOINDEX erstellt wird kann mit der Prozedur SDO_ADMIN.
SDO_CODE_SIZE() die benötigte Länge der SDO_CODE-Spalten der SCHIENE_SDOINDEXTabelle bestimmt werden. Dies kann eine Menge Plattenplatz sparen, da die Indextabelle unter Umständen sehr groß werden kann.
declare
code_size integer;
begin
code_size := mdsys.sdo_admin.sdo_code_size(’SCHIENE’);
dbms_output.put_line(’Value is ’ || code_size);
end;
/
Abbildung 4.4: Skript für die Berechnung der Länge der Raw-Spalten in der
SCHIENE_SDOINDEX-Tabelle
Für die ATKIS-Daten wurde eine Länge von 12 ermittelt.
22KAPITEL 4. ÜBERTRAGUNG DER ATKIS-DATEN IN DIE SPATIAL CARTRIDGE
Rem Create the SDOINDEX table
create table schiene_sdoindex
(
sdo_gid
NUMBER(10),
sdo_groupcode RAW(12),
sdo_code
RAW(12),
sdo_maxcode
RAW(12),
sdo_meta
RAW(12)
);
create index idx_schiene_igid on schiene_sdoindex(sdo_gid);
create index idx_schiene_icode on schiene_sdoindex(sdo_code);
Abbildung 4.5: Skript für die Erzeugung Tabelle SCHIENE_SDOINDEX
Die Erzeugung der Indexe auf die Spalten SDO_GID und SDO_CODE der Tabelle bewirken eine Verbesserung der Anfragegeschwindigkeit.
4.8
Generierung des Raumindexes
Nun kann mit der Generierung des Raumindexes begonnen werden. Die Prozedur SDO_
ADMIN.UPADTE_INDEX_FIXED() speichert die Indexwerte für ein geometrisches Objekt.
Sie arbeitet als einzelne Transaktion. Um den Aufwand für ein ROLLBACK zu vermindern, wird eine Schleife um die Prozedur gelegt und nach jeweils 50 Einträgen ein
Commit abgesetzt.
4.9. ERSTELLEN DER STATISTIKEN FÜR DEN OPTIMIERER
23
declare
cursor c1 is select distinct sdo_gid from schiene_sdogeom;
gid number;
i number;
begin
i := 0;
for r in c1 loop
begin
gid := r.sdo_gid;
sdo_admin.update_index(’SCHIENE’, gid);
exception when others then
dbms_output.put_line
(’error for gid’ || to_char(gid) || ’: ’ || SQLERRM);
end;
i := i + 1;
if i = 50 then
commit;
i := 0;
end if;
end loop;
commit;
end;
/
Abbildung 4.6: Skript für die Generierung des Raumindex der Schicht SCHIENE
4.9
Erstellen der Statistiken für den Optimierer
Ein weiterer Schritt für die Optimierung von Anfragen ist das Erstellen von Statistiken
für die SDO_GEOM- und SDO_INDEX-Tabellen sowie für sämtliche Indexe.
analyze
analyze
analyze
analyze
analyze
table
index
table
index
index
schiene_sdogeom compute statistics;
idx_schiene_gid compute statistics;
schiene_sdoindex compute statistics;
idx_schiene_igid compute statistics;
idx_schiene_icode compute statistics;
Die Statistiken enthalten Informationen über die Größe und Beschaffenheit der Tabellen und Indexe. Diese werden bei der Anfrageauswertung vom Optimierer genutzt, in
dem er die Kosten für unterschiedliche Möglichkeiten der Auswertung berechnet. Für
die Beispielanfragen aus Kapitel 5 bedeutete dies teilweise eine Verzehnfachung der
Geschwindigkeit.
Kapitel 5
Testanfragen und deren
Optimierung
5.1
Testumgebung
Die Tests wurden durchgeführt mit der Oracle8 Spatial Cartridge unter dem Betriebssystem Unix. Die Testumgebung ist vergleichbar mit der in der Diplomarbeit [Kas97]
verwendeten Umgebung. So kann durchaus ein Vergleich der Spatial Data Option unter
Oracle7 und der Spatial Cartridge unter Oracle8 gezogen werden.
Zur Ausgabe der benötigten Zeit der Beispielanfragen wird für eine SQL*PlusSitzung die Ausgabe mit SET TIMING ON vereinbart. Es ergibt sich dann folgende Darstellung der Zeitangabe: 00:00:00.00 für Stunde:Minute:Sekunde.100tel.
Für den Test wurden exemplarisch 4 Testanfragen aus der Diplomarbeit [Kas97]
ausgewählt, die mit den Funktionen der Spatial Cartridge realisiert werden können.
In den folgenden Abschnitten dieses Kapitels werden die Anfragen erläutert, optimiert
und mit den Ergebnissen aus [Kas97] verglichen.
Die Erläuterungen, die in Abschnitt 5.2 bezüglich des Optimierers gemacht werden
gelten für alle Anfragen des Kapitels 5.
5.2
Welche Landesstraßen überqueren die Ihme?
Die Abbildung 5.1 zeigt eine Anfrage zwischen den Schichten STRASSE und WASSER,
bei dem alle Objektnummern von Landesstraßen selektiert werden, die die Ihme überqueren. Ohne die Erstellung der Indexe auf die Spalten SDO_GID und SDO_CODE der
STRASSE_SDOINDEX- und WASSER_SDOINDEX-Tabellen dauerte die Anfrage 00:04:38.34
Minuten, mit den Indexen 00:00:36.14 Sekunden und nach dem Erstellen der Statistiken 00:00:01.04 Sekunden.
25
26
KAPITEL 5. TESTANFRAGEN UND DEREN OPTIMIERUNG
Plan for query
select distinct s_objnr as strasse, w_objnr
from (
select distinct si.sdo_gid gid_s, wi.sdo_gid gid_w,
s.objnr s_objnr, w.objnr w_objnr,
n2.name name
from strasse_sdoindex si, wasser_sdoindex wi,
strasse_sdogeom s, wasser_sdogeom w, name n1, name n2
where si.sdo_code = wi.sdo_code
and wi.sdo_gid = w.sdo_gid
and si.sdo_gid = s.sdo_gid
and w.objnr = n1.objnr
and s.objnr = n2.objnr
and n1.name like ’Ihme%’
and n2.name like ’L%’
)
where
sdo_geom.relate(’STRASSE’, gid_s, ’ANYINTERACT’, ’WASSER’, gid_w)
= ’TRUE’;
with optimizer goal ALL_ROWS:
SELECT STATEMENT
SORT
HASH JOIN
TABLE ACCESS
NESTED LOOPS
HASH JOIN
HASH JOIN
HASH JOIN
TABLE ACCESS
TABLE ACCESS
TABLE ACCESS
TABLE ACCESS
TABLE ACCESS
INDEX
NULL
UNIQUE
NULL
FULL
NULL
NULL
NULL
NULL
FULL
FULL
FULL
FULL
BY INDEX ROWID
RANGE SCAN
NULL
NULL
NULL
NAME
NULL
NULL
NULL
NULL
NAME
WASSER_SDOGEOM
WASSER_SDOINDEX
STRASSE_SDOINDEX
STRASSE_SDOGEOM
IDX_STRASSE_GID
Abbildung 5.1: Anfrage: Welche Landesstraßen überqueren die Ihme?
Abbildung 5.2 zeigt die gleiche Anfrage mit Anweisungen für den Optimierer. So lieferte die Anfrage nach 00:00:00.34 Sekunden das Ergebnis. Sie wurde nach dem Erstellen
der Statistiken über die Tabellen STRASSE_SDOGEOM, WASSER_SDOGEOM, STRASSE_SDOIN
DEX und WASSER_SDOINDEX sowie über die Indexe IDX_STRASSE_GID, IDX_STRASSE_IG
ID, IDX_STRASSE_ICODE, IDX_WASSER_GID, IDX_WASSER_IGID und IDX_WASSER_ICODE
ausgeführt. Das Ergebnis der Anfrage wird in Abbildung 5.3 dargestellt.
5.2. WELCHE LANDESSTRASSEN ÜBERQUEREN DIE IHME?
Plan for query
select /*+ cost
ordered use_nl(strasse_sdogeom)
index (strasse_sdogeom idx_strasse_gid)
*/
distinct s_objnr as strasse, w_objnr
from (
select /*+ index(a idx_strasse_icode)
index(b idx_wasser_icode)
use_nl(a b)
no_merge
*/
distinct
si.sdo_gid gid_s, wi.sdo_gid gid_w,
s.objnr s_objnr, w.objnr w_objnr,
n2.name name
from strasse_sdoindex si, wasser_sdoindex wi,
strasse_sdogeom s, wasser_sdogeom w, name n1, name n2
where si.sdo_code = wi.sdo_code
and wi.sdo_gid = w.sdo_gid
and si.sdo_gid = s.sdo_gid
and w.objnr = n1.objnr
and s.objnr = n2.objnr
and n1.name like ’Ihme%’
and n2.name like ’L%’
)
where
sdo_geom.relate(’STRASSE’, gid_s, ’ANYINTERACT’, ’WASSER’, gid_w)
= ’TRUE’;
with optimizer goal ALL_ROWS:
SELECT STATEMENT
SORT
VIEW
SORT
HASH JOIN
TABLE ACCESS
HASH JOIN
TABLE ACCESS
HASH JOIN
TABLE ACCESS
HASH JOIN
HASH JOIN
TABLE ACCESS
TABLE ACCESS
TABLE ACCESS
NULL
UNIQUE
NULL
UNIQUE
NULL
FULL
NULL
FULL
NULL
FULL
NULL
NULL
FULL
FULL
FULL
NULL
NULL
NULL
NULL
NULL
NAME
NULL
WASSER_SDOGEOM
NULL
WASSER_SDOINDEX
NULL
NULL
NAME
STRASSE_SDOGEOM
STRASSE_SDOINDEX
Abbildung 5.2: Optimierte Anfrage: Welche Landesstraßen überqueren die Ihme?
27
28
KAPITEL 5. TESTANFRAGEN UND DEREN OPTIMIERUNG
Die Anweisung ’no_merge’ zwingt den Optimierer die distinct-Klausel in der Unteranfrage zu berücksichtigen. Normalerweise würde diese vom Optimierer aus Kostengründen unterdrückt werden. Hier aber ist ein vorheriges Sortieren und Entfernen gleicher Ergebnisse des Primärfilters von Vorteil, da der Sekundärfilter wesentlich teurer
ist als die Sortierung der Ergebnisse des Primärfilters. Die Anweisung ’cost’ bewirkt,
daß die Anfrage nach kostenbasierten Kriterien ausgewertet wird, also die Statistiken für den Optimierer benutzt werden. In der Unteranfrage wird jede Kachel der
STRASSE_SDOINDEX-Tabelle mit jeder Kachel der WASSER_SDOINDEX-Tabelle verglichen.
Dafür muß auf eine der beiden Tabellen ein voller ’Table-Scan’ durchgeführt werden.
Die Anweisung ’use_nl’ sorgt dafür, daß dieser Scan auf der kleineren der beiden Tabellen durchgeführt wird. Die restlichen Anweisungen deuten dem Optimierer explizit
an, die erstellten Indexe zu verwenden.
Mit der Spatial Data Option unter Oracle7 dauerte die Anfrage 00:03:37.88 Minuten.
SQL> @ihme_opt.sql;
STRASSE IHME
------- ------N00E61Y N00E621
Elapsed: 00:00:00.34
Abbildung 5.3: Ergebnis optimierte Anfrage: Welche Landesstraßen überqueren die Ihme?
5.3
Welche Straßen schneiden Schienen?
Analog zu Abschnitt 5.2 zeigt die Abbildung 5.4 eine Anfrage zwischen den Schichten STRASSE und SCHIENE. Es werden hier alle Objektnummern von Straßen und
Schienen selektiert, die sich schneiden. Nach der Indexerstellung dauerte diese Anfrage 00:11:02.64 Minuten und nach dem Erstellen der Statistiken 00:01:04.35 Minute.
Abbildung 5.5 zeigt die Anfrage mit den Anweisungen für den Optimierer und wurde
ebenfalls nach Erstellen der Statistiken ausgeführt. Diese Anfrage dauerte nur noch
00:00:19.23 Sekunden. Das Ergebnis dieser optimierten Anfrage zeigt Abbildung 5.6
Die Auswertung dieser Anfrage mit der Spatial Data Option unter Oracle7 brach
nach längerer Laufzeit ohne Ergebnis ab.
5.3. WELCHE STRASSEN SCHNEIDEN SCHIENEN?
29
Plan for query
select distinct str_objnr as strasse, sch_objnr as schiene
from (
select distinct stri.sdo_gid gid_str, schi.sdo_gid gid_sch,
str.objnr str_objnr, sch.objnr sch_objnr
from strasse_sdoindex stri, schiene_sdoindex schi,
strasse_sdogeom str, schiene_sdogeom sch
where stri.sdo_code = schi.sdo_code
and schi.sdo_gid = sch.sdo_gid
and stri.sdo_gid = str.sdo_gid
)
where
sdo_geom.relate(’STRASSE’, gid_str, ’ANYINTERACT’, ’SCHIENE’, gid_sch)
= ’TRUE’;
with optimizer goal ALL_ROWS:
SELECT STATEMENT
SORT
HASH JOIN
HASH JOIN
HASH JOIN
TABLE ACCESS
TABLE ACCESS
TABLE ACCESS
TABLE ACCESS
NULL
UNIQUE
NULL
NULL
NULL
FULL
FULL
FULL
FULL
NULL
NULL
NULL
NULL
NULL
SCHIENE_SDOINDEX
STRASSE_SDOINDEX
SCHIENE_SDOGEOM
STRASSE_SDOGEOM
Abbildung 5.4: Anfrage: Welche Straßen schneiden Schienen?
30
KAPITEL 5. TESTANFRAGEN UND DEREN OPTIMIERUNG
Plan for query
select /*+ cost
ordered use_nl(strasse_sdogeom)
index (strasse_sdogeom idx_strasse_gid)
*/
distinct str_objnr as strasse, sch_objnr as schiene
from (
select /*+ index(a idx_strasse_icode)
index(b idx_schiene_icode)
use_nl(a b)
no_merge
*/
distinct
stri.sdo_gid gid_str, schi.sdo_gid gid_sch,
str.objnr str_objnr, sch.objnr sch_objnr
from strasse_sdoindex stri, schiene_sdoindex schi,
strasse_sdogeom str, schiene_sdogeom sch
where stri.sdo_code = schi.sdo_code
and schi.sdo_gid = sch.sdo_gid
and stri.sdo_gid = str.sdo_gid
)
where
sdo_geom.relate(’STRASSE’, gid_str, ’ANYINTERACT’, ’SCHIENE’, gid_sch)
= ’TRUE’;
with optimizer goal ALL_ROWS:
SELECT STATEMENT
SORT
VIEW
SORT
HASH JOIN
TABLE ACCESS
HASH JOIN
TABLE ACCESS
HASH JOIN
TABLE ACCESS
TABLE ACCESS
NULL
UNIQUE
NULL
UNIQUE
NULL
FULL
NULL
FULL
NULL
FULL
FULL
NULL
NULL
NULL
NULL
NULL
SCHIENE_SDOGEOM
NULL
STRASSE_SDOGEOM
NULL
SCHIENE_SDOINDEX
STRASSE_SDOINDEX
Abbildung 5.5: Optimierte Anfrage: Welche Straßen schneiden Schienen?
5.4. WELCHE FLÄCHEN BERÜHREN DIE LEINE?
31
SQL> @str_sch_opt.sql;
STRASSE
------N00BPCT
N00BPCT
N00BPCT
N00BPCV
N00BPCV
N00BPCV
SCHIENE
------N00BPCM
N00BPCN
N00BPCO
N00BPCM
N00BPCN
N00BPCO
.
.
.
STRASSE
------N00E7JT
N00E7JT
N00E7JT
N00E7KK
N00E7MW
SCHIENE
------N00E6DU
N00E7K0
N00E7K2
N00E6DU
N00E7K0
219 rows selected.
Elapsed: 00:00:19.23
Abbildung 5.6: Ergebnis optimierte Anfrage: Welche Straßen schneiden Schienen?
5.4
Welche Flächen berühren die Leine?
Die Auswertung dieser Anfrage erfordert unter der Spatial Cartridge die Ausführung
mehrerer Anfragen, da es mehrere Schichten gibt, in denen Flächen gespeichert werden.
Abbildung 5.7 zeigt eine optimierte Anfrage, die nach dem Erstellen der Statistiken
ausgeführt worden ist. Sie dauerte insgesamt 00:00:24.21 Sekunden. Das Ergebnis zeigt
Abbildung 5.8. Ohne Statistiken und ohne die Anweisungen für den Optimierer hatte
diese Anfrage eine Laufzeit von insgesamt 00:34:16.45 Minuten.
In der Spatial Data Option unter Oracle7 lieferte diese Anweisung nach eine Laufzeit
von 00:00:02.32 keine Ergebnistupel.
32
KAPITEL 5. TESTANFRAGEN UND DEREN OPTIMIERUNG
Abbildung 5.7: Optimierte Anfrage: Welche Flächen berühren die Leine?
/*************************************************************************/
/* Leine - Vegetationsflächen
/
/*************************************************************************/
select /*+ cost
ordered use_nl(vegfl_sdogeom)
index (vegfl_sdogeom idx_vegfl_gid)
*/
distinct v_objnr as flaeche, w_objnr as leine
from (
select /*+ index(a idx_vegfl_icode)
index(b idx_wasser_icode)
use_nl(a b)
no_merge
*/
distinct vi.sdo_gid gid_v, wi.sdo_gid gid_w, v.objnr v_objnr,
w.objnr w_objnr
from vegfl_sdoindex vi, wasser_sdoindex wi,
vegfl_sdogeom v, wasser_sdogeom w, name n
where vi.sdo_code = wi.sdo_code
and wi.sdo_gid = w.sdo_gid
and vi.sdo_gid = v.sdo_gid
and w.objnr = n.objnr
and n.name like ’Leine%’
)
where
sdo_geom.relate(’VEGFL’, gid_v, ’TOUCH’, ’WASSER’, gid_w)
= ’TRUE’;
/*************************************************************************/
/* Leine - Bauflächen
/
/*************************************************************************/
select /*+ cost
ordered use_nl(baufl_sdogeom)
index (baufl_sdogeom idx_baufl_gid)
*/
distinct v_objnr as flaeche, w_objnr as leine
from (
select /*+ index(a idx_baufl_icode)
index(b idx_wasser_icode)
use_nl(a b)
no_merge
*/
distinct vi.sdo_gid gid_v, wi.sdo_gid gid_w, v.objnr v_objnr,
w.objnr w_objnr
5.4. WELCHE FLÄCHEN BERÜHREN DIE LEINE?
33
from baufl_sdoindex vi, wasser_sdoindex wi,
baufl_sdogeom v, wasser_sdogeom w, name n
where vi.sdo_code = wi.sdo_code
and wi.sdo_gid = w.sdo_gid
and vi.sdo_gid = v.sdo_gid
and w.objnr = n.objnr
and n.name like ’Leine%’
)
where
sdo_geom.relate(’BAUFL’, gid_v, ’TOUCH’, ’WASSER’, gid_w)
= ’TRUE’;
/*************************************************************************/
/* Leine - Siedlungsfreiflächen
/
/*************************************************************************/
select /*+ cost
ordered use_nl(siedlfrfl_sdogeom)
index (siedlfrfl_sdogeom idx_siedlfrfl_gid)
*/
distinct v_objnr as flaeche, w_objnr as leine
from (
select /*+ index(a idx_siedlfrfl_icode)
index(b idx_wasser_icode)
use_nl(a b)
no_merge
*/
distinct vi.sdo_gid gid_v, wi.sdo_gid gid_w, v.objnr v_objnr,
w.objnr w_objnr
from siedlfrfl_sdoindex vi, wasser_sdoindex wi,
siedlfrfl_sdogeom v, wasser_sdogeom w, name n
where vi.sdo_code = wi.sdo_code
and wi.sdo_gid = w.sdo_gid
and vi.sdo_gid = v.sdo_gid
and w.objnr = n.objnr
and n.name like ’Leine%’
)
where
sdo_geom.relate(’SIEDLFRFL’, gid_v, ’TOUCH’, ’WASSER’, gid_w)
= ’TRUE’;
34
KAPITEL 5. TESTANFRAGEN UND DEREN OPTIMIERUNG
/*************************************************************************/
/* Leine - Wasserflächen
/
/*************************************************************************/
select /*+ cost
ordered use_nl(wasser_sdogeom)
index (wasser_sdogeom idx_wasser_gid)
*/
distinct v_objnr as flaeche, w_objnr as leine
from (
select /*+ index(a idx_wasser_icode)
index(b idx_wasser_icode)
use_nl(a b)
no_merge
*/
distinct vi.sdo_gid gid_v, wi.sdo_gid gid_w, v.objnr v_objnr,
w.objnr w_objnr
from wasser_sdoindex vi, wasser_sdoindex wi,
wasser_sdogeom v, wasser_sdogeom w, name n
where vi.sdo_code = wi.sdo_code
and wi.sdo_gid = w.sdo_gid
and vi.sdo_gid = v.sdo_gid
and w.objnr = n.objnr
and n.name like ’Leine%’
)
where
sdo_geom.relate(’WASSER’, gid_v, ’TOUCH’, gid_w)
= ’TRUE’;
5.5. UM WELCHE BAHNHÖFE LIEGEN VEGETATIONSFLÄCHEN?
35
SQL> @leine_opt.sql;
no rows selected
Elapsed: 00:00:07.26
no rows selected
Elapsed: 00:00:10.68
no rows selected
Elapsed: 00:00:02.22
no rows selected
Elapsed: 00:00:04.05
Abbildung 5.8: Ergebnis Anfrage: Welche Flächen berühren die Leine?
5.5
Um welche Bahnhöfe liegen Vegetationsflächen?
Um die Umgebungen von Objekten mit anderen Objekten vergleichen zu können, müssen diese Umgebungen zunächst mit Hilfe der Window Functions (siehe 3.8) als geometrische Objekte definiert werden und anschließend in einer temporär angelegten Schicht
gespeichert werden. Anschließend können die so erzeugten Umgebungen anhand einer
Anfrage mit Objekten anderer Schichten verglichen werden. Abbildung 5.9 zeigt ein
PL/SQL-Skript, daß diese Aufgaben erledigt. In Abbildung 5.10 wird das Ergebnis der
Anfrage dargestellt.
Da die Anfrage nicht exakt mit der Anfrage aus dem Test der Spatial Data Option
unter Oracle7 übereinstimmt, erübrigt sich ein Vergleich.
36
KAPITEL 5. TESTANFRAGEN UND DEREN OPTIMIERUNG
Abbildung 5.9: Optimierte Anfrage: Um welche Bahnhöfe liegen Vegetationsflächen
(Radius 2km)?
/*************************************************************************/
/* Temporäres Layer wird erzeugt
/
/*************************************************************************/
begin
mdsys.sdo_window.create_window_layer
(’BAHNHOF_UMGEBUNG’, 8, 0,
1, 353000000, 356000000, 0.5, ’X’,
2, 578000000, 582000000, 0.5, ’Y’);
end;
/
/*************************************************************************/
/* Hauptprogramm
/
/*************************************************************************/
declare
gid number;
/*************************************************************************/
/* Cursor um die Objektkoordinaten aller Bahnhöfe zu selektieren
/
/*************************************************************************/
cursor bh_cur is
select objnr, nbz, x_koord, y_koord from objekt
where objart = 3501;
/*************************************************************************/
/* Anfrage: Welche Umgebungen schneiden Vegetationsflächen?
/
/*************************************************************************/
cursor v_cur is
select /*+ cost
ordered use_nl(vegfl_sdogeom)
index (vegfl_sdogeom idx_vegfl_gid)
*/
distinct veg_objnr
from (
select /*+ index(a idx_vegfl_icode)
use_nl(a)
no_merge
*/
distinct vegi.sdo_gid gid_veg, veg.objnr veg_objnr
from
mdsys.bahnhof_umgebung_sdoindex bi,
vegfl_sdoindex vegi,
vegfl_sdogeom veg
where bi.sdo_code = vegi.sdo_code
and vegi.sdo_gid = veg.sdo_gid
and bi.sdo_gid = gid
)
5.5. UM WELCHE BAHNHÖFE LIEGEN VEGETATIONSFLÄCHEN?
37
where
sdo_geom.relate
(’VEGFL’, gid_veg, ’ANYINTERACT’,
’MDSYS.BAHNHOF_UMGEBUNG’, gid)
= ’TRUE’;
/*************************************************************************/
/* Deklarationen für die Definition der Umgebungen
/
/*************************************************************************/
nbz varchar2(8);
b_objnr varchar2(7);
v_objnr varchar2(7);
x number;
y number;
xp1 number; yp1 number;
xp2 number; yp2 number;
xp3 number; yp3 number;
xp4 number; yp4 number;
zaehler integer;
begin
/*************************************************************************/
/* Schleife um Selektion der Bahnhöfe
/
/*************************************************************************/
for b in bh_cur loop
zaehler := 0;
b_objnr := b.objnr;
/*************************************************************************/
/* Umrearationen der Koordinaten (Einbeziehung NBZ)
/
/*************************************************************************/
x := to_number(substr(b.nbz, 1, 2))
* 10 + to_number(substr(b.nbz, 5, 1));
y := to_number(substr(b.nbz, 3, 2))
* 10 + to_number(substr(b.nbz, 7, 1));
x := x * 1000000 + b.x_koord;
y := y * 1000000 + b.y_koord;
xp1
xp2
xp3
xp4
:=
:=
:=
:=
x
x
x
x
+
+
-
200000;
200000;
200000;
200000;
yp1
yp2
yp3
yp4
:=
:=
:=
:=
y
y
y
y
+
+
-
200000;
200000;
200000;
200000;
/*************************************************************************/
/* Speicherung des Objekts Umgebung in temporäres Layer
/
/*************************************************************************/
gid := mdsys.sdo_window.build_window_fixed
(’MDSYS’, ’BAHNHOF_UMGEBUNG’, 3, 8,
xp1, yp1, xp2, yp2, xp3, yp3, xp4, yp4, xp1, yp1);
38
KAPITEL 5. TESTANFRAGEN UND DEREN OPTIMIERUNG
/*************************************************************************/
/* Schleife um Anfrage: Welche Umgebungen schneiden Vegetationsflächen?
/
/*************************************************************************/
for v in v_cur loop
v_objnr := v.veg_objnr;
zaehler := zaehler + 1;
end loop;
/*************************************************************************/
/* Ausgabe
/
/*************************************************************************/
dbms_output.put_line
(’Um Bahnhof ’ || b_objnr || ’ sind ’ ||
to_char(zaehler) || ’ Vegetationsflaechen.’);
end loop;
end;
/
/*************************************************************************/
/* Löschen der Schicht
/
/*************************************************************************/
begin
mdsys.sdo_window.clean_window(’BAHNHOF_UMGEBUNG’);
end;
/
5.5. UM WELCHE BAHNHÖFE LIEGEN VEGETATIONSFLÄCHEN?
39
SQL> @bahnhof_2000m.sql;
Um
Um
Um
Um
Um
Um
Um
Um
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
N00BQ80
N00BQ6T
N00BS2T
N00E5V5
N00E5V7
N00E5V9
N00BQ82
N00E3CZ
sind
sind
sind
sind
sind
sind
sind
sind
114 Vegetationsflaechen.
111 Vegetationsflaechen.
52 Vegetationsflaechen.
86 Vegetationsflaechen.
86 Vegetationsflaechen.
88 Vegetationsflaechen.
123 Vegetationsflaechen.
139 Vegetationsflaechen.
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
Bahnhof
N00E5B3
N00E5BL
N00E5BM
N00E6GP
N00E6FD
N00E6HQ
N00E5DQ
N00E5DR
sind
sind
sind
sind
sind
sind
sind
sind
54 Vegetationsflaechen.
50 Vegetationsflaechen.
52 Vegetationsflaechen.
128 Vegetationsflaechen.
132 Vegetationsflaechen.
131 Vegetationsflaechen.
46 Vegetationsflaechen.
46 Vegetationsflaechen.
.
.
.
Um
Um
Um
Um
Um
Um
Um
Um
89 Bahnhöfe seklected.
PL/SQL procedure successfully completed.
Elapsed: 00:05:47.86
Abbildung 5.10: Ergebnis Anfrage: Um welche Bahnhöfe liegen Vegetationsflächen (Radius 2km)?
Kapitel 6
Abschließende Bewertung
Messen lassen muß sich die Spatial Cartridge in Sachen Funktionalität und Performance
sicherlich in erster Linie mit deren Vorgänger, der Spatial Data Option. Mit der Einführung der Schichtenarchitektur hat Oracle einen neuen Ansatz gewählt, um räumliche
Daten in Form von geometrischen Objekten in einer Datenbank zu speichern und zu
verarbeiten. Im Funktionsumfang sind Funktionen, die die Lagebeziehung von verschiedenen Objekten (z.B. Schnitt, Berührung) bestimmen können, die das Laden von Daten
erleichtern, die die Beschaffenheit der Daten (z.B. max. Ausdehnung) untersuchen und
die einen bei der Optimierung des Systems unterstützen, enthalten. Dies ist sicherlich
ein Vorteil gegenüber der Spatial Data Option von Oracle7. Auch muß man aufgrund
der Testergebisse der Spatial Cartridge einen Performancegewinn bescheinigen.
Leider stehen dem Anwender aber weiterhin keine Funktionen zur Schnittpunktund Abstandberechnung, zur Berechnung der Länge eines Linienzuges, zur Berechnung
des Umfangs eines Polygons, zur Bestimmung der Anzahl der Stützpunkte eines Linienzuges und zum Ändern oder Einfügen von Punkten in ein geometrischen Objekt zur
Verfügung. Das sind alles Dinge die bei Anwendungen von Geoinformationssystemen
zum Funktionsumfang gehören sollten.
Insgesamt macht die Spatial Cartridge einen guten Eindruck. Probleme ergaben sich
nur bei falscher Anwendung von Spatial Cartridge Funktionen. Dann kann es allerdings
passieren, daß man auf eine Auswertung einer Anfrage ewig wartet. Nur mit Hilfe des
Servers kann der Anfrageprozeß gestopt werden.
41
Anhang A
Listings
A.1
Quellcode cr_schiene.sql
/*************************************************************************/
/* Beispielskript für die Erstellung der Schicht Schienenverkehr
/
/*************************************************************************/
Rem Create the SDOLAYER table
create table schiene_sdolayer
(
sdo_ordcnt
NUMBER(4),
sdo_level
NUMBER(4),
sdo_numtiles NUMBER(4)
);
Rem Create the SDODIM table
create table schiene_sdodim
(
sdo_dimnum
NUMBER(4),
sdo_lb
NUMBER(9),
sdo_ub
NUMBER(9),
sdo_tolerance NUMBER(10,10),
sdo_dimname
VARCHAR2(10)
);
Rem Create the SDOGEOM table
create table schiene_sdogeom
(
objnr
VARCHAR2(7) constraint fk_schiene_obj references objekt(objnr),
sdo_gid
NUMBER(10),
sdo_eseq
NUMBER(10),
sdo_etype NUMBER(4),
sdo_seq
NUMBER(10),
sdo_x1
NUMBER(9),
43
44
ANHANG A. LISTINGS
sdo_y1
sdo_x2
sdo_y2
sdo_x3
sdo_y3
sdo_x4
sdo_y4
sdo_x5
sdo_y5
sdo_x6
sdo_y6
sdo_x7
sdo_y7
sdo_x8
sdo_y8
sdo_x9
sdo_y9
sdo_x10
sdo_y10
sdo_x11
sdo_y11
sdo_x12
sdo_y12
sdo_x13
sdo_y13
sdo_x14
sdo_y14
sdo_x15
sdo_y15
sdo_x16
sdo_y16
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9),
NUMBER(9)
);
create index idx_schiene_gid on schiene_sdogeom(sdo_gid);
Rem Create the SDOINDEX table
Rem NOTE: The 50 byte RAW length is for example’s sake. A user
Rem
creating a new layer should call the SDO_CODE_SIZE()
Rem
function to compute the exact length of the RAW columns.
create table schiene_sdoindex
(
sdo_gid
NUMBER(10),
sdo_groupcode RAW(12),
sdo_code
RAW(12),
sdo_maxcode
RAW(12),
sdo_meta
RAW(12)
);
A.2. QUELLCODE ATKIS2SC
Rem
Rem
Rem
Rem
Rem
45
Insert default values into the SDOLAYER and SDODIM tables for
standard 2-D geometric elements expressed as lattitude and longitude
in degrees decimal.
The SDOLAYER table is shown supporting the old 7.3.3 tessellation
method.
insert into schiene_sdolayer
(sdo_ordcnt,sdo_level,sdo_numtiles)
values (32,8,null);
insert into schiene_sdodim
(sdo_dimnum, sdo_lb, sdo_ub, sdo_tolerance, sdo_dimname)
values (1, 353000000, 356000000, 0.5, ’X’);
insert into schiene_sdodim
(sdo_dimnum, sdo_lb, sdo_ub, sdo_tolerance, sdo_dimname)
values (2, 578000000, 582000000, 0.5, ’Y’);
create index idx_schiene_igid on schiene_sdoindex(sdo_gid);
create index idx_schiene_icode on schiene_sdoindex(sdo_code, sdo_gid);
Rem
Rem
A.2
In diesem Layer sind ATKIS-Datenbankobjekte aus den Folien mit der
Foliennummer 105 - Schienenverkehr
Quellcode atkis2sc
/*************************************************************************/
/* Deklarationen
/
/*************************************************************************/
#define BENUTZERNAME "scott@atkis"
#define PASSWORT
"tiger"
/* Präprozessor-Macros zur Stringbehandlung */
#define GET_ARR(o) o.arr
/* Zugriff auf arr-Komponente eines VARCHAR */
#define GET_LEN(o) o.len
/* Zugriff auf len-Komponente eines VARCHAR */
/* arr-Komponente eines VARCHAR in einen C-String konvertieren */
#define MAKEZ(o) GET_ARR(o)[GET_LEN(o)] = ’\0’
/* aus einem C-String in C-String in einer arr-Komponente *
* einen ordentlichen VARCHAR machen
*/
#define C2VARCHAR(o) GET_LEN(o) = strlen((char *)GET_ARR(o))
/* Newline Zeichen em Ende eines Strings entfernen */
#define CHOP(s) if ((s)[0] != ’\0’ && (s)[strlen((s))-1] == ’\n’) \
(s)[strlen((s))-1] = ’\0’
/* Einen VARCHAR durch einen C-String initialisieren */
#define SET_VARCHAR(s,o) strcpy((char *)GET_ARR(o), s); C2VARCHAR(o)
/* Einen C-String an einen VARCHAR hengen */
#define CAT_VARCHAR(s,o) strcat((char *)GET_ARR(o), s); C2VARCHAR(o)
/* Einen VARCHAR durch einen VARCHAR initialisieren */
46
ANHANG A. LISTINGS
#define CPY_VARCHAR(o1,o2) strcpy((char *)GET_ARR(o2), GET_ARR(o1)); C2VARCHAR(o2)
/* Einen VARCHAR mit einem C-String vergleichen */
#define CMP1_VARCHAR(s,o) strcmp((char *)GET_ARR(o), s)
/* Einen VARCHAR mit einem VARCHAR vergleichen */
#define CMP2_VARCHAR(o1,o2) strcmp((char *)GET_ARR(o2), (char *)GET_ARR(o1))
/* Systemheader */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* SQL Communications Area; für Fehlermeldungen */
#include <sqlca.h>
void sql_error(char*);
void login(char* username, char* pw);
void print_folien();
void print_koord(long, long, int);
void xytonbz(const char* nbz, long* x, long* y);
VARCHAR ftext[100]; /* Folienbeschreibung */
int anzobj = 0; /* Anzahl Objekte pro Folie */
int anzfol = 0;
int fno[10]; /* Foliennummer */
int folnr;
int ordcnt = 0; /* Anzahl Ordinaten pro Datensatz (SDO_ORDCNT aus SDOLAYER) */
int anzord = 0; /* Zaehler für Ordinaten */
FILE* fp; /* Filehandler */
VARCHAR ono[7]; /* Objektnummer */
VARCHAR ono_alt[7]; /* Vorgaenger Objektnummer */
VARCHAR tno[3]; /* Objektteilnummer */
VARCHAR tno_alt[3]; /* Vorgaenger Objektteilnummer */
VARCHAR otyp[1]; /* Objekttyp (’P’=Punkt, ’L’=Linie, ’F’=Flaeche) */
int vno; /* Vektornummer */
long sdo_gid = 0; /* Objektidentifier in SDO_GEOM */
int sdo_eseq = -1; /* Teilobjektidentifier in SDO_GEOM */
int sdo_seq = -1; /* Sequenznummer fuer Verteilung eines
Teilobjekts auf mehrere Datensaetze */
VARCHAR nbz[8]; /* NBZ der Koordinate*/
long x = 0; /* X-Koordinate fuer Punktobjekte und Stuetzpunkte von Vektoren */
long y = 0; /* Y-Koordinate fuer Punktobjekte und Stuetzpunkte von Vektoren */
VARCHAR a_nbz[8]; /* NBZ des Anfangspunktes von Vektoren*/
long ax = 0; /* X-Koordinaten von Anfangspunkten von Vektoren */
long ay = 0; /* Y-Koordinaten von Anfangspunkten von Vektoren */
VARCHAR e_nbz[8]; /* NBZ des Endpunktes von Vektoren*/
long ex = 0; /* X-Koordinate von Endpunkten von Vektoren */
long ey = 0; /* Y-Koordinate von Endpunkten von Vektoren */
long x_letzte = 0; /* X-Koordinate des letzten Eintrages in das Datenfile */
A.2. QUELLCODE ATKIS2SC
47
long y_letzte = 0; /* Y-Koordinate des letzten Eintrages in das Datenfile */
int dir = 0; /* Richtung eines Vektors
(0=mit Urhzeigersinn, 1=gegen Uhrzeigersinn) */
VARCHAR geom[2]; /* Geometrie eines Vektors
(’11’=ohne Stuetzpunkte, ’15’=mit Stuetzpunkten) */
char folien_str_gesamt[41];
char folien_str[10][4];
char layer_str[11];
char ord_str[4];
char dat_file[15];
char ctl_file[15];
int anz;
int alle_folien_ok = 1;
int i, j;
/*************************************************************************/
/* Hauptprogramm
/
/*************************************************************************/
int main()
{
login(BENUTZERNAME, PASSWORT); /* Anmelden bei der Datenbank */
print_folien();
for (;;) {
do {
alle_folien_ok = 1;
anzfol = 0;
i = -1; j = 0;
/*************************************************************************/
/* Lesen der Foliennummer(n)
/
/*************************************************************************/
printf("\nFoliennummer(n), durch Leerzeichen getrennt (max 10)!\n> ");
fgets(folien_str_gesamt, 41, stdin);
CHOP(folien_str_gesamt);
if (folien_str_gesamt[0] == ’\0’) exit(0);
do {
i++;
if (folien_str_gesamt[i] != ’ ’) {
if (!isdigit(folien_str_gesamt[i]) && folien_str_gesamt[i] != ’\0’)
alle_folien_ok = 0;
folien_str[anzfol][j] = folien_str_gesamt[i];
j++;
}
else {
folien_str[anzfol][j] = ’\0’;
j = 0;
48
ANHANG A. LISTINGS
anzfol++;
}
} while (folien_str_gesamt[i] != ’\0’);
if (alle_folien_ok)
for (i = 0; i <= anzfol; i++) {
fno[i] = atoi(folien_str[i]);
EXEC SQL SELECT count(*) INTO anz FROM folie WHERE folie = :fno[i];
if (anz < 1) alle_folien_ok = 0;
}
} while (!alle_folien_ok);
/*************************************************************************/
/* Lesen des Namens der Schicht
/
/*************************************************************************/
printf("\nLayername (10-stellig)!\n-> ");
fgets(layer_str, 11, stdin);
CHOP(layer_str);
if (layer_str[0] == ’\0’) exit(0);
strcpy(dat_file, layer_str);
strcpy(ctl_file, layer_str);
strcat(dat_file, ".dat");
strcat(ctl_file, ".ctl");
do {
/*************************************************************************/
/* Lesen des Werts SDO_ORDCNT
/
/*************************************************************************/
printf("\nAnzahl der Ordinaten (= sdo_ordcnt in %s_sdolayer)!\n-> ",
layer_str);
fgets(ord_str, 4, stdin);
sscanf(ord_str, "%d\n", &ordcnt);
CHOP(ord_str);
if (ord_str[0] == ’\0’) exit(0);
} while (ordcnt < 4 || ordcnt > 999);
fp = fopen(dat_file, "w");
/*************************************************************************/
/* Deklaration der Cursor
/
/*************************************************************************/
EXEC SQL DECLARE OBJEKTE CURSOR FOR
SELECT objnr, objtyp
FROM objekt
WHERE folie = :folnr
AND objtyp <> ’K’
ORDER BY objnr;
EXEC SQL DECLARE PUNKTE CURSOR FOR
SELECT objteilnr, nbz, x_koord, y_koord
FROM punkt
WHERE objnr = :ono
A.2. QUELLCODE ATKIS2SC
49
ORDER BY objnr, objteilnr;
EXEC SQL DECLARE LINIEN CURSOR FOR
SELECT l.objteilnr, l.richtung, v.anfpkt_nbz, v.anfpkt_x,
v.anfpkt_y, v.endpkt_nbz, v.endpkt_x, v.endpkt_y,
v.geometrie, v.vektornr
FROM linie l, vektor v
WHERE objnr = :ono
AND l.vektornr = v.vektornr
ORDER BY l.objnr, l.objteilnr, l.listnr;
EXEC SQL DECLARE FLAECHEN CURSOR FOR
SELECT f.objteilnr, f.richtung, v.anfpkt_nbz, v.anfpkt_x,
v.anfpkt_y, v.endpkt_nbz, v.endpkt_x, v.endpkt_y,
v.geometrie, v.vektornr
FROM flaeche f, vektor v
WHERE objnr = :ono
AND f.vektornr = v.vektornr
ORDER BY f.objnr, f.objteilnr, f.listnr;
EXEC SQL DECLARE GEOMETRIEN CURSOR FOR
SELECT nbz, x_koord, y_koord
FROM geometrie
WHERE vektornr = :vno
ORDER BY vektornr, punktnr;
EXEC SQL DECLARE GEOMETRIEN_DESC CURSOR FOR
SELECT nbz, x_koord, y_koord
FROM geometrie
WHERE vektornr = :vno
ORDER BY vektornr, punktnr DESC;
/*************************************************************************/
/* Öffnen der Datendatei
/
/*************************************************************************/
fp = fopen(dat_file, "w");
for (j = 0; j <= anzfol; j++) {
folnr = fno[j];
SET_VARCHAR("9999999", ono_alt);
EXEC SQL OPEN OBJEKTE;
EXEC SQL FETCH OBJEKTE INTO :ono, :otyp;
while (sqlca.sqlcode == 0) {
if (CMP2_VARCHAR(ono, ono_alt)) {
sdo_gid++;
}
/*************************************************************************/
/* Wenn Objekt ein Punkt ist
/
/*************************************************************************/
if (!CMP1_VARCHAR("P", otyp)) {
SET_VARCHAR("999", tno_alt);
EXEC SQL OPEN PUNKTE;
EXEC SQL FETCH PUNKTE INTO :tno, :nbz, :x, :y;
50
ANHANG A. LISTINGS
xytonbz(GET_ARR(nbz), &x, &y);
while (sqlca.sqlcode == 0) {
if (CMP2_VARCHAR(tno, tno_alt)) {
sdo_eseq++;
}
fprintf(fp, "%7s %10d %10d %4d %10d %6d %6d",
GET_ARR(ono), sdo_gid, sdo_eseq, 1, 0, x, y);
CPY_VARCHAR(tno, tno_alt);
EXEC SQL FETCH PUNKTE INTO :tno, :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
for (i = 1; i <= ordcnt-2; i++)
fprintf(fp, "%8s", " ");
fprintf(fp, "\n");
}
sdo_eseq = -1;
EXEC SQL CLOSE PUNKTE;
}
/*************************************************************************/
/* Wenn Objekt ein Linienzug ist
/
/*************************************************************************/
else if (!CMP1_VARCHAR("L", otyp)) {
SET_VARCHAR("999", tno_alt);
EXEC SQL OPEN LINIEN;
EXEC SQL FETCH LINIEN INTO
:tno, :dir, :a_nbz, :ax, :ay,
:e_nbz, :ex, :ey, :geom, :vno;
xytonbz(GET_ARR(a_nbz), &ax, &ay);
xytonbz(GET_ARR(e_nbz), &ex, &ey);
while (sqlca.sqlcode == 0) {
if (CMP2_VARCHAR(tno, tno_alt)) {
sdo_eseq++;
sdo_seq = 0;
anzord = 0;
x_letzte = 0;
y_letzte = 0;
}
/*************************************************************************/
/* Vektor zeigt in richtige Richtung
/
/*************************************************************************/
if (!dir) {
print_koord(ax, ay, 2);
/*************************************************************************/
/* Hat der Vektor Stützpunkte? (11=Ja)
/
/*************************************************************************/
if (CMP1_VARCHAR("11", geom)) {
EXEC SQL OPEN GEOMETRIEN;
EXEC SQL FETCH GEOMETRIEN INTO :nbz, :x, :y;
A.2. QUELLCODE ATKIS2SC
51
xytonbz(GET_ARR(nbz), &x, &y);
while (sqlca.sqlcode == 0) {
print_koord(x, y, 2);
EXEC SQL FETCH GEOMETRIEN INTO :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
}
EXEC SQL CLOSE GEOMETRIEN;
}
print_koord(ex, ey, 2);
}
/*************************************************************************/
/* Vektor zeigt in falsche Richtung
/
/*************************************************************************/
else {
print_koord(ex, ey, 2);
/*************************************************************************/
/* Hat der Vektor Stützpunkte? (11=Ja)
/
/*************************************************************************/
if (CMP1_VARCHAR("11", geom)) {
EXEC SQL OPEN GEOMETRIEN_DESC;
EXEC SQL FETCH GEOMETRIEN_DESC INTO :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
while (sqlca.sqlcode == 0) {
print_koord(x, y, 2);
EXEC SQL FETCH GEOMETRIEN_DESC INTO :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
}
EXEC SQL CLOSE GEOMETRIEN_DESC;
}
print_koord(ax, ay, 2);
}
CPY_VARCHAR(tno, tno_alt);
EXEC SQL FETCH LINIEN INTO
:tno, :dir, :a_nbz, :ax, :ay,
:e_nbz, :ex, :ey, :geom, :vno;
xytonbz(GET_ARR(a_nbz), &ax, &ay);
xytonbz(GET_ARR(e_nbz), &ex, &ey);
if (anzord > 0 &&
(CMP2_VARCHAR(tno, tno_alt) || (sqlca.sqlcode != 0))) {
for (i = 1; i <= ordcnt-anzord; i++)
fprintf(fp, "%8s", " ");
fprintf(fp, "\n");
}
}
sdo_eseq = -1;
EXEC SQL CLOSE LINIEN;
52
ANHANG A. LISTINGS
}
/*************************************************************************/
/* Wenn Objekt eine Flaeche ist
/
/*************************************************************************/
else if (!CMP1_VARCHAR("F", otyp)) {
SET_VARCHAR("999", tno_alt);
EXEC SQL OPEN FLAECHEN;
EXEC SQL FETCH FLAECHEN INTO
:tno, :dir, :a_nbz, :ax, :ay,
:e_nbz, :ex, :ey, :geom, :vno;
xytonbz(GET_ARR(a_nbz), &ax, &ay);
xytonbz(GET_ARR(e_nbz), &ex, &ey);
while (sqlca.sqlcode == 0) {
if (CMP2_VARCHAR(tno, tno_alt)) {
sdo_eseq++;
sdo_seq = 0;
anzord = 0;
x_letzte = 0;
y_letzte = 0;
}
/*************************************************************************/
/* Vektor zeigt in richtige Richtung
/
/*************************************************************************/
if (!dir) {
print_koord(ax, ay, 3);
/*************************************************************************/
/* Hat der Vektor Stützpunkte? (11=Ja)
/
/*************************************************************************/
if (CMP1_VARCHAR("11", geom)) {
EXEC SQL OPEN GEOMETRIEN;
EXEC SQL FETCH GEOMETRIEN INTO :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
while (sqlca.sqlcode == 0) {
print_koord(x, y, 3);
EXEC SQL FETCH GEOMETRIEN INTO :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
}
EXEC SQL CLOSE GEOMETRIEN;
}
print_koord(ex, ey, 3);
}
/*************************************************************************/
/* Vektor zeigt in falsche Richtung
/
/*************************************************************************/
else {
print_koord(ex, ey, 3);
/*************************************************************************/
A.2. QUELLCODE ATKIS2SC
53
/* Hat der Vektor Stützpunkte? (11=Ja)
/
/*************************************************************************/
if (CMP1_VARCHAR("11", geom)) {
EXEC SQL OPEN GEOMETRIEN_DESC;
EXEC SQL FETCH GEOMETRIEN_DESC INTO :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
while (sqlca.sqlcode == 0) {
print_koord(x, y, 3);
EXEC SQL FETCH GEOMETRIEN_DESC INTO :nbz, :x, :y;
xytonbz(GET_ARR(nbz), &x, &y);
}
EXEC SQL CLOSE GEOMETRIEN_DESC;
}
print_koord(ax, ay, 3);
}
CPY_VARCHAR(tno, tno_alt);
EXEC SQL FETCH FLAECHEN INTO
:tno, :dir, :a_nbz, :ax, :ay,
:e_nbz, :ex, :ey, :geom, :vno;
xytonbz(GET_ARR(a_nbz), &ax, &ay);
xytonbz(GET_ARR(e_nbz), &ex, &ey);
if (anzord > 0 &&
(CMP2_VARCHAR(tno, tno_alt) || (sqlca.sqlcode != 0))) {
for (i = 1; i <= ordcnt-anzord; i++)
fprintf(fp, "%8s", " ");
fprintf(fp, "\n");
}
}
sdo_eseq = -1;
EXEC SQL CLOSE FLAECHEN;
}
CPY_VARCHAR(ono, ono_alt);
EXEC SQL FETCH OBJEKTE INTO :ono, :otyp;
}
EXEC SQL CLOSE OBJEKTE;
}
fclose(fp);
/*************************************************************************/
/* Öffnen und Schreiben der Kontrolldatei
/
/*************************************************************************/
fp = fopen(ctl_file, "w");
fprintf(fp, "load data infile %s\n", dat_file);
fprintf(fp, "into table %s_sdogeom\n", layer_str);
fprintf(fp, "(\n");
fprintf(fp, "objnr position (1:7),\n");
fprintf(fp, "sdo_gid position (10:19) integer external,\n");
fprintf(fp, "sdo_eseq position (22:31) integer external,\n");
54
ANHANG A. LISTINGS
fprintf(fp, "sdo_etype position (34:37) integer external,\n");
fprintf(fp, "sdo_seq position (40:49) integer external,\n");
for (i = 0; i <= (ordcnt/2)-1; i++) {
fprintf(fp, "sdo_x%d position (%d:%d),\n", i+1, 52+(i*22), 60+(i*22));
fprintf(fp, "sdo_y%d position (%d:%d)", i+1, 63+(i*22), 71+(i*22));
if (i < (ordcnt/2)-1) fprintf(fp, ",\n");
else fprintf(fp, "\n");
}
fprintf(fp, ")\n");
fclose(fp);
printf("\nNächstes Layer!\n");
print_folien();
sdo_gid = 0;
sdo_eseq = -1;
sdo_seq = -1;
}
EXEC SQL COMMIT WORK RELEASE;
exit(0);
}
/*************************************************************************/
/* Umrechnung der Koordinaten (Einbeziehung des NBZ)
/
/*************************************************************************/
void xytonbz(const char* nbz, long* x, long* y)
{
char xnbz_str[4];
char ynbz_str[4];
long xnbz;
long ynbz;
xnbz_str[0] = nbz[0];
xnbz_str[1] = nbz[1];
xnbz_str[2] = nbz[4];
xnbz_str[3] = ’\0’;
xnbz = 1000000 * atoi(xnbz_str);
*x += xnbz;
ynbz_str[0] = nbz[2];
ynbz_str[1] = nbz[3];
ynbz_str[2] = nbz[6];
ynbz_str[3] = ’\0’;
ynbz = 1000000 * atoi(ynbz_str);
*y += ynbz;
}
/*************************************************************************/
/* Anmelden bei der Datenbank und Fehlerbehandlung setzen
/
/*************************************************************************/
A.2. QUELLCODE ATKIS2SC
55
void login(char *username, char *pw)
{
VARCHAR userid[20];
/* für Benutzeranmeldung: Benutzername */
VARCHAR passwd[20];
/* für Benutzeranmeldung: Passwort */
SET_VARCHAR(username, userid);
/* von username nach userid kopieren */
SET_VARCHAR(pw, passwd);
/* von pw nach passwd kopieren */
/* Fehlerbehandlung setzen */
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
printf("Anmeldung bei Oracle laeuft... ");
fflush(stdout);
/* Ausgabe noch vor dem Newline erzwingen */
EXEC SQL CONNECT :userid IDENTIFIED BY :passwd;
printf("\nAngemeldet bei ORACLE als Benutzer: %s\n", GET_ARR(userid));
printf("\n\nMit diesem Programmen koennen Objekte der ATKIS-Datenbank\n");
printf("’folienweise’ in Form von Daten- und Kontrolldateien fuer den\n");
printf("SQL-Loader aufbereitet werden, um dann in einen ’Layer’ der\n");
printf("Oracle Spatial Data Option geladen werden zu koennen.\n");
}
/*************************************************************************/
/* Ausgabe der Folien auf dem Bildschirm
/
/*************************************************************************/
void print_folien()
{
VARCHAR ftext[100]; /* Folienbeschreibung */
int anzobj = 0; /* Anzahl Objekte pro Folie */
int fno = 0; /* Foliennummer */
printf("\nFolie AnzObj Folienbeschreibung\n\n");
EXEC SQL DECLARE FOLIE CURSOR FOR
SELECT f.folie, f.text, count(distinct o.objnr)
FROM folie f, objekt o
WHERE f.folie = o.folie (+)
GROUP BY f.folie, f.text;
EXEC SQL OPEN FOLIE;
EXEC SQL FETCH FOLIE INTO :fno, :ftext, :anzobj;
while (sqlca.sqlcode == 0) {
MAKEZ(ftext);
printf("%5d %6d %s\n", fno, anzobj, GET_ARR(ftext));
EXEC SQL FETCH FOLIE INTO :fno, :ftext, :anzobj;
}
EXEC SQL CLOSE FOLIE;
}
/*************************************************************************/
/* Schreiben einer Koordinate in die Datendatei
/
/*************************************************************************/
void print_koord(long x_ord, long y_ord, int sdo_etype)
56
ANHANG A. LISTINGS
{
if (x_ord != x_letzte || y_ord != y_letzte) {
if (anzord < 1) {
if (sdo_seq > 0) {
fprintf(fp, "%7s %10d %10d %4d %10d %9d %9d %9d %9d",
GET_ARR(ono), sdo_gid, sdo_eseq, sdo_etype, sdo_seq,
x_letzte, y_letzte, x_ord, y_ord);
anzord = 4;
}
else {
fprintf(fp, "%7s %10d %10d %4d %10d %9d %9d",
GET_ARR(ono), sdo_gid, sdo_eseq, sdo_etype, sdo_seq,
x_ord, y_ord);
anzord = 2;
}
x_letzte = x_ord;
y_letzte = y_ord;
}
else if (anzord < ordcnt - 3) {
fprintf(fp, " %9d %9d", x_ord, y_ord);
anzord += 2;
x_letzte = x_ord;
y_letzte = y_ord;
}
else if (anzord < ordcnt) {
fprintf(fp, " %9d %9d\n", x_ord, y_ord);
sdo_seq++;
anzord = 0;
x_letzte = x_ord;
y_letzte = y_ord;
}
}
}
/*************************************************************************/
/* Fehlerbehandlung: Ausgabe einer Fehlermeldung, Rollback, Programmende /
/*************************************************************************/
void sql_error(char *msg)
{
char err_msg[128];
size_t buf_len;
int msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
buf_len = sizeof (err_msg);
sqlglm(err_msg, &buf_len, &msg_len);
printf("%.*s\n", msg_len, err_msg);
A.3. QUELLCODE ATKIS2SC.MK
57
EXEC SQL ROLLBACK RELEASE;
exit(EXIT_FAILURE);
}
A.3
Quellcode atkis2sc.mk
##########################################################################
#
# Makefile zum Import von ATKIS-Daten in die Oracle8 Spatial Cartridge
#
##########################################################################
# userid/password fuer die Oracle8 ATKIS-Datenbank
SQLUSER=username/password@atkis
# Hauptverzeichnis:
BASE_DIR=/home/grobe/grobe/ATKIS_SC
# DATA-Verzeichnis:
DATA_DIR=$(BASE_DIR)/DAT
# CONTROL-Verzeichnis:
CTL_DIR=$(BASE_DIR)/CTL
# LOG-Verzeichnis:
LOG_DIR=$(BASE_DIR)/LOG
# SQL-Verzeichnis:
SQL_DIR=$(BASE_DIR)/SQL
##########################################################################
#
# Aufrufparameter
#
##########################################################################
alles:
begin
clear
layers index load spatial analyze
the_end
loesche_alles:
clean_sys
the_end
58
ANHANG A. LISTINGS
##########################################################################
begin:
-@clear
-@echo "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
-@echo ""
-@echo "
Import von ATKIS-Daten in die"
-@echo "
Oracle8 Spatial Cartridge"
-@echo ""
-@echo "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
-@echo ""
-@echo ""
layers:
-@sqlplus $(SQLUSER) @$(SQL_DIR)/create_layers.sql
-@sqlplus $(SQLUSER) @commit;
index:
-@sqlplus $(SQLUSER) @$(SQL_DIR)/create_sdogeom_index.sql
-@sqlplus $(SQLUSER) @$(SQL_DIR)/create_sdoindex_index.sql
-@sqlplus $(SQLUSER) @commit;
load:
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/baufl.ctl
DATA=$(DAT_DIR)/baufl.dat
LOG=$(LOG_DIR)/baufl.log
BAD=$(LOG_DIR)/baufl.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/gebaeude.ctl
DATA=$(DAT_DIR)/gebaeude.dat
LOG=$(LOG_DIR)/gebaeude.log
BAD=$(LOG_DIR)/gebaeude.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/geopolgrz.ctl
DATA=$(DAT_DIR)/geopolgrz.dat
LOG=$(LOG_DIR)/geopolgrz.log
BAD=$(LOG_DIR)/geopolgrz.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/namen.ctl
DATA=$(DAT_DIR)/namen.dat
LOG=$(LOG_DIR)/namen.log
BAD=$(LOG_DIR)/namen.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
A.3. QUELLCODE ATKIS2SC.MK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/schiene.ctl
DATA=$(DAT_DIR)/schiene.dat
LOG=$(LOG_DIR)/schiene.log
BAD=$(LOG_DIR)/schiene.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/siedlfrfl.ctl
DATA=$(DAT_DIR)/siedlfrfl.dat
LOG=$(LOG_DIR)/siedlfrfl.log
BAD=$(LOG_DIR)/siedlfrfl.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/strasse.ctl
DATA=$(DAT_DIR)/strasse.dat
LOG=$(LOG_DIR)/strasse.log
BAD=$(LOG_DIR)/strasse.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/vegfl.ctl
DATA=$(DAT_DIR)/vegfl.dat
LOG=$(LOG_DIR)/vegfl.log
BAD=$(LOG_DIR)/vegfl.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlldr USERID=$(SQLUSER) CONTROL=$(CTL_DIR)/wasser.ctl
DATA=$(DAT_DIR)/wasser.dat
LOG=$(LOG_DIR)/wasser.log
BAD=$(LOG_DIR)/wasser.bad
ERRORS=999 SILENT=HEADER,FEEDBACK
-@sqlplus $(SQLUSER) @commit;
spatial:
-@sqlplus $(SQLUSER) @$(SQL_DIR)/create_spatial_index.sql
-@sqlplus $(SQLUSER) @commit;
analyze:
-@sqlplus $(SQLUSER) @$(SQL_DIR)/create_spatial_index
-@sqlplus $(SQLUSER) @commit;
clean_sys:
-splplus $(SQLUSER) @$(SQL_DIR)/drop_layers.sql
-@sqlplus $(SQLUSER) @commit;
-rm -f $(LOG_DIR)/*.*
clear:
-@clear
59
the_end:
-@echo
-@echo
-@echo
-@echo
""
"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
"
The End"
"*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
Abbildungsverzeichnis
2.1
EER–Diagramm zu ATKIS[Koch94] . . . . . . . . . . . . . . . . . . . .
3.1
3.2
3.3
3.4
3.5
3.6
3.7
Einfache geometrische Typen[SC98] . . .
Unterteilung (Tessellation) des Raumes
Polygon in SDOGEOM-Tabelle . . . . . . .
Ausschnitt aus SDOINDEX-Tabelle . . . .
Query Model[SC98] . . . . . . . . . . . .
Anfrage für Primärfilter . . . . . . . . .
Anfrage für Sekundärfilter . . . . . . . .
4.1
Skript für die Erzeugung der Tabellen SCHIENE_SDOLAYER, SCHIENE_SDODIM
und SCHIENE_SDOGEOM . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Skript für Berechnung der maximalen Ausdehnung der Objekte in der
Schicht SCHIENE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Skript für Berechnung des Tiling-Levels der Schicht SCHIENE . . . . . . 20
Skript für die Berechnung der Länge der Raw-Spalten in der SCHIENE_SDOINDEXTabelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Skript für die Erzeugung Tabelle SCHIENE_SDOINDEX . . . . . . . . . . . 22
Skript für die Generierung des Raumindex der Schicht SCHIENE . . . . . 23
4.2
4.3
4.4
4.5
4.6
5.1
5.2
5.3
5.4
5.5
5.6
5.7
5.8
5.9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Anfrage: Welche Landesstraßen überqueren die Ihme? . . . . . . . . . .
Optimierte Anfrage: Welche Landesstraßen überqueren die Ihme? . . . .
Ergebnis optimierte Anfrage: Welche Landesstraßen überqueren die Ihme?
Anfrage: Welche Straßen schneiden Schienen? . . . . . . . . . . . . . . .
Optimierte Anfrage: Welche Straßen schneiden Schienen? . . . . . . . .
Ergebnis optimierte Anfrage: Welche Straßen schneiden Schienen? . . .
Optimierte Anfrage: Welche Flächen berühren die Leine? . . . . . . . .
Ergebnis Anfrage: Welche Flächen berühren die Leine? . . . . . . . . . .
Optimierte Anfrage: Um welche Bahnhöfe liegen Vegetationsflächen (Radius 2km)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.10 Ergebnis Anfrage: Um welche Bahnhöfe liegen Vegetationsflächen (Radius 2km)? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
7
11
12
12
13
13
14
26
27
28
29
30
31
32
35
36
39
Tabellenverzeichnis
3.1
3.2
3.3
3.4
SDOLAYER-Tabelle
SDODIM-Tabelle .
SDOGEOM-Tabelle .
SDOINDEX-Tabelle
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
8
9
9
10
Literaturverzeichnis
[HohPle] U. Hohenstein, V. Pleßler. Oracle8 Effiziente Anwendungsentwicklung
mit objektrelationalen Konzepten, p322-332, dpunkt. verlag.
[Kas97] R. Kasten. Modellierung und Verarbeitung geowissenschaftlicher Daten in relationalen und objekt-relationalen Datenbank-ManagementSystemen, Diplomarbeit, Institut für Informatik, Universität Hannover, Oktober 1997.
[Koch94] Hans Koch. Entwurf und Implementierung eines Informationssystems für ATKIS-Daten, Diplomarbeit, TU Braunschweig, 1994.
[Kor97] K. Korte. Transformation von ATKIS-Daten in eine relationale Datenbank zur Verkehrsplanung, Studienarbeit, Institut für Informatik, Universität Hannover, August 1997.
[Ora] Oracle8 Server Documentation, Release 8.0.5
[SC98] J. Herbert. Oracle8 Saptial Cartridge, User’s Guide and Reference, Release
8.0.5, Oracle Corporation, May 1998.
63
Herunterladen