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

Freitag, 17. Juli 2015

Affine Transformationen direkt in der Datenbank - nutzbar bereits mit Oracle Locator

In meinem Posting vom 8. September 2014 hatte ich beschrieben, wie mittels einer eigenen PL/SQL-Funktion Offsets zu Koordinatenwerten hinzugefügt werden können.

Es gibt darüberhinaus noch (mindestens) einen weiteren Lösungsansatz, der eine bereits existente Funktion zum Einsatz bringt. Und damit meine ich die Funktion AFFINETRANSFORMS im PL/SQL Package SDO_UTIL. Die Anwendung von AFFINETRANSFORM für die konkrete Aufgabenstellung möchte ich nachfolgend kurz beschreiben.

Hinweis:
Das Package SDO_UTIL ist mit jeder Edition der Oracle Datenbank nutzbar, also Bestandteil von Oracle Locator. (Sehen Sie dazu auch noch mal Tabelle B-1 im Oracle Spatial Developer´s Guide.)

Was ist die Aufgabenstellung?
Für alle Geometrien einer Tabelle soll eine Translation von 100m nach Norden erfolgen.

Lösung mittels SDO_UTIL.AFFINETRANSFORM:

select
  sdo_util.affinetransforms(
  geometry => geometry,
  translation => 'TRUE'  -- Hier soll eine Translation stattfinden
  tx => 0.0,             -- keine Rechtswert-Verschiebung
  ty => 100.0,           -- Hochwert-Verschiebung um 100 m 
  tz => 0.0
  ) as geometry
from geom_tab;

Das ist erst mal ziemlich einfach. Die Tücke steckt wie immer im Detail.
Die Verschiebungswerte tx, ty, tz  sind natürlich im zusammenhang mit dem Koordinatensystem der Geometrien zu sehen. Eine Verschiebung ty = 100.0 für Geometrien in ETRS89 / UTM (hier ist die Einheit Meter) bedeutet natürlich etwas anderes als für WGS84. Für Letzteres sind die Koordinatenwerte in Dezimalgrad angegeben.

Wie kann man möglichst einfach damit umgehen?
Mein Vorschlag dazu ist, vorab in der Datenbank eine Transformation durchzuführen.
Mit SQL ausgedrückt, sieht das dann so aus:

select
  sdo_util.affinetransforms(
    geometry => geometry,
    translation => 'TRUE', -- Hier soll eine Translation stattfinden
    tx => 0.0,             -- keine Rechtswert-Verschiebung
    ty => 100.0,           -- Hochwert-Verschiebung um 100 m 
    tz => 0.0
  )
from (                     -- Subquery in FROM-Klausel für Transformation
  select
    sdo_cs.transform(geometry, 25833) geometry
  from
    geom_tab_wgs84);

Visuell stellt sich das Ergebnis bezogen auf eine einzelne Geometrie so dar:

[Abb. 1: Original-Geometrie gelb, verschobene Geometrie orange eingefärbt.]

SDO_UTIL.AFFINETRANSFORMATIONS kann übrigens nicht nur für Translationen verwendet werden. Möglich sind u.a. auch Skalierungen, Spiegelungen oder Rotationen.

Für Rotationen füge ich gleich auch noch ein Beipiel hinzu. Das basiert auf einer sehr einfachen Geometrie, einem Rechteck, welches im unteren linken Stützpunkt um 45 gedreht wird.
Hinweis:
Der Rotationswert (Parameter ANGLE) wird als Bogenmass angegeben. Der Einfachheit halber nutze ich die Funktion CONVERT_UNIT im Paclage SDO_UTIL, um mit Werten in Grad zu arbeiten.
-- Einfache Geometrie
select 
 sdo_geometry (
   2003,
   8307,
   null,
   sdo_elem_info_array (1,1003,1),
   sdo_ordinate_array (2,0,2,5,0,5,0,0,2,0))
