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