Posts mit dem Label srid werden angezeigt. Alle Posts anzeigen
Posts mit dem Label srid werden angezeigt. Alle Posts anzeigen

Donnerstag, 24. April 2014

Performance bei Koordinaten-Transformationen: TRANSFORM vs. TRANSFORM_LAYER

Wenn es darum geht, die Koordinaten in einer SDO_GEOMETRY-Spalte in ein anderes
Koordinatensystem umzurechnen, nutzt man SDO_CS.TRANSFORM - ein Beispiel:
create table m_admin_area4_t31467 as
select
  feature_id,
  feature_name,
  area_id,
  name_langcode,
  feature_type,
  country_code_3,
  sdo_cs.transform(geometry, 31467) as geometry
from m_admin_area4;

Tabelle wurde erstellt.

Abgelaufen: 00:06:14.40
Die Tabelle enthält knapp 14.000 Polygone - die Umrechnung dauerte etwas mehr als
6 Minuten. So weit, so gut. Die Frage wäre allerdings, ob es nicht noch schnellere
Varianten gibt - denn stellt man sich eine Tabelle mit 100.000 Polygonen vor, wären
wir schon bei einer Stunde - und so weiter und so fort.
Es ist nicht überall bekannt, dass es neben SDO_CS.TRANSFORM noch eine weitere
Funktion SDO_CS.TRANSFORM_LAYER gibt, welche eine ganze Tabelle auf einmal
umrechnet. TRANSFORM_LAYER soll lt. Dokumentation dabei effizienter arbeiten als TRANSFORM - dafür
ist der Umgang damit etwas aufwändiger. Testen wir das einmal: Die Tabelle, die
umrechnet werden soll, muss einen Eintrag in der View USER_SDO_GEOM_METADATA
haben - ein Spatial-Index dagegen kann, muss aber nicht vorhanden sein. Zuerst sollte
man dies überprüfen.
select table_name, column_name, srid 
from user_sdo_geom_metadata 
where table_name like 'M_ADMIN%'

TABLE_NAME                       COLUMN_NAME                SRID
-------------------------------- -------------------- ----------
M_ADMIN_AREA0                    GEOMETRY                   8307
M_ADMIN_AREA1                    GEOMETRY                   8307
M_ADMIN_AREA2                    GEOMETRY                   8307
M_ADMIN_AREA3                    GEOMETRY                   8307
M_ADMIN_AREA4                    GEOMETRY                   8307
:                                :                          :
Danach kann SDO_CS.TRANSFORM_LAYER ausgeführt werden.
begin
  sdo_cs.transform_layer('M_ADMIN_AREA4', 'GEOMETRY', 'M_ADMIN_AREA4_DST', 31467);
end;
/

Abgelaufen: 00:01:38.95
Das ging schon wesentlich schneller - aber wenn wir uns die Ergebnistabelle ansehen,
dann bemerken wir schnell, dass wir noch nicht ganz fertig sind.
SQL> desc M_ADMIN_AREA4_DST
 Name                                      Null?    Typ
 ----------------------------------------- -------- ----------------------------
 SDO_ROWID                                          ROWID
 GEOMETRY                                           MDSYS.SDO_GEOMETRY
Denn diese Tabelle enthält die umgerechneten Geometrie-Objekte nebst einer ROWID aus
der Quelltabelle. Wenn man nun also die "finale" Tabelle erzeugen möchte, die
aus genau den gleichen Spalten wie die Quelltabelle besteht, so braucht es einen
weiteren Schritt.
create table m_admin_area4_t31467 as
select  
  q.feature_id,
  q.feature_name,
  q.area_id,
  q.name_langcode,
  q.feature_type,
  q.country_code_3,
  i.geometry
from 
  m_admin_area4 q join m_admin_area4_dst i on (i.sdo_rowid = q.rowid)
/

Tabelle wurde erstellt.

Abgelaufen: 00:00:20.68
Diese 20 Sekunden müssen noch hinzugerechnet werden - denn erst nun ist die
gewünschte Tabelle erstellt. Es ergeben sich also etwa 2 Minuten. Verglichen mit
den 6 Minuten und 14 Sekunden ist das doch eine erhebliche Verbesserung! Der Grund dafür
sind Initialisierungen, die bei SDO_CS.TRANSFORM immer wieder und bei TRANSFORM_LAYER
nur einmal aufgerufen werden. Allerdings ist
dieser Ansatz nicht für on-the-fly Transformationen geeignet, da man stets die
"Zwischentabelle" generieren muss. Aber im Rahmen von Ladeprozessen kann man hiermit
durchaus eine Menge Zeit sparen ...

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.

Dienstag, 21. September 2010

Gauss-Krüger: EPSG oder nicht EPSG: Das ist hier die Frage!