from dual;
-- Einfache Geometrie gedreht um 45 Grad bezogen auf den 1. Stützpunkt
select sdo_util.affinetransforms(
  geometry => sdo_geometry (
                2003,
                8307,
                null,
                sdo_elem_info_array (1,1003,1),
                sdo_ordinate_array (2,0,2,5,0,5,0,0,2,0)),
  rotation => 'TRUE',
  p1 => sdo_geometry (2001,8307,sdo_point_type(2,0,null),null,null),
  line1 => NULL,
  angle => sdo_util.convert_unit(45, 'Degree', 'Radian'),  -- Bogenmass
  -- Bzw. für Drehung im Uhrzeigersinn:
  -- angle => sdo_util.convert_unit(-45, 'Degree', 'Radian')   
  dir => -1                                                -- Default
) from dual;
Visuelle Darstellung der Ergebnisse der SQL-Abfragen (Geometrien):
 [Abb. 2: Einfache Geometrie - Orignal]
 [Abb. 3: Einfache Geometrie - um 45 Grad gedreht]

Die gedrehte Geometrie als SDO_GEOMETRY-Objekt - mit Anfangs- und Endstützpunkt x=2, y=0:
MDSYS.SDO_GEOMETRY(
 2003,
  8307,
  NULL,
  MDSYS.SDO_ELEM_INFO_ARRAY(1,1003,1),
  MDSYS.SDO_ORDINATE_ARRAY(
    2,0,
    -1.53553390593274,3.53553390593274,
    -2.94974746830583,2.12132034355964,
    0.585786437626905,-1.41421356237309,
    2,0)) 

Montag, 8. September 2014

Hoch- und Rechtswert von Geometrien verschieben

Heute erhielt ich die Frage, wie man mittels SQL die Hochwerte von Geometrien z.B. um 100 Meter erhöhen kann.
Die Antwort darauf ist relativ einfach: z.B. mit einer Funktion.
Für die Funktion habe ich mit folgenden Prämissen gearbeitet:
  • Bei den Geometrietypen handelt sich um Linien oder Polygone.
  • Die Geometrien sind zweidimensional.
  • Die Masseinheit für die Verschiebung ist "m".
Der Funktion gebe ich 3 Parameter mit:
  1. Ausgangsgeometrie
  2. Offset für den Rechtswert
  3. Offset für den Hochwert
Der Default für die beiden Offset-Werte ist jeweils 0.
Damit sieht die Funktion dann wie folgt aus:
create or replace function geom_add_offset(
  p_geom sdo_geometry,
  p_offset_east number default 0,               -- Offset für den Rechtswert in Metern
  p_offset_north number default 0               -- Offset für den Hochwert in Metern
) return sdo_geometry deterministic is
  l_geom sdo_geometry := p_geom ;
begin
  for i in 1..p_geom.sdo_ordinates.count loop
    if mod(i,2) = 0 then
    -- Hochwert: 2. Wert in einem Koordinatenpaar
      l_geom.sdo_ordinates(i) := l_geom.sdo_ordinates(i) + p_offset_north;
   else
    -- Hochwert: 1. Wert in einem Koordinatenpaar
      l_geom.sdo_ordinates(i) := l_geom.sdo_ordinates(i) + p_offset_east;
    end if; 
  end loop;
  return l_geom;
end geom_add_offset;
/
Diese Funktion kann ich jetzt in jedes beliebige SQL-Statement einbauen.
Beispielhaft lege ich eine neue Tabelle, welche den Hochwert aller Geometrien um 100 erhöht.
create table geom_tab_with_offset as
select
  id,
  name,
  geom_add_offset(geometry, 0, 100) as geometry
from geom_tab;
Visuell dargestellt sieht das für ein Beispiel-Polygon dann ausschnittsweise so aus:
Mit recht wenig Aufwand lässt sich also die gewünschte Verschiebung direkt mittels SQL in der Datenbank vornehmen.