Logo Oracle Deutschland   Application Express Community
APEX und Geodaten: Positionen, Koordinaten und mehr - Teil 1

Erscheinungsmonat APEX-Version Datenbankversion
Oktober 2013 alle ab 10.2

Die Arbeit mit Positionen und Koordinaten, also Geodaten, spielt in mehr und mehr Anwendungsgebieten eine Rolle. Diese Tipp-Reihe der deutschsprachigen APEX Community stellt am Beispiel einer Image Geotagging Application vor, wie man in einer APEX-Anwendung mit Geodaten arbeiten, die Möglichkeiten der Datenbank ausnutzen und schließlich eine Karte in die APEX-Anwendung integrieren kann. Die Anwendung wird es ermöglichen ...

  • ... mit dem Smartphone aufgenommene Bilder per APEX-Anwendung hochzuladen
  • ... die Position zu extrahieren und als Koordinate zu speichern
  • ... die Bilder auf einer Karte darzustellen
  • ... räumliche Abfragen bzw. Auswertungen mit den Positionen durchzuführen
APEX Anwendung: "Image Geotagging"
Abbildung 1: APEX Anwendung: "Image Geotagging"

Da dies den Rahmen eines einzelnen Community-Tipps sprengen würde, werden die Themengebiete auf mehrere Community-Tipps verteilt.

Arbeiten mit Geodaten in der Datenbank: SDO_GEOMETRY

Die Oracle-Datenbank bietet seit langer Zeit eine umfassende Unterstützung für die Arbeit mit Geodaten an. Kern der Funktionalität ist der Datentyp SDO_GEOMETRY, welcher "geometrische Primitive" wie Punkte, Linienzüge oder Polygone repräsentiert. SDO_GEOMETRY kann in Tabellen und PL/SQL-Logik so verwendet werden, wie jeder andere Datentyp auch. Der erste Schritt zur Nutzung von Geodaten in APEX ist also das Erzeugen einer Tabelle mit einer Spalte vom Typ SDO_GEOMETRY.

create table tab_geotag_images (
  id           number(10)    primary key,
  titel        varchar2(200) not null,
  zeitstempel  date          not null,
  image        blob,
  thumbnail    blob,
  metadata     xmltype,
  geometry     sdo_geometry,
  owner        varchar2(200)
)
/

Die Bilder werden in der Spalte IMAGE vom Typ BLOB abgelegt. Zur Darstellung ist es hilfreich, eine im Vorfeld "kleingerechnete" Version des Bildes vorzuhalten; dieses wird in der Spalte THUMBNAIL hinterlegt. Die Spalte GEOMETRY ist vom Typ SDO_GEOMETRY und wird die Koordinate aufnehmen - weitere Metadaten des Bildes werden in METADATA im XML-Format gespeichert. Wie für jede andere Tabelle empfiehlt es sich auch hier, zur Befüllung der Primärschlüsselspalte ID eine Sequence und einen Trigger zu erzeugen.

create sequence seq_geotag_images
/

create or replace trigger tr_pk_geotag_images
before insert on tab_geotag_images
for each row
begin
  :new.id := seq_geotag_images.nextval;
  :new.zeitstempel := sysdate;
end;
/
sho err

Spalten vom Typ SDO_GEOMETRY erfordern allerdings noch einen weiteren Schritt: Sie müssen nochmals separat im Data Dictionary registriert werden. Dies geschieht, indem in die View USER_SDO_GEOM_METADATA eine Zeile eingefügt wird. Beachten Sie, dass dieser Schritt nicht im APEX SQL Workshop gemacht werden kann - Sie müssen den SQL Developer oder SQL*Plus oder ein anderes Werkzeug dafür verwenden. Wichtig dabei ist, dass Sie die physikalische Datenbankverbindung als Eigentümer der Tabellen aufgebaut haben - das ist bei APEX nicht der Fall. Alle anderen Schritte können auch im APEX SQL Workshop gemacht werden.

-- Dieses Kommando NICHT im APEX SQL Workshop,
-- sondern in SQL*Plus oder SQL Developer ablaufen lassen

insert into user_sdo_geom_metadata values (
  'TAB_GEOTAG_IMAGES', 
  'GEOMETRY', 
  sdo_dim_array(
    sdo_dim_element('X', -180, 180, 1),
    sdo_dim_element('Y',  -90,  90, 1)
  ), 
  8307
)
/

