Montag, 29. Juli 2013

NURBS-Kurven in Oracle12c - So geht's

Das heutige Blog-Posting widmet sich einem neuen Spatial-Feature in Oracle12c: den NURBS-Kurven;
diese werden ab Oracle 12.1 als Geometrietyp unterstützt. Die Grundlagen zu NURBS
(Non-Uniform Rational B-Spline) möchte ich im Rahmen dieses Blogs nicht erkläutern; dazu
gibt es eine Reihe guter Literatur und auch online sind eine Menge Informationen verfügbar - als Beispiel
sei der deutschsprachige Wikipedia-Artikel genannt.
Der wesentliche Vorteil einer NURBS-Kurve ist der, dass ein Kurvenverlauf mit wenig
Parametern sehr genau beschrieben werden kann. Dagegen verbraucht eine "Approximation" mit
Stützpunkten und geraden Linien als Verbindung wesentlich mehr Parameter für eine
wesentlich ungenauere Beschreibung der Kurve. Anwendung finden NURBS-Kurven vor allem
im CAD Bereich, aber auch Straßenkurven können mit NURBS-Kurven beschrieben werden.
Das folgende Syntaxbeispiel zeigt, wie eine NURBS-Kurve als SDO_GEOMETRY aufgebaut und
in eine Tabelle gespeichert wird.
insert into nurbs_test values (1, SDO_GEOMETRY(
  2002,                 -- Zweidimensionaler Linienzug
  31468,                -- Koordinatensystem
  NULL,                
  SDO_ELEM_INFO_ARRAY(  
    1, 2, 3             -- 1,2,3 = NURBS-Kurve
  ), 
  SDO_ORDINATE_ARRAY (
    3,                  -- Grad der Kurve (3=Kubisch) "d"
    7,                  -- Es gibt 7 Kontrollpunkte   "m"
    0,   0,   1,        -- 1. Kontrollpunkt
  -50, 100,   1,        -- :
   20, 200,   1, 
   50, 350,   1, 
   80, 200,   1, 
   90, 100,   1, 
   30,   0,   1,        -- 7. Kontrollpunkt
   11,                  -- Der Knotenvektor hat 11 Elemente = d + m + 1
   0,    0,   0,    0,  -- Normalisierter Knotenvektor
   0.25, 0.5, 0.75,     -- Start bei 0 - Ende bei 1
   1,    1,   1,    1   -- Ansteigend 
  )
)
)
/
Zunächst ist eine NURBS-Kurve ein Linienzug, der zwei- oder dreidimensional sein kann;
daher wird der SDO_GTYPE in diesem Fall, wie bei einer geraden Linie, auf 2002 gesetzt.
Der zweite Parameter ist, wie bei anderen Geometrien auch, das Koordinatensystem. Wie bei
den bisher auch schon unterstützten einfachen Kurven (Arc Line Strings) sind geodätische
Koordinatensysteme für NURBS-Kurven nicht zugelassen.
Für eine NURBS-Kurve wird das SDO_ELEM_INFO_ARRAY stets mit 1,2,3 belegt. Das
SDO_ORDINATE_ARRAY besteht aus mehreren Abschnitten:
  • Der erste Wert ist der Grad der Kurve, welcher die verwendete Basisfunktion festlegt. Die Basisfunktion
    beschreibt den Verlauf der NURBS-Kurve.
    • "1" bedeutet, dass eine lineare Funktion verwendet wird - es ergibt sich eine einfache, zusammengesetzte Linie durch die Kontrollpunkte. Dazu braucht man dann eigentlich keine NURBS-Kurve, daher wird eine "1" wohl nur sehr selten verwendet
    • "2" ist eine quadratische Basisfunktion (die NURBS-Kurve entspricht dann einer Parabel)
    • "3" ist eine kubische Basisfunktion - die Kurve wird dann einen S-Verlauf bekommen
    • "5" ergibt eine mehrfach geschwungene Linie
  • Der zweite Wert im SDO_ORDINATE_ARRAY gibt an, wieviele Kontrollpunkte vorliegen. Die Kontrollpunkte
    kann man sich so vorstellen, dass sie gewissermaßen "an der Kurve" ziehen.
    Die NURBS-Kurve wird (außer am Anfang und am Ende) nicht durch die Kontrollpunkte laufen. Jeder Kontrollpunkt
    ist gewichtet, mit der Gewichtung (zwischen 0 und 1) kann man festlegen, wie stark der
    Kontrollpunkt an der Kurve ziehen soll. Gibt man eine 0 an, so wird der Punkt quasi nicht berücksichtigt.


  • Der darauf folgende Wert gibt die Anzahl der Elemente im dann folgenden Knotenvektor - hier
    muss immer der Wert (d + m + 1) stehen - also der Grad der Kurve plus die Anzahl der Kontrollpunkte
    plus eins. Für eine Kurve dritten Grades mit 7 Kontrollpunkten steht dort also eine 11.


  • Der dann folgende Knotenvektor legt den Kurvenverlauf nicht direkt fest (das tun ja schon die Kontrollpunkte),
    sie haben nur indirekt Einfluß. Die absoluten Werte sind ohne Bedeutung, interessant ist allein das
    Verhältnis der Differenzen zueinander. Die Knotenvektoren 0,0,1,2,3,4,4 bedeuten also das
    gleiche wie 1,1,2,3,4,5,5 oder 0,0,0.25,0.50.0.75,1,1. Ein Knotenvektor muss steigend sein.
    Oracle erwartet normalisierte Werte (also zwischen 0 und 1). Ferner müssen die ersten "d+1" Werte gleich 0 und die letzten "d+1" Werte gleich 1 sein (siehe obiges Beispiel).


