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, 29. Juni 2015

"Call for Speakers" für den Spatial Summit @ BIWA 2016

Der "Call for Speakers" zum Oracle Spatial Summit auf der Oracle Big Data + Analytics User Conference 2016  ist da. Gesucht werden innovative und interessante Projekte rund um Oracle Spatial and Graph sowie Oracle Fusion Middleware MapViewer. Gern auch im Umfeld von Data Warehouse, Business Intelligence, Analytics, Big Data, Cloud, IoT.
Details zur Konferenz gibt es hier. Und auch Abstracts können bereits eingericht werden. Dazu finden Sie ganz unten auf dieser Seite einen Link auf das Einreichungsformular.

Wer noch mal nachschauen möchte, welche Spatial-/MapViewer-Beiträge es auf dem BIWA Summit im Januar 2015 gab, wird hier fündig.

Montag, 18. Mai 2015

Oracle Big Data Spatial and Graph ist draussen

Seit letzter Woche gibt es ein neues Produkt: Oracle Big Data Spatial and Graph.

Hier ist eine kurze Zusammenfassung (in Englisch) über die 2 wesentlichen Komponenten des neuen Produkts:
  1. A distributed property graph database with 35 built-in graph analytics to
    • Discover graph patterns in big data, such as communities and influencers within a social graph
    • Generate recommendations based on interests, profiles, and past behaviors
  2. A wide range of spatial analysis functions and services to
    • Evaluate data based on how near or far something is to one another, or whether something falls within a boundary or region
    • Process and visualize geospatial map data and imagery
Mehr dazu hat Jean Ihm auf dem neuen "Oracle Big Data Spatial and Graph" Blog veröffentlicht. Und wie immer gibt es als Einstieg natürlich auch die Webseiten zum Produkt im Oracle Technologie Network.

Das "Big Picture" zum Oracle Big Data Management sieht jetzt also so aus:


Donnerstag, 23. April 2015

PolygonToLine: Geht das auch umgekehrt?

Im PL/SQL Package SDO_UTIL findet sich die Funktion POLYGONTOLINE, welche ein Polygon in eine Linie konvertiert.
Für den umgekehrten Vorgang, eine Linie in ein Polygon umzuwandeln, gibt es (noch) keine solche Funktion im Package. Allerdings kann ich die gerade ganz gut brauchen. Denn für ein Set an Isolinien (SDO_GEOMETRY mit GTYPE=2002) sollen die abgedeckten Flächen berechnet werden. Flächenberechnungen mit SDO_GEOM.AREA sind aber nur für Polygone (inklusive Polygone mit Löchern) erlaubt.
Also schreiben wir uns selbst eine Funktion. Das ist auch nicht weiter schwierig, wie Ihr weiter unten sehen könnt.
Der besseren Lesbarkeit halber habe ich die wichtigsten Erläuterungen als Kommentar in den Code eingefügt.

Viel Spass beim Ausprobieren !

create or replace function line_to_polygon (
  p_line_geom sdo_geometry)
return sdo_geometry deterministic
is 
  p_polygon_geom sdo_geometry;
  k number;
begin
  /* 
   * Geometrietyp prüfen: Muss gleich 2002 für einfache 2D Linien-Geometrie sein.
   * Doku: http://docs.oracle.com/cd/B28359_01/appdev.111/b28400/sdo_objrelschema.htm#g1013735 
   */
  if p_line_geom.sdo_gtype <> 2002 then
    raise_application_error (-20001, 'Geometrie ist keine einfache 2D Linie.  GTYPE <> 2002.');
  end if;

  p_polygon_geom := p_line_geom;

  -- GTYPE neu setzen für einfache 2D Polygon-Geometrie.
  p_polygon_geom.sdo_gtype := 2003;

  /* 
   * ELEMENT_INFO_ARRAY neu setzen.
   * Doku: http://docs.oracle.com/cd/B28359_01/appdev.111/b28400/sdo_objrelschema.htm#SPATL494 
   */
  
  p_polygon_geom.sdo_elem_info := sdo_elem_info_array (1, 1003, 1);

  -- Anzahl der Stützpunkte bestimmen
  k := p_polygon_geom.sdo_ordinates.count;

  /* 
   * Polygon muss geschlossen werden. 
   * Daher prüfen, ob letzter Stützpunkt der Linie gleich dem 1. Stützpunkt ist. 
   * Wenn nicht, neuen Stützpunkt einfügen (SDO_ORDINATE_ARRAY wird erweitert um 2 Werte). 
   */

  if (p_polygon_geom.sdo_ordinates(k-1) <> p_polygon_geom.sdo_ordinates(1)
    or p_polygon_geom.sdo_ordinates(k) <> p_polygon_geom.sdo_ordinates(2))
  then
    p_polygon_geom.sdo_ordinates.extend(2);
    p_polygon_geom.sdo_ordinates(k+1) := p_polygon_geom.sdo_ordinates(1);
    p_polygon_geom.sdo_ordinates(k+2) := p_polygon_geom.sdo_ordinates(2);
  end if;
  
  -- Jetzt nur noch die Polygon-Geometrie zurückgeben
  return p_polygon_geom;
end line_to_polygon;
/
sho err
Diejenigen unter Euch, welche Anfang des Jahres im Oracle Spatial Workshop mit Albert Godfrind waren, finden die Funktion auch im Labs/07-processing-Ordner.

Freitag, 17. April 2015

Welche Geometrietypen sind in einer SDO_GEOMETRY-Tabelle zu finden?

Dieser Tipp fällt wieder einmal in die Kategorie: Lauter kleine Helferlein.

Fragen Sie sich auch manchmal, welche Geometrietypen eigentlich in einer Tabelle so alles zusammengefaßt sind?
Ohne groß weiter nachzudenken, ist ein Ansatz:
  select distinct g.geom.sdo_gtype
    from geom_tab g
order by 1;
Im Ergebnis erhalte ich die numerische Kodierung der verwendeten Geometrietypen.

Das Ganze könnte ich zum Beispiel mit DECODE nun noch so aufbereiten, dass auch nicht mit dieser Kodierung Vertraute den Inhalt verstehen.

Alternativ - und das ist an dieser Stelle meine Empfehlung - kann ich aber auch die Funktion MIX_INFO im SDO_TUNE-Package benutzen.
execute sdo_tune.mix_info('GEOM_TAB','GEOM');
Die gibt mir die Geometrietypen nicht nur in lesbarer Form zurück, sondern zusätzlich auch gleich noch:
  • die Gesamtzahl der Geometrien,
  • die Anzahl an Geometrien pro Geometrietyp sowie
  • die prozentuale Verteilung pro Geometrietyp
Das sieht für meine Beispiel-Tabelle dann so aus:
Total number of geometries: 5274896
   Point geometries:        0  (0%)
   Curvestring geometries:   0  (0%)
   Polygon geometries:      5274896  (100%)
   Complex geometries:      0  (0%)