Neben dem Tabellen- und Spaltennamen sind hier vor allem die mögliche räumliche Ausdehnung der Koordinaten (Spalte DIMINFO) und das verwendete Koordinatensystem (SRID) wichtig. Im Beispiel werden Längen- und Breitengrade ("WGS84" mit der ID 8307) als Koordinatensystem verwendet - als Koordinaten kommt "die ganze Welt" in Frage (Längengrade: -180 bis +180; Breitengrade: -90 bis +90).

Der räumliche Index ist ebenfalls wichtig, wenn es um Tabellen mit SDO_GEOMETRY-Spalten geht. Dieser ist zwar nicht zwingend erforderlich - die meisten Operationen sind jedoch nur mit Spatial-Index möglich; es ist also sinnvoll, ihn gleich anzulegen.

create index sx_geotag_images on tab_geotag_images (geometry)
indextype is mdsys.spatial_index
/

SDO_GEOMETRY: Einige Beispiele

Wie schon gesagt, repräsentiert SDO_GEOMETRY geometrische Primitive wie Punkte, Linienzüge oder Polygone. Abbildung 2 zeigt die möglichen Geometrieformen.

Mögliche Geometrieformen mit SDO_GEOMETRY
Abbildung 2: Mögliche Geometrieformen mit SDO_GEOMETRY

