Oracle JETは、チャート以外にもビジュアリゼーションのコンポーネントを提供しています。
以下よりOracle JETのTreemapをOracle APEXのアプリケーションに組み込んでみます。
作成するアプリケーションは以下のように動作します。
Oracle JET Cookbookで紹介されているTreemapのBasicを、Oracle APEXに実装しています。
以下より実装手順を説明します。
Oracle JET CookbookではJSON形式のファイルusaMeanIncomeSubregion.jsonをデータソースとしています。Oracle APEXのアプリケーションでは、データベースの表をデータソースとして使用します。
usaMeanIncomeSubregion.jsonには米国の地域ごとの人口と平均所得が記載されています。また、米国全体、地域、州の単位で階層化されたデータになっています。
以下のクイックSQLのモデルより、表TMAP_POPULATIONSを作成します。
# prefix: tmap
populations
label vc80 /nn
population num /nn
mean_income num /nn
parent_node_id /fk populations
SQLの生成、SQLスクリプトを保存、レビューおよび実行を順次実施します。表の作成までを実施し、アプリケーションは作成しません。
Treemapを組み込むAPEXのアプリケーションを作成します。
アプリケーション作成ウィザードを起動します。アプリケーションの名前をJET Treemapとします。
デフォルトで作成されているホーム・ページを削除し、表TMAP_POPULATIONSをデータソースとした対話グリッドをページとして追加します。
対話グリッドのページ名はTreemap、表またはビューとしてTMAP_POPULATIONSを選択します。編集を許可を選択します。
以上でアプリケーションの作成を実行します。
アプリケーションが作成されます。Treemapはページ番号1のTreemapのページに実装します。
Breadcrumb BarにあるJET Treemapを削除します。JET Treemapの上でコンテキスト・メニューを表示させ、削除を実行します。
表TMAP_POPULATIONSに初期データを投入するボタンを作成します。
識別のボタン名はINIT、ラベルはInitとします。対話グリッドTmap Populationsの上に配置します。外観のテンプレート・オプションのWidthをStretchに変更し、画面の横幅いっぱいにボタンを表示させます。
動作のアクションはデフォルトのページの送信のまま変更しません。
左ペインでプロセス・ビューを開き、ボタンINITを押した時に実行するプロセスを作成します。
識別の名前は初期化、タイプとしてコードを実行を選択します。ソースのPL/SQLコードとして以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_response clob; | |
l_nodes json_array_t; | |
l_node json_object_t; | |
l_label varchar2(80); | |
l_population number; | |
l_mean_income number; | |
procedure process_node( | |
p_node in json_object_t | |
,p_parent_node_id in number | |
) | |
as | |
l_label tmap_populations.label%type; | |
l_population tmap_populations.population%type; | |
l_mean_income tmap_populations.mean_income%type; | |
l_node_id tmap_populations.id%type; | |
l_nodes json_array_t; | |
l_node_count pls_integer; | |
l_node json_object_t; | |
begin | |
l_label := p_node.get_string('label'); | |
l_population := p_node.get_number('population'); | |
l_mean_income := p_node.get_number('meanIncome'); | |
insert into tmap_populations(label, population, mean_income, parent_node_id) | |
values(l_label, l_population, l_mean_income, p_parent_node_id) returning id into l_node_id; | |
dbms_output.put_line(l_node_id || ',' || l_label || ',' || l_population || ',' || l_mean_income); | |
l_nodes := p_node.get_array('nodes'); | |
if l_nodes is null then | |
return; | |
end if; | |
l_node_count := l_nodes.get_size(); | |
if not l_node_count > 0 then | |
return; | |
end if; | |
for i in 1..l_node_count | |
loop | |
l_node := treat(l_nodes.get(i-1) as json_object_t); | |
process_node(l_node, l_node_id); | |
end loop; | |
end process_node; | |
begin | |
delete from tmap_populations; | |
commit; | |
execute immediate 'alter table tmap_populations modify id generated by default on null as identity (start with limit value)'; | |
l_response := apex_web_service.make_rest_request( | |
p_url => 'https://www.oracle.com/webfolder/technetwork/jet/cookbook/dataVisualizations/treeView/resources/usaMeanIncomeSubregion.json' | |
,p_http_method => 'GET' | |
); | |
l_nodes := json_array_t(l_response); | |
for i in 1..l_nodes.get_size() | |
loop | |
l_node := treat(l_nodes.get(i-1) as json_object_t); | |
process_node(l_node, null); | |
end loop; | |
commit; | |
end; |
サーバー側の条件のボタン押下時にINITを設定します。
この状態でアプリケーションを実行し、ボタンInitをクリックします。
表TMAP_POPULATIONSにデータが投入され、対話グリッドで編集できるようになります。
Treemapを表示するリージョンを作成します。
識別のタイトルはTreemapとします。タイプとして静的コンテンツを選択します。ボタンINITと対話グリッドの間にリージョンを配置します。
ソースのHTMLコードとして以下を記述します。Oracle JET Cookbookのdemo.htmlとほぼ同じです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="treemap-container"> | |
<oj-treemap style="width: 100%; height: 600px;" | |
id="treemap1" | |
animation-on-display="auto" | |
animation-on-data-change="auto" | |
data="[[treemapData]]"> | |
<template slot="nodeTemplate"> | |
<oj-treemap-node | |
label="[[$current.data.label]]" | |
value="[[$current.data.population]]" | |
color="[[getColor($current.data.meanIncome)]]" | |
short-desc="[[getShortDesc($current.data.label, $current.data.population, $current.data.meanIncome)]]"></oj-treemap-node> | |
</template> | |
</oj-treemap> | |
</div> |
余計な装飾を除くため、外観のテンプレートとしてBlank with Attributes (No Grid)を選択します。
TreemapのデータはAjaxコールバックを呼び出して取得します。
プロセス・ビューを開き、Ajaxコールバックにプロセスを作成します。Ajaxコールバックは、JSON形式で表TMAP_POPULATIONSの内容を木構造で返します。
識別の名前はGET_DATA、タイプとしてコードを実行を選択します。ソースのPL/SQLコードとして以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
function get_child_nodes( | |
l_node_id in number | |
) | |
return json_array_t | |
as | |
l_node_count pls_integer; | |
l_nodes json_array_t; | |
l_node json_object_t; | |
l_child_nodes json_array_t; | |
begin | |
select count(*) into l_node_count from tmap_populations | |
where (l_node_id is null and parent_node_id is null) or (parent_node_id = l_node_id); | |
if l_node_count = 0 then | |
return null; | |
end if; | |
l_nodes := json_array_t; | |
for r in ( | |
select | |
id, | |
json_object( | |
key 'label' value label, | |
key 'population' value population, | |
key 'meanIncome' value mean_income | |
) as json | |
from tmap_populations | |
where (l_node_id is null and parent_node_id is null) or (parent_node_id = l_node_id) | |
) | |
loop | |
l_node := json_object_t(r.json); | |
l_child_nodes := get_child_nodes(r.id); | |
if l_child_nodes is not null then | |
l_node.put('nodes', l_child_nodes); | |
end if; | |
l_nodes.append(l_node); | |
end loop; | |
return l_nodes; | |
end get_child_nodes; | |
begin | |
htp.p(get_child_nodes(null).to_clob()); | |
end; |
ページ・プロパティのJavaScriptのファイルURLに以下を記述します。
[require jet]
ファンクションおよびグローバル変数の宣言に以下を記述します。
var treemap;
ページ・ロード時に実行に以下を記述します。TreemapのモデルとなるクラスTreemapModelと、Treemapを更新するapex.actionのupdate-treemapを定義しています。内容は概ねOracle JET Cookbookのdemo.jsを踏襲しています。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require(["require", "exports", "knockout", "ojs/ojbootstrap", "ojs/ojarraytreedataprovider", "ojs/ojpalette", "ojs/ojpaletteutils", "ojs/ojknockout", "ojs/ojtreemap"], function (require, exports, ko, ojbootstrap_1, ArrayTreeDataProvider, ojpalette_1, ojpaletteutils_1) { | |
"use strict"; | |
class TreemapModel { | |
/* | |
* Treemapで表示するデータを更新する。 | |
*/ | |
update(nodes) { | |
this.treemapData(new ArrayTreeDataProvider(nodes, { | |
keyAttributes: "label", | |
childrenAttribute: "nodes", | |
})); | |
} | |
constructor() { | |
/* | |
* this.treemapDataはknockoutのobservableArrayに変更し、 | |
* データはコンストラクタの外から設定する。 | |
*/ | |
this.treemapData = ko.observableArray(); | |
/* | |
this.data = JSON.parse(jsonData); | |
this.treemapData = new ArrayTreeDataProvider(this.data, { | |
keyAttributes: 'label', | |
childrenAttribute: 'nodes' | |
}); | |
*/ | |
this.maxIncome = 70000; | |
this.minIncome = 35000; | |
this.colors = (0, ojpalette_1.getColorValuesFromPalette)('viridis'); | |
this.getColor = (meanIncome) => { | |
return (0, ojpaletteutils_1.getColorValue)(this.colors, (meanIncome - this.minIncome) / (this.maxIncome - this.minIncome)); | |
}; | |
this.getShortDesc = (label, population, meanIncome) => { | |
return ('<b>' + | |
label + | |
'</b><br/>Population: ' + | |
population + | |
'<br/>Income: ' + | |
meanIncome); | |
}; | |
} | |
} | |
(0, ojbootstrap_1.whenDocumentReady)().then(() => { | |
treemap = new TreemapModel(); | |
ko.applyBindings(treemap, document.getElementById('treemap-container')); | |
/* ページ・ロード時の表示 */ | |
apex.actions.invoke("update-treemap"); | |
}); | |
}); | |
/* | |
* Treemapを更新する。 | |
*/ | |
apex.actions.add([ | |
{ | |
name: "update-treemap", | |
action: () => { | |
apex.server.process ( "GET_DATA", {}, | |
{ | |
success: (data) => { | |
treemap.update(data); | |
} | |
} | |
); | |
} | |
} | |
]); |
CSSのファイルURLに以下を指定します。
#JET_CSS_DIRECTORY#redwood/oj-redwood-notag-min.css
対話グリッドでデータを変更し保存したときに、Treemapを更新する動的アクションを作成します。
識別の名前は変更の保存とします。タイミングのイベントとして保存[対話グリッド]を選択し、選択タイプはリージョン、リージョンとしてTmap Populationsを指定します。
TRUEアクションとしてJavaScriptコードの実行を選択し、設定のコードに以下の1行を記述します。
apex.actions.invoke("update-treemap");
以上でアプリケーションは完成です。アプリケーションを実行すると、記事の先頭のGIF動画のように動作します。
今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/jet-treemap.zip
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完