Montag, 25. November 2013

Noch ein Helferlein in SDO_CS - Mapping von Oracle SRID auf EPSG

Beim Blick auf die Funktionen von SDO_CS fällt auch eine Weitere auf, die eine recht häufig gestellte Frage beantwortet: Welche Oracle SRID ist auf welchen EPSG Code ge"mapped"?

Die Funktion MAP_ORACLE_SRID_TO_EPSG gibt die Antwort darauf. Im Unterschied zu GET_EPSG_DATA_VERSION erwartet diese Funktion einen Eingabewert, nämlich die Oracle SRID.

select sdo_cs.map_oracle_srid_to_epsg(8307) from dual;

Das Ergebnis ist 4326.

EPSG-Code Versionen und die Oracle Datenbank Versionen

Auf der Deutschen Oracle Anwenderkonferenz letzte Woche in Nürnberg wurde folgende Frage an mich herangetragen: Welche EPSG Version wird in welcher Oracle DB Version verwendet?

Wie nicht anders zu erwarten, ist diese Information zugänglich über die Objekte, Methoden und Attribute, die zum Package SDO_CS des Nutzers MDSYS gehören.
Die Funktion GET_EPSG_DATA_VERSION zeigt die entsprechende Version an.

select sdo_cs.get_epsg_data_version from dual;


Die DB Version 11.2.0.3 referenziert damit auf die EPSG Version 6.5. Für 12.1.0.1 wird die EPSG Version 7.5 verwendet.

Mittwoch, 20. November 2013

Umrechnung von Adressen in Koordinaten direkt in der Oracle Datenbank

Die Gocoding Engine in der Oracle Datenbank wird mittels PL/SQL über das Package SDO_GCDR, welches dem Nutzer MDSYS gehört, angesprochen.
Neben dem eigentlichen Umrechnen einer Adresszeile in eine Punktgeometrie (SDO_GEOMETRY mit dem Geometrietyp 2001) mittels GEOCODE_AS_GEOMETRY gibt es in diesem Package auch die Funktion GEOCODE.

select SDO_GCDR.GEOCODE(user, SDO_KEYWORDARRAY('Schiffbauergasse 14', '14467 Potsdam'),'DE', 'DEFAULT') from dual;

Sie wird genutzt, um eine zweite wichtige Funktion des Geocoders (neben der Berechnung der Geokoordinate) anzusprechen, nämlich das Verfizieren, Ergänzen und ggf. Korrigieren der Adresse. Der Rückgabewert hier ist ein Objekt vom Typ SDO_GEO_ADDR. Bei einem Match mit dem Referenzdatenbestand erhält man Informationen zum eigentlichen Prozess des Geokodierens wie z.B. die Qualität des Matches sowie die vervollständigten Daten der Adresse aus dem Referenzdatenbestand.

describe SDO_GEO_ADDR

offenbart, welcher Informationsumfang zur Verfügung gestellt werden kann.

Im Falle des oben angeführten Aufrufes erscheint das folgende Ergebnis, welches sich doch ohne Blick in den Oracle Spatial Developer´s Guide nur in einzelnen Teilen verständlich lesen läßt.

MDSYS.SDO_GEO_ADDR(MDSYS.SDO_KEYWORDARRAY(),
 NULL,NULL,NULL,NULL,NULL,
 'Potsdam','BRANDENBURG','DE','14467',
 NULL,NULL,NULL,NULL,NULL,NULL,'F','F',
 NULL,NULL,'L',0,64959047,'??????????B281CP?',4,'DEFAULT',
 13.04793,52.39935,'???11111110??400?')

Mehr Verständlichkeit bringt hier ein PL/SQL-Skript, welches die Array-Werte herauszieht, zuordnet und im Falle von ERRORMESSAGE, MATCH_VECTOR und MATCH_MODE interpretiert.

/* Autor: Albert Godfrind */
/* Quellen: Pro Oracle Spatial for Oracle Database 11g und Oracle Spatial Workshop für Partner */
create or replace procedure format_geo_addr (
  address SDO_GEO_ADDR
)
AS
  type strings is table of varchar2(30);
  match_names strings := strings (
    '?            ',
    '0 (MATCHED)  ',
    '1 (ABSENT)   ',
    '2 (CORRECTED)',
    '3 (IGNORED)  ',
    '4 (SUPPLIED) '
  );
  address_elements strings := strings (
    null,
    null,
    'X Address Point',
    'O POI Name',
    '# House or building number',
    'E Street prefix',
    'N Street base name',
    'U Street suffix',
    'T Street type',
    'S Secondary unit',
    'B Built-up area or city',
    null,
    null,
    '1 Region',
    'C Country',
    'P Postal code',
    'P Postal add-on code'
  );
  element_match varchar2(128);
  element_match_code char(1);

