Browsing "Older Posts"

SVG in APEX: Best Practices

Von Tobias Arnhold → 12.21.2016
In the last 8 month I was working on a new version of my SVG (Raphaël) example application. Of course I have a job (freelancer) and I have children. So the time to spend was diminished on only a few hours per week mostly on my way to work.
Some of the results were presented at the Swedish Oracle meetup in Summer '16. But the final application was made for DOAG 2016.

What leads me to an important point. To have the honor to present somewhere means for me also a passion to learn something new, create something new and share it with others to become a better developer. You may think 8 month of free time spend for a simple presentation is stupid but now I'm able to propose new business solutions to my customers which I didn't even thought were so easy to achieve.

And this is not all. One month before DOAG I was talking to a colleague "Sebastian Reinig" from another consulting company (Syntegris). A young men still studying for master grade and with a lot of passion towards Oracle APEX. He explained me that he worked on an APEX project using my former SVG solution from back in '14 and he enhanced my code by adding cool new features. (e.g. drag and drop in the harbor).
I mean those features were fitting perfectly in my example application. So I asked him to join my presentation at DOAG and show others what he was doing. An he did it. He added a new variant of my old example and helped me finishing the application.

That is why we should be presenting somewhere:
To become better by doing something new.
To become better by sharing your ideas with others and even get inspired by their ideas.

The result of this passion is the "SVG in APEX" application.



Download: github.com/tobiasarnhold/svg-in-apex
Example application URL: apex.oracle.com/pls/apex/f?p=SVG_IN_APEX
User: demo
Password: demo

Examples:

 1. Power grid (Great Britain including their main electricity pipelines)



2. Image tag editor




3. Harbor - Edit mask




4. Harbor - Drag and Drop



5. Mapael - Country visualization with edit mask




6. Mapael - Country visualization with canvas spark-line charts




7. Raphaël - Transform, integrate and interact with external SVG file
The application includes several slides describing the way how to transform an SVG file towards Raphaël.





8. SVG.js - Integrate and interact with external SVG files

SQL Developer: Quick Outline with SQL statements

Von Tobias Arnhold → 11.30.2016
Most of you probably know the "Quick Outline" function you have inside the SQL Developer.
It helps you to easily jump between different functions/procedures inside a package.


My colleague Holger told me about a bug in SQL Developer 3.x where you could use the "Outline" view with normal SQL files, too. Unfortunately in version 4 it didn't work anymore. So he stayed with version 3 for a long while. Otherwise he would had to scroll again instead of a short jump towards a specific SQL select.


A few days ago he asked me again if I knew a way how to easily jump between SQL statements inside a SQL file.
So I thought I talk to a SQL developer specialist "Sabine Heimsath". She knows all about those little tricks I have no clue about. But Sabine didn't know how to do it either.

My last chance was to ask on Twitter about a proper solution.

Even on Twitter nobody answered me. I guess they just started to implement such a feature. :)

Anyway yesterday Holger told me that he found a way to get it to run on SQL Developer 4.1.
Reason enough for me to share the awesome idea.

create or replace package body "" as 
/* ************************************************************************************************************************************************ */
function "SQL example 1";
/
-- SQL Statement

/* ************************************************************************************************************************************************ */
function "SQL example 2 - no character capping";
/
-- SQL Statement

/* ************************************************************************************************************************************************ */
function SQL_example_3_without_double_quote;
/
-- SQL Statement

end;


Finally a little GIF movie showing you the usage in action:

Tablespaces verkleinern (TEMP, USER_TS, ORA-03297)

Von Tobias Arnhold → 11.28.2016

Info
Die Select-Statements in diesem Blogpost habe ich von anderen Webseiten kopiert. Daher ist dieser Beitrag eher als Zusammenfassung unterschiedlicher Lösungsversuche zu sehen und dient mir als schnelle Hilfe bei der Verkleinerung eines zu großen Tablespaces. Schaut euch die Quellen an, die sehr viel detaillierter auf die jeweiligen Probleme eingehen.

Wer kennt nicht die Situation? Der DBA ruft an und meint der TEMP Tablespace verbraucht mehrere hundert Gigabyte an Speicher.

Was ist in solch einer Situation zu tun?
In dem Moment wo ein TEMP Tablespace überproportional ansteigt, muss eine Session diesen Anstieg verursachen. Mit dem folgenden Select erfahren Sie welche Session wie viel Speicher im TEMP-Tablespace verbraucht.

-- Source: http://stackoverflow.com/questions/174727/discover-what-process-query-is-using-oracle-temp-tablespace
select   b.tablespace
       , b.segfile#
       , b.segblk#
       , round (  (  ( b.blocks * p.value ) / 1024 / 1024 ), 2 ) size_mb
       , a.sid
       , a.serial#
       , a.sql_id
       , a.username
       , a.osuser
       , a.program
       , a.status
    from v$session a
       , v$sort_usage b
       , v$process c
       , v$parameter p
   where p.name = 'db_block_size'
     and a.saddr = b.session_addr
     and a.paddr = c.addr