Für den Umgang mit der NURBS-Kurve gelten die gleichen Regeln für für SDO_GEOMETRY im allgemeinen; der Spatial Index wird erstellt wie immer (der Index löst die NURBS-Kurve übrigens intern in einen "normalen" Linestring auf). Die Validierung einer NURBS-Kurve erfolgt ebenfalls (wie immer) mit SDO_GEOM.VALIDATE_GEOMETRY_WITH_CONTEXT. Würde man in obigem Beispiel also einen Knotenvektor mit 12 Elementen verwenden, so sähe die Validierung der Nurbs-Kurve wie folgt aus:
SQL> select gid, sdo_geom.validate_geometry_with_context(geom, 1) as valid 
  2  from nurbs_test where gid=1

       GID VALID
---------- --------------------
         1 13072 [Element <1>]

1 Zeile wurde ausgewählt.

SQL> exit
 
$ oerr ora 13072
13072, 00000, "incorrect number of knots for SDO_GEOMETRY object"
//     *Cause: An incorrect number of knot values was passed for a Non-Uniform
//             Rational B-Spline curve.
//     *Action: Correct the number of knot values passed for the Non-Uniform
//              Rational B-Spline curve.
Der Oracle MapViewer und Oracle Maps können NURBS-Kurven per Juli 2013 noch nicht darstellen; hier kann die SQL-Funktion SDO_GEOM.GETNURBSAPPROX weiterhelfen - die konvertiert die NURBS-Kurve explizit in einen "klassischen" zusammengesetzten Linienzug. Die folgende Abbildung zeigt die Appriximation der NURBS-Kurve aus dem Beispiel oben - die Knotenpunkte sind ebenfalls hinzugefügt.
SQL> select SDO_UTIL.GetNurbsApprox(geom, 0.05) as geom from nurbs_test where gid = 1;

GEOM
--------------------------------------------------------------------------------
SDO_GEOMETRY(2002, 31468, NULL, SDO_ELEM_INFO_ARRAY(1, 2, 1), SDO_ORDINATE_ARRAY
(0, 0, -2,9128395, 5,96995228, -5,6243745, 11,8211319, -8,1393559, 17,5559751, -
10,462535, 23,1769184, -12,598662, 28,6863981, -14,552488, 34,0868505, -16,32876
4, 39,380712, -17,932241, 44,5704191, -19,36767, 49,6584079, -20,639802, 54,6471
15, -21,753387, 59,5389767, -22,713177, 64,3364292, -23,523922, 69,0419091, -24,
190374, 73,6578527, -24,717284, 78,1866962, -25,109401, 82,6308762, -25,371477,
:
Viel Spaß beim Ausprobieren.

Mittwoch, 10. Juli 2013

Vektordaten mit mehr sehr vielen Stützpunkten speichern

Stützpunkte von Vektorgeometrien werden als SDO_ORDINATE_ARRAY gespeichert. Der dafür im Schema MDSYS definierte Typ ist ein "VARRAY(1048576) OF NUMBER". Das bedeutet, für 2D Geometrien können maximal 1048576 ⁄ 2 und für 3D Geometrien maximal 1048576 ⁄ 3 Stützpunkte gespeichert werden.

Das ist für so manche Geometrie zu wenig, um sie als SDO_GEOMETRY abzulegen.
Daher gibt es einen Workaround, der für die Version 11.2 nun auch im Oracle Spatial and Graph Online Handbuch dokumentiert und als Skript sdoupggeom.sql im Installationspfad der Oracle Datenbank liegt @$ORACLE_HOME/md/admin.
Das Kernstück des Skriptes ist das Überschreiben des Typs mittels:

alter type mdsys.sdo_ordinate_array modify limit 10000000 cascade;

Der Wert 10000000 kann bei Bedarf angepasst werden.
Zusätzlich werden über das Skript Prüfungen (z.B. ob Versionierung für Tabellen mit SDO_GEOMETRY-Spalten eingeschaltet ist) und anschließend ein Recompile der betroffenen Objekte vorgenommen.