BEGIN
  if address is not null then
    dbms_output.put_line ('- ID                  ' || address.ID);
    dbms_output.put_line ('- ADDRESSLINES');
    if address.addresslines is not null then
      for i in 1..address.addresslines.count() loop
        dbms_output.put_line ('- ADDRESSLINES['||i||']           ' || address.ADDRESSLINES(i));
      end loop;
    end if;
    dbms_output.put_line ('- PLACENAME           ' || address.PLACENAME);
    dbms_output.put_line ('- STREETNAME          ' || address.STREETNAME);
    dbms_output.put_line ('- INTERSECTSTREET     ' || address.INTERSECTSTREET);
    dbms_output.put_line ('- SECUNIT             ' || address.SECUNIT);
    dbms_output.put_line ('- SETTLEMENT          ' || address.SETTLEMENT);
    dbms_output.put_line ('- MUNICIPALITY        ' || address.MUNICIPALITY);
    dbms_output.put_line ('- REGION              ' || address.REGION);
    dbms_output.put_line ('- COUNTRY             ' || address.COUNTRY);
    dbms_output.put_line ('- POSTALCODE          ' || address.POSTALCODE);
    dbms_output.put_line ('- POSTALADDONCODE     ' || address.POSTALADDONCODE);
    dbms_output.put_line ('- FULLPOSTALCODE      ' || address.FULLPOSTALCODE);
    dbms_output.put_line ('- POBOX               ' || address.POBOX);
    dbms_output.put_line ('- HOUSENUMBER         ' || address.HOUSENUMBER);
    dbms_output.put_line ('- BASENAME            ' || address.BASENAME);
    dbms_output.put_line ('- STREETTYPE          ' || address.STREETTYPE);
    dbms_output.put_line ('- STREETTYPEBEFORE    ' || address.STREETTYPEBEFORE);
    dbms_output.put_line ('- STREETTYPEATTACHED  ' || address.STREETTYPEATTACHED);
    dbms_output.put_line ('- STREETPREFIX        ' || address.STREETPREFIX);
    dbms_output.put_line ('- STREETSUFFIX        ' || address.STREETSUFFIX);
    dbms_output.put_line ('- SIDE                ' || address.SIDE);
    dbms_output.put_line ('- PERCENT             ' || address.PERCENT);
    dbms_output.put_line ('- EDGEID              ' || address.EDGEID);
    dbms_output.put_line ('- ERRORMESSAGE        ' || address.ERRORMESSAGE);
    if address.MATCHVECTOR is not null then
      dbms_output.put_line ('- MATCHVECTOR         ' || address.MATCHVECTOR);
      for i in 1..length(address.MATCHVECTOR) loop
        if address_elements(i) is not null then
          if substr (address.matchvector,i,1) = '?' then
            element_match_code := 0;
          else
            element_match_code := substr(address.matchvector,i,1) + 1;
          end if;
          dbms_output.put_line ('-   '|| substr(address.errormessage,i,1)  || ' ' ||
            match_names (element_match_code + 1) || ' ' ||
            address_elements (i)
          );
        end if;
      end loop;
    end if;
    if address.MATCHVECTOR is not null then
      dbms_output.put_line ('- MATCHCODE           ' || address.MATCHCODE || ' = ' ||
        case address.MATCHCODE
          when  0 then 'Ambiguous'
          when  1 then 'Exact match'
          when  2 then 'Street type not matched'
          when  3 then 'House number not matched'
          when  4 then 'Street name not matched'
          when 10 then 'Postal code not matched'
          when 11 then 'City not matched'
        end
      );
    end if;
    dbms_output.put_line ('- MATCHMODE           ' || address.MATCHMODE);
    dbms_output.put_line ('- LONGITUDE           ' || address.LONGITUDE);
    dbms_output.put_line ('- LATITUDE            ' || address.LATITUDE);
  else
    dbms_output.put_line ('**** NO MATCH ****');
  end if;
end;
/
show errors

create or replace procedure format_addr_array (
  address_list SDO_ADDR_ARRAY
)