order by b.tablespace
       , b.segfile#
       , b.segblk#
       , b.blocks;

Über die SQL_ID können Sie wenn vorhanden auch auf das Verursacher-Select zugreifen:
-- Source: http://cheatsheet4oracledba.blogspot.de/2014/01/how-to-find-top-temp-using-oracle.html
select sql_text 
from v$sql 
where sql_id='b6kta08q9jj3f';

Über den SQL Developer > Tools > Monitor Sessions können Sie die betroffene SID killen.

Anschließend wird der Verbrauch des TEMP-Tablespace zwar wieder zurückgefahren, aber die Größe bleibt bestehen.

Prüfen Sie daher zunächst die aktuelle verwendete Größe im TEMP-Tablespace:
-- Source: https://alexzeng.wordpress.com/2012/06/13/how-to-find-the-sql-that-using-lots-of-temp-tablespace-in-oracle/
select b.total_mb,
       b.total_mb - round(a.used_blocks*8/1024) current_free_mb,
       round(used_blocks*8/1024)                current_used_mb,
      round(max_used_blocks*8/1024)             max_used_mb
from v$sort_segment a,
 (select round(sum(bytes)/1024/1024) total_mb from dba_temp_files ) b;

-- Oder:
select * from dba_temp_free_space;

Wenn der Wert (current_used_mb) entsprechend klein ist, dann können Sie den TEMP-Tablespace verkleinern, andernfalls haben Sie nicht die richtige Session gekillt.

TEMP-Tablespace verkleinern:
select file_name,bytes,blocks from dba_temp_files;
-- .../tempfile/temp.911 564863696896 68953088

alter tablespace temp shrink space;

select file_name,bytes,blocks from dba_temp_files;
-- ../tempfile/temp.911 289513472 35341


Es kann aber auch vorkommen das eine normaler Tablespace zu groß wurde und dadurch viel mehr Platz verbraucht als es aktuell verwendet.
Um darüber einen Überblick zu erhalten, führen Sie folgendes Select aus:
 -- Source: Nicht mehr bekannt :(
select
   mb.*
  ,nvl(round(100 * freemb / sizemb,1),0) free_prozent
from 
 (select 
    b.tablespace_name
   ,round(tbs_size,2) as sizemb
   ,a.free_space freemb
  from 
    (select 
       tablespace_name
      ,round(sum(bytes)/1024/1024 ,2) as free_space 
     from dba_free_space group by tablespace_name
    ) a
   ,(select 
      tablespace_name, 
      sum(bytes)/1024/1024 as tbs_size 
     from dba_data_files group by tablespace_name
     union
     select 
       tablespace_name, 
       sum(bytes)/1024/1024 tbs_size
     from dba_temp_files
     group by tablespace_name 
    ) b
  where a.tablespace_name(+)=b.tablespace_name
  ) mb
order by free_prozent;

Bei einer solchen Situation muss anstelle des Tablespaces die Datendatei verkleinert werden.
Nun benötigen Sie dafür noch den richtigen Dateinamen, um die korrekte Datei zu verkleinern:
SELECT 
  name, 
  bytes/1024/1024 AS size_mb
FROM   v$datafile
;

Statement zum verkleinern der Datendatei:
ALTER DATABASE DATAFILE '.../DATAFILE/my_schema.033.123331' RESIZE 3G

Die Verkleinerung kann aber in einem ORA-03297 Fehler enden.

ALTER DATABASE DATAFILE '.../DATAFILE/my_schema.033.123331' RESIZE 3G
Error report -
SQL Error: ORA-03297: file contains used data beyond requested RESIZE value

Jetzt bleiben Ihnen 3 Schritte um mit geringem Aufwand diese Datei doch noch zu verkleinern:

1. Fragmentierung bereinigen
Die Datendatei wurde fragmentiert und eine Tabelle liegt am Ende der Datei und verhindert dadurch die Verkleinerung.

Beispiel:
Die Datendatei ist 90 GB groß und tatsächlich werden nur 2 GB verwendet.
Eine Tabelle liegt von der Verteilung her zwischen 82-83 GB. Heißt, ich könnte die Datendatei nur auf 84G verkleinern. Also müssen Sie in solch einem Fall das Objekt ausfindig machen und löschen. Sinnvollerweise kopiere ich vorher die Tabelle in einen anderen Tablespace, um diese anschließend wieder herstellen zu können. :)

Um die Blockverteilung analysieren zu können, muss vorher die richtige File-ID ausgelesen werden:
select 
  s.tablespace_name, s.owner, s.segment_name, s.segment_type,
  sum(s.bytes) size_in_bytes,
  round(sum(s.bytes) / 1024 / 1024, 2) size_in_m,
  sum(round(sum(s.bytes) / 1024 / 1024, 2)) over() as size_in_m_gesamt,
  f.file_id,
  f.file_name
from sys.dba_segments s, sys.dba_data_files f
where f.tablespace_name = s.tablespace_name
and f.file_id = s.header_file
and s.tablespace_name in ('NTDC03')
group by s.tablespace_name, s.owner, s.segment_name, s.segment_type, f.file_id, f.file_name
order by s.tablespace_name, s.owner, s.segment_name;