Seit Oracle10g Release 2 unterstützt die Oracle-Datenbank die EPSG-Systematik für Koordinatensysteme. Die Projektion Gauss-Krüger Zone 3 steht damit gleich zweimal bereit:
  • als "klassische" Oracle-SRID 82027
  • im EPSG-Standard als Code 31467
SQL> select srid,cs_name from cs_srs where srid in (31467, 82027)

      SRID CS_NAME
---------- ----------------------------------------
     31467 DHDN / Gauss-Kruger zone 3
     82027 GK Zone 3 (DHDN)
Man könnte auf den Gedanken kommen, dass es nun egal ist, welchen Code man verwendet. Aber das ist es leider nicht. Zwischen dem nach EPSG standardisierten System 31467 und der "Oracle-Version" gibt es Unterschiede ... und die möchte ich in diesem Blog Posting herausarbeiten.
Zunächst nehme ich die Koordinate von Oracle in München (mit dem Oracle-Geocoder, versteht sich) und rechne die nach EPSG:31467 um.
select sdo_cs.transform(
  sdo_geometry(2001, 8307, sdo_point_type(11.536734, 48.1800773, null), null, null), 
  31467
) transformed from dual;

TRANSFORMED
------------------------------------------------------------------------------------
SDO_GEOMETRY(2001, 31467, SDO_POINT_TYPE(3688714,69, 5341125,2, NULL), NULL, NULL)
Dies ist die absolut korrekte Koordinate im Koordinatensystem EPSG:31467. Man könnte aber auf den Gedanken kommen, diese mit der ID 82027 in die Datenbank zu speichern ... wenn's das gleiche Koordinatensystem ist, wäre das ja egal ...
Also rechnen wir die Koordinate wieder in WGS84 zurück und zwar einmal mit der SRID 31467 und einmal mit 82027.
select sdo_cs.transform(
  SDO_GEOMETRY(2001, 31467, SDO_POINT_TYPE(3688714.69, 5341125.2, NULL), NULL, NULL ),
  8307
) transformed from dual;

TRANSFORMED
------------------------------------------------------------------------------------
SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(11,5367341, 48,1800773, NULL), NULL, NULL)

select sdo_cs.transform(
  SDO_GEOMETRY(2001, 82027, SDO_POINT_TYPE(3688714.69, 5341125.2, NULL), NULL, NULL ),
  8307
) from dual;

TRANSFORMED
------------------------------------------------------------------------------------
SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(11,5391267, 48,180154, NULL), NULL, NULL)
Und man sieht, dass es nicht das gleiche ist. Wenn man die Well Known Texts vergleicht, lässt sich auch feststellen, dass es in den Parametern ein paar Unterschiede gibt. Die Abweichung liegt meist im Bereich von ca. 100 bis 200 Metern.
select sdo_geom.sdo_distance(
 sdo_cs.transform(
  SDO_GEOMETRY(2001, 31467, SDO_POINT_TYPE(3688714.69, 5341125.2, NULL), NULL, NULL ),
  8307
 ),
 sdo_cs.transform(
  SDO_GEOMETRY(2001, 82027, SDO_POINT_TYPE(3688714.69, 5341125.2, NULL), NULL, NULL ),
  8307
 ),
 1
) distanz from dual;

DISTANZ
-------------
   178,128988
Und was bedeutet das? Zuerstmal alles kein Problem! Man muss es, wenn man GK3-Koordinaten (bspw. aus Shapefiles) in die Datenbank lädt, nur beachten. Stellt man beim Betrachten der Daten Abweichungen im Bereich von 100 bis 200 Metern fest, sollte man die andere GK3-SRID versuchen. Meistens liegt man mit der neueren EPSG-Nummer 31467 richtig. Aber ich habe auch schon Fälle erlebt, in denen die 82027 genommen werden musste.
Rechnet man eine Geometrie von 82027 nach 31467 um, kommen (folgerichtig) auch nicht die gleichen Koordinaten heraus.
SQL> select sdo_cs.transform(
  2    SDO_GEOMETRY(2001, 82027, SDO_POINT_TYPE(3688714.69, 5341125.2, NULL), NULL, NULL ),
  3    31467
  4  ) TRANSFORMED from dual;

TRANSFORMED
-----------------------------------------------------------------------------------
SDO_GEOMETRY(2001, 31467, SDO_POINT_TYPE(3688892,31, 5341139,61, NULL), NULL, NULL)
Für die anderen in Deutschland gebräuchlichen Gauss-Krüger Projektionen gilt das analog.
SQL> select srid,cs_name from cs_srs where srid in (31466, 82015);

      SRID CS_NAME
---------- ----------------------------------------
     31466 DHDN / Gauss-Kruger zone 2
     82015 GK Zone 2 (DHDN)

SQL> select srid,cs_name from cs_srs where srid in (31468, 82032);

      SRID CS_NAME
---------- ----------------------------------------
     31468 DHDN / Gauss-Kruger zone 4
     82032 GK Zone 4 (DHDN)