as
begin
  if address_list is not null and address_list.count() > 0 then
    for i in 1..address_list.count() loop
      dbms_output.put_line ('ADDRESS['||i||']');
      format_geo_addr (address_list(i));
    end loop;
  else
    dbms_output.put_line ('**** NO MATCH ****');
  end if;
end;
/
show errors


Jetzt wird die Funktion GEOCODE noch mal aufgerufen, aber diesmal innerhalb der Formatierungsfunktion.

exec format_geo_addr (SDO_GCDR.GEOCODE(user, SDO_KEYWORDARRAY('Schiffbauergasse 14', '14467 Potsdam'), 'DE', 'DEFAULT'));

Das Ergebnis ist jetzt doch sehr viel besser zu lesen und zu verstehen.
Probiert´s einfach mal aus.

- ID                  0
- ADDRESSLINES
- PLACENAME          
- STREETNAME          Schiffbauergasse
- INTERSECTSTREET    
- SECUNIT            
- SETTLEMENT         
- MUNICIPALITY        Potsdam
- REGION              BRANDENBURG
- COUNTRY             DE
- POSTALCODE          14467
- POSTALADDONCODE    
- FULLPOSTALCODE     
- POBOX              
- HOUSENUMBER         14
- BASENAME            SCHIFFBAUER
- STREETTYPE          GASSE
- STREETTYPEBEFORE    F
- STREETTYPEATTACHED  F
- STREETPREFIX       
- STREETSUFFIX       
- SIDE                R
- PERCENT             .75
- EDGEID              748777426
- ERRORMESSAGE        ????#ENUT?B281CP?
- MATCHVECTOR         ???10101010??400?
-   ? ?             X Address Point
-   ? 1 (ABSENT)    O POI Name
-   # 0 (MATCHED)   # House or building number
-   E 1 (ABSENT)    E Street prefix
-   N 0 (MATCHED)   N Street base name
-   U 1 (ABSENT)    U Street suffix
-   T 0 (MATCHED)   T Street type
-   ? 1 (ABSENT)    S Secondary unit
-   B 0 (MATCHED)   B Built-up area or city
-   1 4 (SUPPLIED)  1 Region
-   C 0 (MATCHED)   C Country
-   P 0 (MATCHED)   P Postal code
-   ? ?             P Postal add-on code
- MATCHCODE           1 = Exact match
- MATCHMODE           DEFAULT
- LONGITUDE           13.0745378367008
- LATITUDE            52.4041174822031


Hinweis: Als Referenzdatenbestand stand mir für diesen Test mit dem Oracle Geocoder  das Datenset von Nokia (NAVTEQ) EU Q312 zur Verfügung.

Donnerstag, 14. November 2013

Rasterdaten inventarisieren und nutzen mit Oracle Spatial - Ein Beispiel von DigitalGlobe

Auf der letzten Oracle Spatial User Conference (OSUC) im Mai 2013 wurde das Image Inventory von DigitalGlobe vorgestellt.
Darin werden immense Bestände an Satellitenbildern über Oracle Spatial auswertbar gemacht. So u.a. zum Zwecke der Bearbeitung von Aufträgen für die Bereitstellung von Rasterdaten(ausschnitten) oder auch um nicht verwertbare Daten (Beispiel: bewölkte Gebiete) herauszufiltern.
Informationen zu dem Projekt finden sich auf den Oracle Technology (OTN) Webseiten für die Oracle Spatial and Graph, ganz genau und zum Nachlesen hier.
Das Projekt ist besonders interessant auch für jene mit speziellem Interesse an der Optimierung räumlicher Anfragen (Stichwort: Spatial Index, SQL Hints) bzw. an der Verarbeitung Verarbeitung von Rasterdaten mittels Map/Reduce im Kontext von Big Data.

Freitag, 6. September 2013

Mal schnell ein Beispieldatenset mit 1 Mio Punkten erzeugen

In Vorbereitung von Tests mit dem neuen Oracle DB 12c Spatial Feature "Vector Performance Accelerator" wollte ich mir ein ausreichend großes Datenset erzeugen.
Die Punktgeometrien zu generieren und in eine SDO_GEOMETRY-Tabelle zu laden, ist an sich keine schwere Übung. Die Frage ist hauptsächlich, wie lange man für das Schreiben der Daten in die Tabelle braucht.
Der herkömmliche Ansatz über ein PL/SQL-Skript (Skript 1) mit  insert into <table> values ... in einer FOR Loop verlangt bei größeren Punktmengen - zumindest in meiner recht beschränkten virtuellen Testumgebung auf dem Laptop - doch einige Geduld.