Der genaue Aufbau des Datentypen SDO_GEOMETRY ist in der Dokumentation (Oracle Spatial and Graph Developers Guide ausführlich beschrieben - im folgenden seien daher nur einige Beispiele gezeigt.

  • Am häufigsten benötigt man sicherlich den Punkt. Der Punkt besteht normalerweise aus einem Koordinatenpaar (Längen- und Breitengrad). Das folgende Beispiel erzeugt einen (zweidimensionalen) Punkt als SDO_GEOMETRY:
    select sdo_geometry(2001, 8307, sdo_point_type(11.5, 48.2, null), null, null) from dual;
    
    SDO_GEOMETRY(2001,8307,SDO_POINT_TYPE(11.5,48.2,NULL),NULL,NULL)(SDO_GTYPE, SDO_
    --------------------------------------------------------------------------------
    SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(11,5, 48,2, NULL), NULL, NULL)
    
    1 Zeile wurde ausgewählt.
    
    Das erste Attribut des SDO_GEOMETRY (2001) besagt, dass es sich hier um einen zweidimensionalen Punkt handelt, der zweite Parameter bestimmt das verwendete Koordinatensystem (8307 = WGS84 = Längen- und Breitengrade). Der dritte Parameter wird nur bei Punten verwendet und enthält die konkreten Koordinaten; der vierte und fünfte Parameter bleiben leer - diese sind komplexeren Geometrieformen vorbehalten.

  • Ein Linienzug wird wie folgt gebildet:
    select sdo_geometry(
      2002, 
      8307, 
      null, 
      sdo_elem_info_array(1,2,1), 
      sdo_ordinate_array(11.5, 48.2, 11.6, 48.3, 11.5, 48.4)
    ) from dual;
    
    SDO_GEOMETRY(2002,8307,NULL,SDO_ELEM_INFO_ARRAY(1,2,1),SDO_ORDINATE_ARRAY(11.5,4
    --------------------------------------------------------------------------------
    SDO_GEOMETRY(2002, 8307, NULL, SDO_ELEM_INFO_ARRAY(1, 2, 1), SDO_ORDINATE_ARRAY(
    11,5, 48,2, 11,6, 48,3, 11,5, 48,4))
    
    Wiederum bestimmt das erste Attribut die Art der Geometrie (2002 = zweidimensionaler Linienzug); das zweite Attribut bestimmt (wiederum) das Koordinatensystem; das dritte Attribut bleibt leer (kein Punkt). Die Koordinaten selbst befinden sich im fünften Attribut; das vierte Attribut legt genau fest, wie die Koordinaten zu interpretieren sind (hier als Linienzug mit geraden Linien). Mit einem solchen Linienzug lässt sich schon etwas anfangen; so kann man mit der Funktion SDO_GEOM.SDO_LENGTH die Länge bestimmen ...
    select sdo_geom.sdo_length(
      sdo_geometry(
        2002, 
        8307, 
        null, 
        sdo_elem_info_array(1,2,1), 
        sdo_ordinate_array(11.5, 48.2 , 11.6, 48.3, 11.5, 48.4)
      ) ,1
    ) as "Länge in m" from dual;
    
    Länge in m
    ----------
    26734,9816
    
  • Eine Fläche wird als Polygon formuliert. Wir beginnen mit dem "Normalfall":
    select sdo_geometry(
      2003, 
      8307, 
      null, 
      sdo_elem_info_array(1,1003,1), 
      sdo_ordinate_array(11.5, 48.2, 11.6, 48.2, 11.  6, 48.3, 11.5, 48.3, 11.5, 48.2)
    ) from dual;
    
    SDO_GEOMETRY(2003,8307,NULL,SDO_ELEM_INFO_ARRAY(1,1003,1),SDO_ORDINATE_ARRAY(11.
    --------------------------------------------------------------------------------
    SDO_GEOMETRY(2003, 8307, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 1), SDO_ORDINATE_ARR
    AY(11,5, 48,2, 11,6, 48,2, 11,6, 48,3, 11,5, 48,3, 11,5, 48,2))
    
    Der grundsätzliche Aufbau ist analog zum Linienzug. Der Geometrietyp 2003 bedeutet, dass hier ein zweidimensionales Polygon vorliegt. Die Koordinaten sind wiederum im fünften Attribut; das vierte Attribut bedeutet, dass das Polygon durch einen geschlossenen Linienzug entgegen dem Uhrzeigersinn definiert ist; folgerichtig ist das letzte Koordinatenpaar gleich dem ersten. Die gleiche Geometrie kann auch einfacher (als "optimiertes Rechteck") formuliert werden. In diesem Fall werden nur die linke untere und die rechte obere Ecke angegeben.
    select sdo_geometry(
      2003, 
      8307, 
      null, 
      sdo_elem_info_array(1,1003,3), 
      sdo_ordinate_array(11.5, 48.2, 11.6, 48.3)
    ) from dual;
    
    SDO_GEOMETRY(2003,8307,NULL,SDO_ELEM_INFO_ARRAY(1,1003,3),SDO_ORDINATE_ARRAY(11.
    --------------------------------------------------------------------------------
    SDO_GEOMETRY(2003, 8307, NULL, SDO_ELEM_INFO_ARRAY(1, 1003, 3), SDO_ORDINATE_ARR
    AY(11,5, 48,2, 11,6, 48,3))
    
    Ein Polygon eignet sich gut zur Flächenberechnung (SDO_GEOM.SDO_AREA).
    select sdo_geom.sdo_area(
      sdo_geometry(
        2003, 
        8307, 
        null, 
        sdo_elem_info_array(1,1003,3), 
        sdo_ordinate_array(11.5, 48.  2, 11.6, 48.3)
      ), 1
    ) "Fläche in m²" from dual;
    
    Fläche in m²
    ------------
        82578072
    

Anhand dieser Informationen könnte man nun schon damit beginnen, die Tabelle mit Koordinaten zu befüllen. Allerdings möchten wir das nicht manuell tun, vielmehr sollen die Koordinaten aus den hochgeladenen Bildern extrahiert werden.

Koordinaten aus einem Smartphone-Bild extrahieren

Die Oracle-Datenbank bietet mit dem Paket ORDIMAGE einige Methoden zum Umgang mit Bildern an - dazu gibt es einen eigenen Community-Tipp. Zur Extraktion der GPS-Position, an der das Bild aufgenommen wurde, ist vor allem die Möglichkeit interessant, die EXIF-Metadaten auszulesen - dazu bietet die Datenbank die Funktion ORDIMAGE.GETMETADATA an. Voraussetzung ist natürlich, dass das Bild mit einem GPS-Fähigen Smartphone aufgenommen wurde.

METHOD
------
 STATIC FUNCTION GETMETADATA RETURNS XMLSEQUENCETYPE
 Argument Name                  Typ                     In/Out Defaultwert?
 ------------------------------ ----------------------- ------ --------
 IMAGEBLOB                      BLOB                    IN
 METADATATYPE                   VARCHAR2                IN     DEFAULT

Hat man also ein Bild in Form eines BLOB vorliegen, so kann man mit dieser Funktion die Metadaten abrufen. Diese werden dann als XML aufbereitet. Vor dem Aufruf von GETMETADATA sollte man sicherstellen, dass NLS_TERRITORY auf AMERICA eingestellt ist, sonst werden Fehlermeldungen wegen des Datumsformats ausgelöst.

alter session set nls_territory='AMERICA'
/

select ordimage.getmetadata(image, 'EXIF') from {image-table} where id = 1
/

XMLSEQUENCETYPE(XMLTYPE(<exifMetadata xmlns="http://xmlns.oracle.com/ord/meta/ex
if" xsi:schemaLocation="http://xmlns.oracle.com/ord/meta/exif http://xmlns.oracl
e.com/ord/meta/exif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <TiffIfd>
    <Make tag="271">Apple</Make>
    <Orientation tag="274">top left</Orientation>
:
:
  </TiffIfd>
  <ExifIfd tag="34665">
    <ExposureTime tag="33434">.00061614293</ExposureTime>
:
    <SceneCaptureType tag="41990">Standard</SceneCaptureType>
  </ExifIfd>
  <GpsIfd tag="34853">
    <GPSLatitudeRef tag="1">North latitude</GPSLatitudeRef>
    <GPSLatitude tag="2">48.3555</GPSLatitude>
    <GPSLongitudeRef tag="3">East longitude</GPSLongitudeRef>
    <GPSLongitude tag="4">11.773334</GPSLongitude>
    <GPSAltitudeRef tag="5">Sea level</GPSAltitudeRef>
    <GPSAltitude tag="6">470</GPSAltitude>
    <GPSTimeStamp tag="7">12:42:38.120000</GPSTimeStamp>
    <GPSImgDirectionRef tag="16">True direction</GPSImgDirectionRef>
    <GPSImgDirection tag="17">109.33391</GPSImgDirection>
  </GpsIfd>
</exifMetadata>
))

Mit den XML-Funktionen der Datenbank lassen sich die Koordinaten recht einfach aus dem Bild ausschneiden. Spielen Sie dazu das PL/SQL-Paket PKG_ORDIMAGE in Ihr Datenbankschema ein. Darin enthalten ist die Funktion GET_LOCATION, welche diese Arbeit erledigt und darüber hinaus die Funktion THUMBNAIL, welche eine kleinere Vorschauversion generiert.

FUNCTION GET_LOCATION RETURNS SDO_GEOMETRY
 Argument Name                  Typ                     In/Out Defaultwert?
 ------------------------------ ----------------------- ------ --------
 P_IMAGE                        BLOB                    IN
:
FUNCTION THUMBNAIL RETURNS BLOB
 Argument Name                  Typ                     In/Out Defaultwert?
 ------------------------------ ----------------------- ------ --------
 P_IMAGE                        BLOB                    IN
 P_WIDTH                        NUMBER                  IN
 P_HEIGHT                       NUMBER                  IN

Mit Hilfe von PKG_ORDIMAGE lässt sich nun eine PL/SQL-Prozedur (PROC_STOREIMAGE) erstellen, welche ein Bild entgegennimmt und in die anfangs erstellt Tabelle TAB_GEOTAG_IMAGES speichert. Zusätzlich werden die Metadaten und die Position ausgelesen, der Thumbnail generiert und alles zusammen in die Tabelle abgelegt.

create or replace procedure proc_storeimage(
  p_image     blob, 
  p_titel     varchar2,
  p_owner     varchar2 default user
) is
  l_thumbnail blob;
  l_exif      xmltype;
  l_geometry  sdo_geometry;
begin
 l_thumbnail := pkg_ordimage.thumbnail(p_image => p_image, p_width  => 300, p_height => 300);
 l_exif      := pkg_ordimage.get_exif(p_image => p_image);
 l_geometry  := pkg_ordimage.get_location(p_image => p_image);

 insert into tab_geotag_images (
   titel, image, thumbnail, metadata, geometry, owner
 ) values (
   p_titel, p_image, l_thumbnail, l_exif, l_geometry, p_owner
 );
end proc_storeimage;
/
sho err

Mit Hilfe dieser Prozedur können Sie nun beginnen, Bilder in Ihre Tabelle abzulegen.

  • Erstellen Sie eine neue APEX-Anwendung mit einer Anwendungsseite und einer Region vom Typ HTML.
  • Fügen Sie der Region ein neues Seitenelement vom Typ Datei durchsuchen namens P{X}_BILD hinzu. Legen Sie fest, dass die Datei in die Tabelle WWV_FLOW_FILES (und nicht in eine eigene Tabelle) gespeichert wird.
    Bild in Tabelle "WWV_FLOW_FILES" ablegen
    Abbildung 3: Bild in Tabelle "WWV_FLOW_FILES" ablegen
  • Fügen Sie eine Schaltfläche zum Absenden hinzu (Page Submit).
  • Hinterlegen Sie einen Prozess zum Speichern des Bildes in die eigentliche Tabelle. Er soll bei Klick auf die Schaltfläche gestartet werden und folgenden PL/SQL Code ausführen.
    declare
      l_bild      blob;
      l_filename  varchar2(200);
      l_nls_orig  nls_session_parameters.value%type;
    begin
      -- Einstellung NLS_TERRITORY sichern
      select value into l_nls_orig from nls_session_parameters where parameter = 'NLS_TERRITORY';
    
      -- NLS_TERRITORY auf AMERICA stellen - wichtig für Metadaten-Extraktion
      dbms_session.set_nls('nls_territory', 'AMERICA');
    
      select blob_content, filename into l_bild, l_filename 
      from wwv_flow_files where name = :PX_BILD;
    
      proc_storeimage(
        p_image   => l_bild,
        p_titel   => l_filename,
        p_owner   => :APP_USER
      );
    
      delete from wwv_flow_files where name = :PX_BILD;
    
      dbms_session.set_nls('nls_territory', l_nls_orig);
    end;
    

Starten Sie die Anwendung anschließend und speichern Sie einige Bilder ab. Wenn Sie die Tabelle danach nochmals ansehen, sollte die Spalte GEOMETRY gefüllt sein.

SQL> select id, titel, geometry from tab_geotag_images;

        ID TITEL                     GEOMETRY(SDO_GTYPE, SDO_SRID,
---------- ------------------------- ------------------------------
         1 Bild01.jpg                SDO_GEOMETRY(2001, 8307, SDO_P
                                     OINT_TYPE(16,365, 48,215332, N
                                     ULL), NULL, NULL)

         2 Bild02.jpg                SDO_GEOMETRY(2001, 8307, SDO_P
                                     OINT_TYPE(11,773334, 48,3555,
                                     NULL), NULL, NULL)
         : :                         :

Referenzdaten (Kartendaten) hinzufügen

Um richtig mit Geodaten arbeiten zu können, braucht man neben den Anwendungsdaten meist zusätzlich noch Kartendaten. Kartendaten müssen typischerweise käuflich erworden werden; dafür kommen die verschiedensten Anbieter in Betracht. In diesem Tipp möchten wir speziell auf das Beispieldatenset von HERE (ehemals NAVTEQ) eingehen, welches aus dem Oracle OTN kostenlos heruntergeladen werden kann. Das World Sample wird als DataPump-Exportdatei bereitgestellt und kann entsprechend mit dem impdp-Werkzeug in die Datenbank importiert werden. Enthalten sind vor allem Ländergrenzen, Administrative Einheiten und Major Roads, also die wichtigen Straßen eines Landes. Detailliertere Informationen wir Ortsstraßen sind nicht enthalten. Spielen Sie das Beispieldatenset also entsprechend der beigefügten Installationsanleitung ein - typischerweise wird dafür das Datenbankschema WORLD_SAMPLE erzeugt. Nach dem Einspielen enthält es einige neue Tabellen mit den Kartendaten.

TNAME                          TABTYPE  CLUSTERID
------------------------------ ------- ----------
NTC_MAP_ISLAND_AREA            TABLE
NTC_MAP_OCEAN_AREA             TABLE
NTC_MAP_POI_CITY_CENTER        TABLE
NTC_MAP_POI_CITY_GE_1000       TABLE
NTC_MAP_ROAD_HIGHWAY           TABLE
NTC_MAP_ROAD_HIGHWAY_AGGR      TABLE
NTC_MAP_WATER_AREA             TABLE
:                              :

Die Tabelle WOM_AREA enthält alle Flächen - da das WORLD_SAMPLE hauptsächlich zur Kartendarstellung gedacht ist, sind alle Flächen aller administrativen Ebenen in einer Tabelle zusammengefasst - der Inhalt der Spalte FEATURE_TYPE legt fest, ob die Fläche ein Staat, ein Bundesland oder eine Gemeindefläche ist.

Mit diesen Tabellen kann nun auf SQL-Ebene gearbeitet werden. So zeigt die folgende SQL-Abfrage, in welchen Staaten die Bilder gemacht wurden.

select i.titel, w.name 
from world_sample.wom_area w, tab_geotag_images i 
where sdo_anyinteract(w.geometry, i.geometry) = 'TRUE'
and w.feature_type = '907196';

TITEL                          NAME
------------------------------ -------------------------
Bild01.jpg                     GERMANY
Bild02.jpg                     AUSTRIA
Bild03.jpg                     GERMANY
Bild04.jpg                     GERMANY
Bild05.jpg                     BELGIUM

Schränkt man FEATURE_TYPE auf einen anderen Wert ein, so erhält man die Bundesländer bzw. Provinzen (in Staaten, die keine Bundesrepubliken sind).

select i.titel, w.name 
from world_sample.wom_area w, tab_geotag_images i 
where sdo_anyinteract(w.geometry, i.geometry) = 'TRUE'
and w.feature_type = '909996';

TITEL                          NAME
------------------------------ ------------------
Bild01.jpg                     BAYERN
Bild02.jpg                     WIEN
Bild03.jpg                     BERLIN
Bild04.jpg                     BERLIN
Bild05.jpg                     WALLONIE

Mit diesen Informationen lässt sich nun ein APEX-Bericht in die Anwendung einbauen - dieser soll eine Übersicht über die gespeicherten Bilder zeigen. Legen Sie also einen klassischen Bericht an und verwenden Sie dazu die folgende SQL-Abfrage.

select 
  i.id, i.titel, i.geometry, i.thumbnail,
  w.name
from tab_geotag_images i, world_sample.wom_area w
where sdo_anyinteract(w.geometry, i.geometry) = 'TRUE'
and w.feature_type = '907196'

Der erste Wurf kann noch nicht ganz überzeugen (Abbildung 4) ...

Erster Wurf: Klassischer Bericht mit SQL-Abfrage
Abbildung 4: Erster Wurf: Klassischer Bericht mit SQL-Abfrage

Sowohl die Spalten THUMBNAIL als auch GEOMETRY werden von APEX nicht dargestellt. Zur Darstellung des Vorschaubildes kann allerdings APEX-Standardfunktionalität verwendet werden. Ändern Sie dazu zunächst die SQL-Abfrage wie folgt um:

select 
  i.id, i.titel, i.geometry, 
  dbms_lob.getlength(i.thumbnail) thumbnail,
  w.name
from tab_geotag_images i, world_sample.wom_area w
where sdo_anyinteract(w.geometry, i.geometry) = 'TRUE'
and w.feature_type = '907196'

Navigieren Sie dann zu den Spaltenattributen der Spalte THUMBNAIL. Tragen Sie dort im Bereich Column Attributes den Text IMAGE:TAB_GEOTAG_IMAGES:THUMBNAIL:ID::::::Inline:Download als Number / Date Format ein (Abbildung 5).

Spaltenformat zur Anzeige eines Bildes im klassischen Bericht
Abbildung 5: Spaltenformat zur Anzeige eines Bildes im klassischen Bericht

Nun sollte der Bericht wie in Abbildung 6 aussehen.

Nun werden die Bilder angezeigt
Abbildung 6: Nun werden die Bilder angezeigt

Es bleibt die Spalte GEOMETRY übrig. APEX ist aus dem Stand nicht in der Lage, die Inhalte eines SDO_GEOMETRY anzuzeigen. Da es in diesem Fall aber nur um Punkte geht, kann man sich einfach helfen. Es braucht eine PL/SQL-Funktion, welche einen SDO_GEOMETRY in eine Zeichenkette umwandelt. Die Dezimalzahl wandeln wir dann auch gleich in eine Angabe in Grad, Minuten und Sekunden um. Spielen Sie also die folgende Funktion FUNC_GEOMTOSTRING in Ihr Datenbankschem ein.

create or replace function func_geomtostring(
  p_geom in sdo_geometry
) return varchar2 is
  l_lonlatstring varchar2(100) := '';
  l_num number;
  l_rem number;
  l_dir char(1);
  function make_part(p_num in number) return varchar2 is
    l_num number;
    l_rem number;
    l_part varchar2(100) := '';
  begin
    l_num := p_num;
    l_rem := l_num - floor(l_num);
    l_part := l_part || to_char(floor(l_num))||'°';
    l_num := l_rem * 60;
    l_rem := l_num - floor(l_num);
    l_part := l_part || to_char(floor(l_num), 'FM00')||'''';
    l_num := l_rem * 60;
    l_rem := l_num - floor(l_num);
    l_part := l_part || to_char(floor(l_num), 'FM00')||'"';
    return l_part;
  end make_part;
begin
  if p_geom is not null then 
    if p_geom.sdo_srid in (4326,8307) and p_geom.sdo_gtype in (2001, 3001, 3301) then
      if p_geom.sdo_point.x < 0 then l_dir := 'W'; else l_dir := 'E'; end if;
      l_lonlatstring := l_lonlatstring || make_part(abs(p_geom.sdo_point.x)) || ' ' || l_dir || ' - ';
      if p_geom.sdo_point.y < 0 then l_dir := 'S'; else l_dir := 'N'; end if;
      l_lonlatstring := l_lonlatstring || make_part((p_geom.sdo_point.y)) || ' ' || l_dir;
    else 
      l_lonlatstring := '- NOT A POINT GEOMETRY -';
    end if;
  end if;
  return l_lonlatstring;
end func_geomtostring; 
/

In der SQL-Abfrage wird diese nun einfach eingesetzt ...

select 
  i.id, i.titel, 
  func_geomtostring(i.geometry) geometry, 
  dbms_lob.getlength(i.thumbnail) thumbnail,
  w.name
from tab_geotag_images i, world_sample.wom_area w
where sdo_anyinteract(w.geometry, i.geometry) = 'TRUE'
and w.feature_type = '907196'

Danach sieht der Bericht wie in Abbildung 7 aus.

Nun werden die Bilder und die Position angezeigt
Abbildung 7: Nun werden die Bilder und die Position angezeigt

Ändert man das Berichtstemplate, lässt sich Layout noch weiter optimieren:

  • Erzeugen Sie ein neues Berichtstemplate
  • Wählen Sie from Scratch
  • Nehmen Sie ein Named Column Template
  • Vergeben Sie einen Namen und speichern Sie ab.
  • Navigieren Sie dann zu den Berichtsattributen, wählen Sie das neue Template aus und schalten Sie dabei auch gleich die Pagination ab.
  • Navigieren Sie zum soeben erstellten Berichtstemplate, wählen Sie Bearbeiten und hinterlegen Sie bei Row Template 1 den folgenden HTML-Code.
    <div style="margin-bottom: 1cm; padding: 3px; border-bottom: 1px dotted black; display: inline; float:left;"> 
    <b>#TITEL#</b><br/>
    #THUMBNAIL#<br/>
    #GEOMETRY#<br/>
    #NAME#
    </div>
    
    HTML-Code des neuen Berichts-Templates
    Abbildung 8: HTML-Code des neuen Berichts-Templates

Nun sieht die Bildübersicht etwas eleganter aus (Abbildung 9).

Bildübersicht mit neuem Berichts-Template
Abbildung 9: Bildübersicht mit neuem Berichts-Template

Fazit

In diesem Community-Tipp haben Sie erfahren, wie Sie eine Tabelle mit einer SDO_GEOMETRY-Spalte zur Verwaltung von Geoinformationen erzeugen. Neben einigen Beispielen zum Umgang mit SDO_GEOMETRY wurde eine APEX-Anwendung erzeugt, mit der Bilder in die Tabelle gespeichert werden - dabei werden die Metadaten und damit die GPS-Position des Bildes extrahiert und in der Tabelle gespeichert. Mit Hilfe des HERE (ehemals NAVTEQ)-Beispieldatensets "World Sample" können räumliche Abfragen durchgeführt und so bspw. das Land, in dem das Bild aufgenommen wurde, festgestellt werden. Mit Hilfe von APEX-Standardkomponenten wie dem klassichen Bericht lässt sich schließlich eine einfache Übersicht über die vorhandenen Bilder generieren.

Der nun folgende, nächste logische Schritt ist das Aufnehmen einer Karte in die Anwendung. Dies wird in Teil 2 der Tipp-Reihe behandelt.

Zurück zur Community-Seite