Das folgende Select zeigt die Blockverteilung mit den verwendeten DB-Objekten innerhalb der Datendatei:
 Source: http://www.orait.de/db_fehler/ora-03297_file-contains-used-data-beyond.html
select 
  file_id,
  block_id, 
  blocks*8192/1024/1024 as mb,
  owner||'.'||segment_name as object_name,
  block_id*8192/1024/1024 as position_mb
from sys.dba_extents
where file_id = 206
union
select 
  file_id, 
  block_id, 
  blocks*8192/1024/1024 as mb, 
  'Free' as object_name,
  block_id*8192/1024/1024 as position_mb
from sys.dba_free_space
where file_id = 206
order by 1,2,3;


Wenn Sie die betroffenen Tabellen gelöscht haben, dann klappt auch die Verkleinerung wieder:  
ALTER DATABASE DATAFILE '.../DATAFILE/my_schema.033.123331' RESIZE 3G 
Database datafile '.../DATAFILE/my_schema.033.123331' altered.

2. Recycle Bin löschen
purge recyclebin;

3. Coalesce Tablespace
alter tablespace fred coalesce;

Info: TEMP Tablespace auf Unlimited setzen
alter database tempfile '.../TEMPFILE/temp.910.901132571' autoextend on next 250m maxsize unlimited;

DOAG 2016 Review

Von Tobias Arnhold → 11.21.2016

Die diesjährige DOAG war für mich anders als die Jahre zuvor. Dieses Jahr lag mein Fokus weniger auf Vorträgen, sondern mehr auf Community-Aktivitäten und Jugendarbeit.
Daher habe ich diverse Unconference Sessions besucht und viele Unterhaltungen geführt. Dabei habe ich zum Beispiel erfahren, dass es eine neue Community innerhalb der DOAG gibt  -  die DOAG NextGen. Bei der es darum geht, junge Menschen für Oracle zu begeistern.


Da ich hier ein riesiges Potential sehe und Oracle IMHO ein wachsendes Alters-Problem mit seiner Zielgruppe hat, war ich sofort begeistert von der Idee und den Zielen. Jeder im Oracle-Umfeld sollte langsam erkennen, dass langfristig der Job eines jeden Oracle-Enthusiasten in Gefahr geraten kann. Nicht weil die Software schlecht ist, sondern weil der Nachwuchs fehlt und deshalb der Einsatz der Software risikobehaftet und teuer werden würde. Man kann jetzt sagen, dass ich einen zu negativen Ausblick darstelle, aber wer jetzt nicht bereit ist zu handeln, muss später möglicherweise den Preis dafür zahlen. Egal ob Oracle selbst, Consulting-Firmen, Freelancer oder begeisterte Oracle-User (DBAs & DEVs) innerhalb der Unternehmen.

In diesem Sinne wurde bei der Unconference Session "Young PL/SQL" den Oracle-Verantwortlichen mitgegeben, eine Datenbank für Studenten und Hochschulen anzubieten, um einen schnellen und unkomplizierten Zugriff auf Datenbanken zu erhalten.
Hier mal ein Beispiel: Ein Student muss mit Hilfe der Oracle Cloud und REST Data Services innerhalb von einem Tag in der Lage sein, mit einer mobilen Anwendung zu kommunizieren.
Das Ganze natürlich ganz Oracle untypisch kostenfrei. In Zeiten der Oracle Cloud ist das rein aus Marketing-Sicht wohl keine schlechte Idee.

Neben all der Nachwuchsarbeit war Oracle APEX natürlich mein Schwerpunkt. Hier hat mich ganz besonders gefreut, dass neben den Experten aus dem deutschsprachigen Raum auch Joel Kallmann und Marc Sewtz aus den USA da waren. Den Höhepunkt gab es dann auf der APEX Open Mic Night mit vielen Ideen und lustigen Aktionen rund um APEX:


Zu APEX 5.1 lässt sich sagen, dass eine Fertigstellung in diesem Jahr nicht völlig abwegig ist und das Interesse am neuen Grid extrem hoch ist. APEX 5.1 bietet damit noch mehr Gründe, veraltete Forms-Anwendungen abzulösen.

Zur Info: Im nächsten Jahr gibt es zum Thema Forms Migration spezielle Workshops im gesamten Bundesgebiet.

Was ich persönlich toll fand, war der Besuch von Mathias Magnusson von der schwedischen Oracle Usergroup (SWEOUG). Mit Ihm habe ich die Möglichkeiten eines APEX-Events in Schweden ausgelotet. Denn im Gegensatz zu Deutschland gibt es bei der Verbreitung in anderen europäischen Ländern noch deutliches Potenzial. Von daher ist die Idee APEX in anderen Ländern anzutriggern eine Aktivität, die ich auf jeden Fall unterstütze.
Als ich Mathias dem Joel vorstellen wollte, um ein mögliches Event näher zu besprechen, fand ich anstelle von Joel die Jungs und Mädels der polnischen Oracle Usergroup (POUG). Genau diese Leute haben eine fantastische erste Oracle-Konferenz in Warschau hingelegt, wodurch ich mir gesagt habe, da musst du nächstes Jahr dabei sein. Dieser Eindruck hat sich nach einem kurzen Kennenlernen bewahrheitet. Der Spirit und die Aufbruchstimmung wie diese kleine Gruppe sie verkörpert, sucht derzeit seines Gleichen. Ich kann nur hoffen, dass es bei ihrer nächsten Konferenz im September 2017 einen eigenen APEX Track gibt.