set serveroutput on

drop table gps_positions purge;
 

create table gps_positions (
  id number,
  position sdo_geometry,
  constraint gps_position_pk primary key (id) enable);


declare
  l_curr_lon   number;
  l_curr_lat   number;
  i number;
  j number;
  id number;
  l_start_time number;
  l_end_time number;
begin
  l_start_time := dbms_utility.get_time;
 
  id := 1;

  -- 100 x 1000 points
  for i in 1 .. 100 loop
    for j in 1 .. 1000 loop
      l_curr_lat := round(dbms_random.value(-122,-74),10);
      l_curr_lon := round(dbms_random.value(24,49),10);
      insert into gps_positions (id, position) values (id, mdsys.sdo_geometry (2001, 4326, sdo_point_type (l_curr_lat, l_curr_lon, null), null, null));
      id := id + 1;
      -- dbms_output.put_line('GPS Point: ' ||id|| '   Lat: ' || l_curr_lat || '   Lon: ' || l_curr_lon);
      commit;
    end loop;
  end loop;
  commit;
 
  l_end_time := dbms_utility.get_time;
  dbms_output.put_line(to_char((l_end_time-l_start_time)/100));
end;
/

(Skript 1)

Ich bin aber gerade nicht geduldig, zumal wenn ich jetzt schon weiß, dass es durchaus mehr als 100K Punkte für die anstehenden Tests sein dürfen.
Also ist es an der Zeit, ein Bulk Insert mittels FORALL in meinem PL/SQL Skript auszuprobieren.
Und hier ist das veränderte Skript zum Erzeugen der Punkte in meiner GPS Tabelle.

set serveroutput on

drop table gps_positions purge;
 

create table gps_positions (
  id number,
  position sdo_geometry,
  constraint gps_position_pk primary key (id) enable);
 
declare
  type t_gps_positions is table of gps_positions%ROWTYPE;
  l_tab t_gps_positions := t_gps_positions();
 

  -- Sample size
  l_size number    := 100000;

  l_start_time number;
  l_end_time number; 

  l_lastid number;
  l_curr_lon number;
  l_curr_lat number;
  l_geom sdo_geometry;
begin

  -- Fetch last id from gps_positions table
  select nvl(max(id),1) + 1 into l_lastid from gps_positions;

  l_start_time := dbms_utility.get_time;
 
  -- Populate sample as collection
  for i in 1 .. l_size LOOP
 
    l_curr_lat := round(dbms_random.value(-122,-74),10);
    l_curr_lon := round(dbms_random.value(24,49),10);
    l_geom := mdsys.sdo_geometry (2001, 4326, sdo_point_type (l_curr_lat, l_curr_lon, null), null, null);
   
    l_tab.extend;
    l_tab(l_tab.last).id := l_lastid;
    l_tab(l_tab.last).position := l_geom;
   
    l_lastid := l_lastid + 1;
   
  end loop;
 
  -- Ingest table with point geometries
  forall i in l_tab.first .. l_tab.last
    insert /*+ APPEND_VALUES */ into gps_positions values l_tab(i);
   
  commit;
 
  l_end_time := dbms_utility.get_time;
  dbms_output.put_line(to_char((l_end_time-l_start_time)/100));

end;
/

(Skript 2)

Das Ganze dauert nur mehr gute 10 Sekunden, was 17x mal schneller war als beim 1. Ansatz.
Einen zusätzlichen Performanceschub zum reinen FORALL bringt hier übrigens noch der APPEND_VALUES Hint im insert.

Es versteht sich von selbst, dass anschließend noch die SDO-Metadaten registriert und der räumliche Index angelegt werden müssen.

Über die eigentlichen Tests mit dem Vector Performance Accelerator wird es dann in meinem nächsten Blogeintrag gehen.

Freitag, 30. August 2013

Oracle Spatial and Graph Sessions auf der Oracle Open World 2013

Die Oracle Open World findet dieses Jahr vom 22.-26. September an gewohnter Stelle in San Francisco statt.
Es mit wieder verschiedene Sessions mit Fokus auf die Oracle Spatial Technologies.
Details zu diesen Sessions sind hier zu finden.

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.