Joel haben wir dann im Übrigen noch auf der VIP-Party getroffen. Eine verdammt gute Party und die Chance auf ein APEX-Event in Schweden ist sehr groß.


Aber zurück nach Deutschland. Nach der Konferenz ist vor der Konferenz. Ich freue mich schon jetzt auf die APEX Connect 2017 in Berlin und wünsche allen einen entspanntes letztes Quartal. :)

DOAG 2016 und weitere APEX Events im November

Von Tobias Arnhold → 11.04.2016
Das größte deutschsprachige Oracle Event des Jahres steht vor der Tür.
Die DOAG Konferenz vom 15. bis zum 18.11.2016.

Wie schon in den letzten Jahren, bin ich auch dieses Jahr wieder mit dabei.
Mein Vortrag zum Thema "SVG in APEX" findet am Mittoch dem 16.11. um 13:00 Uhr statt.


Den Vortrag halte ich nicht allein. Sebastian Reinig (Syntegris) ein sehr talentierter junger APEX-Consultant hält den Vortrag mit mir Zusammen. Sebastian hat auf Grundlage meines RaphaelJS Vortrags aus dem Jahr 2014 eine technische Erweiterung geschrieben, die unbedingt auf der DOAG gezeigt werden muss. Der Vortrag richtet sich an alle Entwickler die aus Ihrer individuellen Geschäftsanwendung die User Experience (UX) auf ein neues Level heben wollen. In diesem Vortrag werden anhand realer Business-Cases die Möglichkeiten von SVG aufgezeigt. Wichtig war vor allem die Betrachtung aus Sicht Aufwand und Nutzen.

An dieser Stelle möchte ich auch gern auf den MT AG Vortrag zum Thema "Der APEX GENERATOR" verweisen. Der rein marketingtechnisch schon richtig was her macht! :)



Meine Partner von APEX-EXPERTS sind natürlich auch dabei. Markus Hohloch hält einen Vortrag zu Jasper Reports und Denes Kubicek zu Maps in APEX.

Ich persönlich freue mich auch auf die internationalen Gäste: Mathias Magnusson, Ilmar Kerm und Joel Kallman mit denen ich unbedingt ein APEX Event im Norden Europas besprechen muss.

Die APEX Open Mic Night findet wie gewohnt wieder am Mittwochabend statt, wo wieder spannende und lustige Kurzvorträge zu erwarten sind aus der Community.

Wusstet ihr, dass sich Studenten ebenfalls noch bei der diesjährigen DOAG bewerben können?

Nach der DOAG ist vor dem APEX Meetup Frankfurt.
Dieses findet das nächste Mal bei der DB Systel im Silberturm statt.
Nur mal so: Die DB Systel hat ein fast 20 köpfiges APEX Team rund um kleine und mittelgroße Projekte.

Jeder ist willkommen und ich halte auch einen Vortrag der als Diskussionsgrundlage zum Thema "Dashboards" dienen soll.

Auch mal wichtig zu erwähnen: Moritz (MT AG) und Sabine (its-people) organisieren dieses tolle lokale Event immer und immer wieder.


Und zu guter Letzt noch ein mal der Hinweis auf den APEX 5 Best Practices Kurs von Denes und Dietmar vom 21-23.11.2016. Ein Kurs den ein jeder APEX Neuling besucht haben sollte.


Wir sehen uns auf der DOAG und hoffentlich auch in meinem Vortrag...



Open a windows directory from inside your APEX application (IE, Firefox, Chrome)

Von Tobias Arnhold → 11.02.2016
Back in 2010 I wrote about a solution for IE only: Open Windows directory with APEX (IE only)

The request itself appears at least once a year in one of my projects.
Nowadays I do not propose the workaround from 2010. It just doesn't fit in modern browsers.

Today I got another request to open a directory and this time I searched for 10 minutes and found a suitable solution working in IE, Firefox and Chrome.

As in 90% of each cases Stackoverflow showed me the way how I could to do it:
Stackoverflow Question: Open local folder from link

This solution creates a downloadable URL file including the target directory. The browser allows you to open directories from local files.

Unfortunately in Firefox and Chrome you need to download the LINK first and then execute it.



In IE you can open it directly.



This workaround is 100% HTML only and it is easy to integrate into APEX:
DECLARE
    L_BLOB           BLOB;
    L_CLOB           CLOB;
    L_DEST_OFFSET    INTEGER := 1;
    L_SRC_OFFSET     INTEGER := 1;
    L_LANG_CONTEXT   INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
    L_WARNING        INTEGER;
    L_LENGTH         INTEGER;
BEGIN

    -- create new temporary BLOB
    DBMS_LOB.CREATETEMPORARY(L_BLOB, FALSE);
    
    -- Create file source including the target directory
    SELECT '[InternetShortcut]'||CHR(13)||'URL=file:///D:' INTO L_CLOB FROM DUAL;
    
    -- tranform the input CLOB into a BLOB of the desired charset
    DBMS_LOB.CONVERTTOBLOB( DEST_LOB     => L_BLOB,
                            SRC_CLOB     => L_CLOB,
                            AMOUNT       => DBMS_LOB.LOBMAXSIZE,
                            DEST_OFFSET  => L_DEST_OFFSET,
                            SRC_OFFSET   => L_SRC_OFFSET,
                            BLOB_CSID    => NLS_CHARSET_ID('WE8MSWIN1252'),
                            LANG_CONTEXT => L_LANG_CONTEXT,
                            WARNING      => L_WARNING
                          );

    -- determine length for header
    L_LENGTH := DBMS_LOB.GETLENGTH(L_BLOB);  

    -- first clear the header
    htp.flush;
    htp.init;

    -- create response header including the filename ending with .url
    OWA_UTIL.MIME_HEADER( 'application/internet-shortcut', FALSE);
    htp.p('Content-length: ' || L_LENGTH);
    htp.p('Content-Disposition: inline; filename="URIShortcut.url"');
    htp.p('Set-Cookie: fileDownload=true; path=/');

    OWA_UTIL.HTTP_HEADER_CLOSE;
    -- download the BLOB
    WPG_DOCLOAD.DOWNLOAD_FILE( L_BLOB );

    -- stop APEX
    APEX_APPLICATION.STOP_APEX_ENGINE;
 
END;

Creating interactive graphics in APEX with Raphaël (RaphaelJS)

Von Tobias Arnhold → 9.21.2016
Some of you may know of my personal interest for SVG interaction in APEX applications. I have been working with a JavaScript library called "Raphaël" (RaphaelJS, raphael.js or just raphael) for a while now. It is an amazing plugin to create all kind of customized business solutions. But before I show you how I have used Raphaël I tell you how I came to the technology and what happened in the last few years.


5 years ago an APEX developer called "Matt Nolan" created an impressive example how you can use Raphaël for a custom business case: SVG Body Chart Plugin
 

Inspired by his example I started looking for this library called Raphaël.

What does the library do?
It creates dynamic SVG graphics with Javascript and JSON.

Why is it so special?
From an APEX developer point of view it gives you great possibilities to create SVG graphics right from the data out of your database and Raphaël itself brings you several ways to create more or less complex solutions. Just go on reading and you will find out.

How to integrate Raphaël?
Upload the JavaScript file and include it in your APEX page.
/* Implementation: Page > JavaScript > File URLs */
#APP_IMAGES#raphael-min.js
Add a new APEX region from type "Static Content" and add a DIV element:
<div id="svg_example1"></div>
Finally add the Raphaël-JavaScript code below the DIV region:
<script>
  document.addEventListener("DOMContentLoaded", function(){ 
    /* Set up raphael svg area and save it in variable rc (raphael container)*/
    var rc = new Raphael(document.getElementById('svg_example1'), 200, 180);
    
    /* add new svg element */
    var el = rc.path("M 170 120 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");
 
    /* Add attributes to element */
    el.attr(
        {
            fill: '#F2621B',
            stroke: '#2F3E66',
            'stroke-width': 10,
            'stroke-linejoin': 'round',
            rotation: -90
        }
    );
 }, false);
</script>

Result:


Who made this library?
The developer behind Raphaël is Dmitry Baranovskiy. Compared to the APEX community he is the Patrick Wolf for SVG solutions using JavaScript. At least in my mind. :)
After the success of Raphaël he joined Adobe. There he created an library called Snap which is quite similar to Raphael and real powerful. Unfortunately it seems that the Snap library is not further developed anymore. The last release is from 2 years ago.

Support?
The last Raphael release is from July 2016. Having questions use "Stack Overflow".

Why not creating the same kind of solution as Matt did?
It took me a while before I had the time thinking about the possibilities and purpose in business applications. I mean not every developer can create an example as Matt did. So I had some reasons to consider about:

1. No sources available
The typical company does not have SVG graphics to visualize their custom business situations. Mostly they use some advanced Excel files including some graphical parts to visualize their business cases. So if you create an APEX application based on those Excel solutions, the customers want their "beautiful" visualization in the APEX application as well.
For example an "workshop plan".

2. Complexity
It is really complicated to create a SVG graphic for the typical APEX developer.
Interacting with SVG graphics is also not the typical activity for an APEX developer.
Moving an SVG to the database to build it with Raphael is not easy either.
You may have to use Raphaël with other JavaScript libraries to enhance the user experience.

3. Time and Money
The more complex the example the more times it takes creating a result and it will become more expensive in the first run and on future updates.

4. Maintenance
Could a decent APEX developer take my solution and extend it? This is an important question to me.

What did I do?
2 Years ago I created an example application for the DOAG 2014.
There I published several examples how you can use Raphaël on top of an existing image file. I only created a few moving SVG components to interact with the application user.

Advantages:
- It is really easy to integrate.
- You do not need an SVG out of your Excel file to publish it in APEX.
- Less code because of the static background image

Disadvantage
- The image can't be responsive
- It can never be as beautiful as the example from Matt

The solutions looked like this:

Harbor example (Source: marinafuehrer.adac.de)









Bridge example (Source: dresden.de)  
Power grid example (Source: Dirk Witthaut, MPI für Dynamik und Selbstorganisation, ds.mpg.de)




The image, the data and the Raphael code comes directly from inside the database. Published with a PL/SQL procedure:
procedure generate_harbor (in_image_id number)
is
  v_image_filename           varchar2(100);
  v_image_name                varchar2(100);
  v_image_width              number;
  v_image_height               number;
  
  v_background_color varchar2(100);
begin
  s_proc_name := 'RJS_DEMO.GENERATE_HARBOR';
  s_parameter := 'in_image_id: ' || to_char(in_image_id);
  
  s_action := 'Image selection';
  select image_filename, image_name, image_width, image_height
  into   v_image_filename,v_image_name, 
         v_image_width, v_image_height
  from   rjs_image
  where  id = in_image_id;
  
  s_action := 'HTML generation';
  htp.p('
   <style>
    #wrapper {
        position: relative;
        width: ' || v_image_width || 'px;
        height: ' || v_image_height || 'px;
        padding: 0;
        outline: 1px solid #999;
    }
    #wrapper img {
        position: absolute;
        top: 0;
        left: 0;
    }
    #canvas{
        position: absolute;
        top: 0;
        left: 0;
    }
   </style>
   <div id="wrapper">
    <img src="' || v('WORKSPACE_IMAGES') || v_image_filename ||'">
    <div id="canvas">
    </div>
   </div>
   ');
   

  s_action := 'JS open';
  htp.p('
   <script>
    document.addEventListener("DOMContentLoaded", function(){ 
      var canvas = Raphael(document.getElementById("canvas"), ' || v_image_width || ', ' || v_image_height || ');
  ');

  s_action := 'JS data loop';
  for rec_k in (
         select rownum, image_id, coordinate_id, 
                result_type, text, c_x, c_y, c_t, c_x_text, c_y_text
         from vw_rjs_harbor
         where image_id = in_image_id
  ) loop
    
      s_action := 'txt'||rec_k.rownum || ' generation.';
      htp.p ('
        var txt'||rec_k.rownum||' = canvas.text('||(rec_k.c_x_text)||', '||(rec_k.c_y_text)||', "'||rec_k.text||'");
        txt'||rec_k.rownum||'.attr({ "font-size": '||'"9"'||','
                                   || ' "font-family": "Arial, Helvetica, sans-serif",'
                                   || ' "fill": "black",'
                                   || ' transform:"r-'||rec_k.c_t||'"});  
        var box'||rec_k.rownum||' = txt'||rec_k.rownum||'.getBBox();
        var img'||rec_k.rownum||' = canvas.image("'|| v('WORKSPACE_IMAGES')||rec_k.result_type||'.png", '
                                                   || rec_k.c_x||', '||rec_k.c_y||', '||'26'||', '||'19'||')'
                                                   || '.attr({transform:"r-'||rec_k.c_t||'"}).glow({width:1,opacity:0.8});
        txt'||rec_k.rownum||'.toFront();
        ');   

  end loop;
  
  s_action := 'JS close';
  htp.p('
    }, false);
   </script>
  ');

exception
when others then   
      rollback; s_ora_error := sqlerrm;
      s_custom_error := 'Internal error occured. Processing stopped.';
      add_err; commit;raise_application_error(-20001, s_custom_error);
end;
APEX itself needs a region from type: "PL/SQL Dynamic Content"
The source is simple:
RJS_DEMO.GENERATE_HARBOR (:P11_IMAGE_ID);
The result looked like this:
 

From my point of view:
You get a new way publishing data in a decent amount of time and low complexity in the source code.



This was two years ago.

On this years DOAG 2016 I will tell you how you can achieve even more powerful examples.
Using the Raphael extension Mapael for custom map visualizations or how to create an own SVG and convert it real fast towards APEX. And maybe there will come some more...



See you at DOAG.

And for all who will miss the DOAG event.  I plan to publish an open source version of the application. So check apex.world at the end of the year. Maybe 24. of December will be a good date. :)

Oracle APEX Meetup in Stockholm 16.08.

Von Tobias Arnhold → 7.19.2016
In my summer holiday, this year in Stockholm (Sweden), I will make a stop and present two APEX topics in the "Stockholm Oracle Meetup group".

The topics will be:

Working with interactive SVG graphics in APEX 
Adding SVG graphics inside individual business applications is quite in common. Because those graphics can increase the usability and even more important it creates a completely new way to visualize data inside a browser app. In this presentation he will show us how one can integrate SVG charts and how to use the possibilities of SVG elements to make every static image interactive with only a few lines of code.



APEX Dashboard Competition 
At the beginning of the year the German community announced the "APEX Dashboard Competition". The goal was to create a nice looking dashboard application. This presentation will give you an impression about the best 10 applications and their individual solutions.


If you are interested to see what APEX is capable of then join the presentation on meetup.com.
Thanks to Mathias Magnusson for organizing this great event. Hope to meet as much APEX enthusiasts as possible. :)

Interactive Report with a Font Awesome edit icon

Von Tobias Arnhold → 7.01.2016
Most of you know the default APEX edit icons you can choose from inside your Interactive Report.
Basically you use them to link to a detail page.


The icon source looks like this.


A typical result page.


Two things I would like to change:
 1. The Icon should be a nice looking car symbol from the Font Awesome library.
 2. The column width should be as small as the icon and not relative to the page width.

We change the pencil.png towards the nice looking automobile icon from Font Awesome.
Code:  
<span class="fa fa-automobile"></span> 
 

Then we add a little CSS snippet to the page inline CSS.
Code:
.minimize-first-table-col td:first-child{
  width: 1%;
  overflow: visible;
  font-size:120%;
  padding: 8px;
}



And as a last step we add the class minimize-first-table-col inside the Interactive Report > Appearance > CSS Classes.


The result will looks like this.


Reset Interactive Report (IR)

Von Tobias Arnhold → 6.30.2016
Resetting an Interactive Report (IR) can be done in 4 different ways.
User Reset, URL Link, Page Process, Dynamic Action

First of all
It is always a good start to set up a specific static report id.


1.  User Reset
Not much to say.


1. URL Link

f?p=&APP_ID.:1:&APP_SESSION.:::RIR,CIR:

Cache definition:
- RIR: reset IR to primary report
- CIR: reset IR to primary report but with custom columns
- RP: reset IR pagination.

More details about the difference of RIR and CIR can be found here:
Apex Interactive Report: The difference between CIR and RIR

Basically all you need to know about the URL options in an IR can be found in the documentation:
Application Express Application Builder User's Guide


Also be aware that you can use several IR since APEX 5 on one page. Filters must now be applied in a specific syntax:
IR[region static ID]_

2. Page Process (Before Header)
DECLARE
  v_region_id APEX_APPLICATION_PAGE_REGIONS.REGION_ID%TYPE;
BEGIN

  SELECT region_id INTO v_region_id
  FROM APEX_APPLICATION_PAGE_REGIONS
  WHERE application_id = :APP_ID
  AND page_id = 1
  AND static_id = 'IR_RO_REPORT_ID'; -- Static ID of your IR

  APEX_IR.RESET_REPORT(
   P_page_id => 1,
   P_region_id => v_region_id,
   P_report_id => NULL
  );

END;

3. Dynamic Action
Create a new Dynamic Action.
Add a TRUE action of type "Execute PL/SQL code"  and use the same code as above.
And finally add another TRUE action to refresh the IR.

If you still need more information then take a look here:
Reset an Interactive Report (IR)

Oracle Spatial (Teil 4) - Unterschiedliche Koordinaten Punkte zu einer Linie zusammenführen

Von Tobias Arnhold → 6.21.2016
Vor kurzem musste ich Daten aus einer Excel-Liste in das SDO_GEOMETRY Format bringen. Leider hatte die Excelliste einen Haken.

Die Anforderungen:
- Es musste eine Linie (Typ 2002) im GK3 Format (31467) aus VON und NACH Punkten generiert werden.
- Die Excel-Liste hatte die Punkte entweder als GK3 oder als WGS84 Format hinterlegt.

Nach Import der Daten sah meine Quelltabelle dann so aus:


Und hier das Select zur richtigen Transformation der Geo-Daten ins SDO_GEOMETRY Format:
SELECT 
     SDOP.ID,
     SDOP.VON_NAME,
     SDOP.NACH_NAME,

     /* Linie generieren */
     MDSYS.SDO_GEOMETRY(
        2002, /* Linie */
        31467, /* GK3 */
        NULL,
        MDSYS.SDO_ELEM_INFO_ARRAY(1,2,1),
        MDSYS.SDO_ORDINATE_ARRAY(
            SDOP.VON_GEO_GK3.SDO_POINT.X,
            SDOP.VON_GEO_GK3.SDO_POINT.Y,
            SDOP.NACH_GEO_GK3.SDO_POINT.X,
            SDOP.NACH_GEO_GK3.SDO_POINT.Y
        )
     ) AS GEO_GK3,

     /* Entfernung berechnen */
     ROUND(SDO_GEOM.SDO_DISTANCE(
           GEOM1 => SDOP.VON_GEO_GK3,
           GEOM2 => SDOP.NACH_GEO_GK3,
           TOL => 5,
           UNIT => 'unit=KM'
     ),3) ENTFERNUNG
FROM (
      /* Berechnung der SDO Punkte */
      SELECT
        ID,
        VON_NAME,
        NACH_NAME,

        /* VON: Check ob GK3 oder WGS84 und Vereinheitlichung ins GK3 Format*/
        CASE 
         WHEN VON_GK3_X IS NOT NULL 
         THEN
            MDSYS.SDO_GEOMETRY (
                      2001, -- Zweidimensionaler Punkt
                      31467, -- Typ: GK3
                      SDO_POINT_TYPE(
                          X => VON_GK3_X, 
                          Y => VON_GK3_Y,
                          Z => NULL
                      ), 
                      NULL,
                      NULL
            ) 
          ELSE
            SDO_CS.TRANSFORM( 
              MDSYS.SDO_GEOMETRY (
                2001, -- Zweidimensionaler Punkt
                8307, -- Typ: WGS84
                SDO_POINT_TYPE(
                    X => VON_WGS84_X,  -- Längengrad / Longitude / Ost
                    Y => VON_WGS84_Y,  -- Breitengrad / Latitude / Nord
                    Z => NULL
                ), 
                NULL,
                NULL
              ),
             31467 -- Umwandlung in GK3
             )
        END AS VON_GEO_GK3,

        /* NACH: Check ob GK3 oder WGS84 und Vereinheitlichung ins GK3 Format*/
        CASE 
         WHEN NACH_GK3_X IS NOT NULL 
         THEN
            MDSYS.SDO_GEOMETRY (
                      2001, -- Zweidimensionaler Punkt
                      31467, -- Typ: GK3
                      SDO_POINT_TYPE(
                          X => NACH_GK3_X, 
                          Y => NACH_GK3_Y,
                          Z => NULL
                      ), 
                      NULL,
                      NULL
            ) 
          ELSE
            SDO_CS.TRANSFORM( 
              MDSYS.SDO_GEOMETRY (
                2001, -- Zweidimensionaler Punkt
                8307, -- Typ: WGS84
                SDO_POINT_TYPE(
                    X => NACH_WGS84_X,  -- Längengrad / Longitude / Ost
                    Y => NACH_WGS84_Y,  -- Breitengrad / Latitude / Nord
                    Z => NULL
                ), 
                NULL,
                NULL
              ),
             31467 -- Umwandlung in GK3
             )
        END AS NACH_GEO_GK3

      FROM GEO_MEINE_DATEN
) SDOP
Mit dem Ergebnis:

APEX Master (Interactive Report) - Detail (Modal Dialog) Form: Conditional Column Link

Von Tobias Arnhold → 6.15.2016

Since APEX 5 it is much easier to create master-detail pages with modal dialogs. But there is still no declarative way to create a conditional row based column link.

This blog post will show you a way how to create a conditional row based master - detail page.

1. We need some sample data:
WITH MY_DATA AS
(
select 1 as ID, 'APEX Connect' as name, 1 as CONDITIONAL_COL from DUAL
union all
select 2 as ID, 'KScope' as name, 0 as CONDITIONAL_COL from DUAL
union all
select 3 as ID, 'DOAG' as name, 1 as CONDITIONAL_COL from DUAL
union all
select 4 as ID, 'APEX-FRA Meetup Group' as name, 1 as CONDITIONAL_COL from DUAL
union all
select 5 as ID, 'Oracle World' as name, 0 as CONDITIONAL_COL from DUAL
)
select 
  ID,
  NAME,
  case when nvl(CONDITIONAL_COL,99) = 1 then
    'inline-block'
  else 'none' end as CSS,
  case when nvl(CONDITIONAL_COL,99) = 1 then
    'Visited conference'
  else 'Not visited yet' end as DETAIL_COL
from MY_DATA
As you can see the column CONDITIONAL_COL defines the conditional appearance.
CONDITIONAL_COL = 1 means show a detail button.
CONDITIONAL_COL = 0 means no detail button.

2. Now we can create a APEX master detail page where the "Page Mode" of the detail page is set to:
"Modal Dialog".


The master page gets an Interactive Report with the above select statement.

3. Now we need to configure the column attributes inside the Interactive Report:
Columns ID and NAME with be displayed as of type: "Plain Text".
Column CSS will not be displayed: "Hidden Column"
Column DETAIL_COL will be displayed as of type: "Link"
 - Target: 
   - Page: 2
   - Set Items: P2_ID - #ID#
 - Link Text: <span class="fa fa-search-plus" style="font-size: 160%"></span>
 - Link Attribues: class="a-Button" style="display:#CSS#;"
 We created a custom link for our detail page.


And the functionality to display the detail button conditionally is set by the column "CSS".


The result looks like this:

You can even filter the button by some readable data:


Security issue:
On the detail page you need a validation to check if the user is allowed to edit the data of the transmitted ID. An alternative way is to update the select so that no ID value will be generated if the user has no rights.
Example:
WITH MY_DATA AS
select 
  case when nvl(CONDITIONAL_COL,99) = 1 then
       ID
  else NULL end as ID,
  ID as ID_DISPLAY
  NAME,
  case when nvl(CONDITIONAL_COL,99) = 1 then
    'inline-block'
  else 'none' end as CSS,
  case when nvl(CONDITIONAL_COL,99) = 1 then
    'Visited conference'
  else 'Not visited yet' end as DETAIL_COL
from MY_DATA