🌟
WebFOCUSの拡張グラフ開発ガイドを自分で書いた
WebFOCUS拡張グラフ開発ガイド
1. 拡張グラフ開発の基本
1.1 プロジェクト構成
拡張グラフの標準的なディレクトリ構造は以下のとおりです:
com.mycompany.mychart/
├── com.mycompany.mychart.js (メインJSファイル)
├── properties.json (プロパティ設定)
├── css/
│ └── style.css (スタイルシート)
└── lib/
└── chart_utils.js (補助スクリプト)
1.2 拡張グラフ開発の流れ
-
既存の拡張グラフを複製するnpm run create-extension
コマンドを実行して - 複製したフォルダのproperties.jsonを編集する
- 拡張グラフのメインJSファイルを編集する
-
test.htmlを使ってローカルでテストする -
WebFOCUSにデプロイするnpm run deploy
コマンドを使って
2. 主要なコンポーネント
2.1 プロパティファイル (properties.json)
properties.jsonファイルは、拡張グラフのメタデータと設定を定義します。
{
"info": {
"version": "1.0",
"implements_api_version": "1.0",
"author": "My Company",
"copyright": "My Company Inc.",
"url": "https://www.mycompany.com",
"icons": {
"medium": "icons/medium.png"
}
},
"properties": {
"barColor": "#1f77b4",
"barSpacing": 2
},
"propertyAnnotations": {
"barColor": "str",
"barSpacing": "number"
},
"dataBuckets": {
"tooltip": true,
"buckets": [
{
"id": "value",
"type": "measure",
"count": {"min": 1, "max": 1}
},
{
"id": "labels",
"type": "dimension",
"count": {"min": 1, "max": 1}
}
]
},
"translations": {
"en": {
"name": "My Chart",
"description": "My custom chart description",
"icon_tooltip": "My Chart",
"value_name": "Value Bucket",
"value_tooltip": "Drop a measure here",
"labels_name": "Label Bucket",
"labels_tooltip": "Drop a dimension here"
},
"ja": {
"name": "マイチャート",
"description": "カスタムチャートの説明",
"icon_tooltip": "マイチャート",
"value_name": "値バケット",
"value_tooltip": "ここに測定値をドロップ",
"labels_name": "ラベルバケット",
"labels_tooltip": "ここにディメンションをドロップ"
}
}
}
2.2 メインJSファイル
メインJSファイルには、拡張グラフのロジックが含まれます。
(function() {
// 初期化コールバック
function initCallback(successCallback, initConfig) {
successCallback(true);
}
// データがない場合の前処理コールバック
function noDataPreRenderCallback(preRenderConfig) {
var chart = preRenderConfig.moonbeamInstance;
chart.legend.visible = false;
}
// データがない場合のレンダリングコールバック
function noDataRenderCallback(renderConfig) {
var container = d3.select(renderConfig.container);
container.append("text")
.attr("x", renderConfig.width / 2)
.attr("y", renderConfig.height / 2)
.attr("text-anchor", "middle")
.text("データがありません");
renderConfig.renderComplete();
}
// レンダリング前コールバック
function preRenderCallback(preRenderConfig) {
var chart = preRenderConfig.moonbeamInstance;
chart.title.visible = true;
chart.title.text = "マイカスタムチャート";
}
// メインレンダリングコールバック
function renderCallback(renderConfig) {
var chart = renderConfig.moonbeamInstance;
var data = renderConfig.data;
var props = renderConfig.properties;
var container = d3.select(renderConfig.container)
.attr("class", "com_mycompany_chart");
// データの正規化
if (renderConfig.dataBuckets.depth === 1) {
data = [data];
}
// スケールの設定
var width = renderConfig.width;
var height = renderConfig.height;
var margin = {top: 20, right: 20, bottom: 40, left: 50};
var x = d3.scale.ordinal()
.domain(data.map(function(d) { return d.labels; }))
.rangeRoundBands([margin.left, width - margin.right], 0.1);
var yMax = d3.max(data, function(d) { return d.value[0]; });
var y = d3.scale.linear()
.domain([0, yMax * 1.1])
.range([height - margin.bottom, margin.top]);
// 軸の描画
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
container.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + (height - margin.bottom) + ")")
.call(xAxis);
container.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis);
// バーの描画
var bars = container.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.labels); })
.attr("width", x.rangeBand() - props.barSpacing)
.attr("y", function(d) { return y(d.value[0]); })
.attr("height", function(d) { return height - margin.bottom - y(d.value[0]); })
.attr("fill", props.barColor)
.attr("class", function(d, i) {
return chart.buildClassName("riser", 0, i, "bar");
})
.each(function(d, i) {
renderConfig.modules.tooltip.addDefaultToolTipContent(this, 0, i, d);
});
// データラベルの追加
container.selectAll(".bar-label")
.data(data)
.enter().append("text")
.attr("class", "bar-label")
.attr("x", function(d) { return x(d.labels) + x.rangeBand() / 2; })
.attr("y", function(d) { return y(d.value[0]) - 5; })
.attr("text-anchor", "middle")
.text(function(d) { return chart.formatNumber(d.value[0], "#,###.##"); });
// レンダリング完了を通知
renderConfig.renderComplete();
renderConfig.modules.tooltip.updateToolTips();
}
// 拡張グラフの設定
var config = {
id: 'com.mycompany.mychart',
containerType: 'svg',
initCallback: initCallback,
preRenderCallback: preRenderCallback,
renderCallback: renderCallback,
noDataPreRenderCallback: noDataPreRenderCallback,
noDataRenderCallback: noDataRenderCallback,
resources: {
script: ['lib/chart_utils.js'],
css: ['css/style.css']
},
modules: {
tooltip: {
supported: true
},
dataSelection: {
supported: true,
needSVGEventPanel: false
}
}
};
// 拡張グラフの登録
tdgchart.extensionManager.register(config);
})();
3. データバケットの操作
3.1 バケットの定義
拡張グラフのデータバケットは、properties.jsonで定義されます:
"dataBuckets": {
"tooltip": true,
"buckets": [
{
"id": "value",
"type": "measure",
"count": {"min": 1, "max": 1}
},
{
"id": "labels",
"type": "dimension",
"count": {"min": 1, "max": 1}
}
]
}
3.2 バケットデータへのアクセス
function renderCallback(renderConfig) {
var dataBuckets = renderConfig.dataBuckets;
// バケットの存在を確認
if (dataBuckets && dataBuckets.buckets) {
// valueバケットの情報を取得
if (dataBuckets.buckets.value) {
var valueTitle = dataBuckets.buckets.value.title;
var valueFieldName = dataBuckets.buckets.value.fieldName;
var valueNumberFormat = dataBuckets.buckets.value.numberFormat;
}
// labelsバケットの情報を取得
if (dataBuckets.buckets.labels) {
var labelsTitle = dataBuckets.buckets.labels.title;
var labelsFieldName = dataBuckets.buckets.labels.fieldName;
}
}
// データの処理
var data = renderConfig.data;
// ...
}
3.3 配列形式への正規化
バケットのプロパティは、データ構造に応じて文字列または配列で返されることがあります。一貫した処理を行うために、常に配列形式に正規化することをお勧めします:
// バケットのプロパティを常に配列として扱う
var labelsTitles = buckets.labels ? (Array.isArray(buckets.labels.title) ? buckets.labels.title : [buckets.labels.title]) : [];
var labelsFieldNames = buckets.labels ? (Array.isArray(buckets.labels.fieldName) ? buckets.labels.fieldName : [buckets.labels.fieldName]) : [];
var valueTitles = buckets.value ? (Array.isArray(buckets.value.title) ? buckets.value.title : [buckets.value.title]) : [];
var valueFieldNames = buckets.value ? (Array.isArray(buckets.value.fieldName) ? buckets.value.fieldName : [buckets.value.fieldName]) : [];
var valueNumberFormats = buckets.value ? (Array.isArray(buckets.value.numberFormat) ? buckets.value.numberFormat : [buckets.value.numberFormat]) : [];
4. 拡張グラフのモジュール
4.1 ツールチップモジュール
ツールチップを実装するには:
var config = {
// ...
modules: {
tooltip: {
supported: true,
autoContent: function(target, s, g, d) {
return d.labels + ': ' + d.value;
}
}
}
};
レンダリング時に各要素にツールチップを追加:
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("class", function(d, i) {
return chart.buildClassName("riser", 0, i, "bar");
})
.each(function(d, i) {
renderConfig.modules.tooltip.addDefaultToolTipContent(this, 0, i, d);
});
// 最後にツールチップを更新
renderConfig.modules.tooltip.updateToolTips();
4.2 データ選択モジュール
データ選択を実装するには:
var config = {
// ...
modules: {
dataSelection: {
supported: true,
needSVGEventPanel: false
}
}
};
レンダリング後に有効化:
// グラフが完全に描画された後に呼び出す
renderConfig.modules.dataSelection.activateSelection();
5. よくある問題と解決方法
5.1 データが表示されない
問題: データがchartに表示されない
解決方法:
-
console.log(renderConfig.data)
でデータが正しく渡されているか確認 - データバケットが正しく定義されているか確認
-
renderConfig.dataBuckets.depth
をチェックし、必要に応じてデータを正規化
5.2 外部リソースが読み込まれない
問題: 外部JSやCSSが読み込まれない
解決方法:
-
resources
プロパティで正しいパスが指定されているか確認 - 相対パスが正しいか確認(通常は拡張フォルダからの相対パス)
5.3 拡張グラフが認識されない
問題: WebFOCUSで拡張グラフが表示されない
解決方法:
-
html5chart_extensions.json
に拡張グラフが正しく登録されているか確認 - WebFOCUS管理コンソールでキャッシュをクリアする
6. デバッグテクニック
6.1 コンソールログ
各コールバック関数の開始時にログを出力することで、処理の流れを確認できます:
function renderCallback(renderConfig) {
console.log('renderCallback:', renderConfig);
// データをログ出力
console.log('Data:', renderConfig.data);
console.log('Buckets:', renderConfig.dataBuckets);
// 処理を続行...
}
6.2 テスト用HTML
ローカル環境でテストするためのHTMLファイルを作成:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>拡張グラフテスト</title>
<script src="tdgchart-min-for-test.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="com.mycompany.mychart.js"></script>
<style>
#chart {
width: 800px;
height: 500px;
border: 1px solid #ccc;
margin: 20px;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
// テストデータ
var testData = [
{labels: "A", value: [100]},
{labels: "B", value: [150]},
{labels: "C", value: [200]},
{labels: "D", value: [125]},
{labels: "E", value: [175]}
];
// テスト用レンダリング設定
var renderConfig = {
moonbeamInstance: new tdgchart(),
data: testData,
properties: {
barColor: "#1f77b4",
barSpacing: 2
},
width: 800,
height: 500,
container: document.getElementById('chart'),
dataBuckets: {
buckets: {
value: {
title: "値",
fieldName: "VALUE"
},
labels: {
title: "ラベル",
fieldName: "LABELS"
}
}
},
modules: {
tooltip: {
supported: true,
addDefaultToolTipContent: function() {},
updateToolTips: function() {}
},
dataSelection: {
supported: true,
activateSelection: function() {}
}
},
renderComplete: function() {
console.log("レンダリング完了");
}
};
// 拡張グラフのレンダリング呼び出し
// コールバック関数の名前を正しく指定する必要があります
renderCallback(renderConfig);
</script>
</body>
</html>
7. WebFOCUSでの利用
7.1 拡張グラフのインストール
-
拡張グラフのフォルダを以下に配置します:
C:\ibi\WebFOCUS93\config\web_resource\extensions\com.mycompany.mychart
-
html5chart_extensions.json
ファイルを編集:{ "com.mycompany.mychart": {"enabled": true}, // 他の拡張グラフ設定 }
-
管理コンソールでキャッシュをクリアします
7.2 FOCEXECでの使用例
GRAPH FILE WF_RETAIL_LITE
SUM COGS_US
BY PRODUCT_CATEGORY
ON GRAPH PCHOLD FORMAT JSCHART
ON GRAPH SET LOOKGRAPH EXTENSION
ON GRAPH SET AUTOFIT ON
ON GRAPH SET STYLE *
INCLUDE=IBFS:/FILE/IBI_HTML_DIR/javaassist/intl/EN/combine_templates/ENWarm.sty,$
TYPE=DATA, COLUMN=PRODUCT_CATEGORY, BUCKET= >labels, $
TYPE=DATA, COLUMN=COGS_US, BUCKET= >value, $
*GRAPH_JS
chartType: "com.mycompany.mychart",
barColor: "#3366CC",
barSpacing: 5
*END
ENDSTYLE
END
8. ベストプラクティス
8.1 コード構成
- 匿名関数で全体を囲み、グローバル名前空間を汚染しない
- 各コールバック関数を明確に分離する
- 共通の処理は別関数として抽出する
8.2 エラーハンドリング
function renderCallback(renderConfig) {
try {
// チャートのレンダリングコード
renderConfig.renderComplete();
} catch (e) {
console.error("レンダリングエラー:", e);
// エラーメッセージを表示
var container = d3.select(renderConfig.container);
container.selectAll("*").remove();
container.append("text")
.attr("x", renderConfig.width / 2)
.attr("y", renderConfig.height / 2)
.attr("text-anchor", "middle")
.text("エラーが発生しました: " + e.message);
renderConfig.renderComplete();
}
}
8.3 レスポンシブデザイン
グラフのサイズに応じて自動的に調整される設計を心がけます:
function renderCallback(renderConfig) {
var width = renderConfig.width;
var height = renderConfig.height;
// サイズに基づいてマージンを調整
var margin = {
top: Math.max(10, height * 0.05),
right: Math.max(10, width * 0.05),
bottom: Math.max(30, height * 0.1),
left: Math.max(40, width * 0.1)
};
// フォントサイズも調整
var fontSize = Math.max(8, Math.min(width, height) / 40);
// 以降、これらの値を使用してグラフを描画
}
9. 高度な実装例
9.1 アニメーション
D3.jsのトランジションを使用したアニメーション:
// バーのアニメーション
bars.attr("y", height - margin.bottom)
.attr("height", 0)
.transition()
.duration(800)
.delay(function(d, i) { return i * 50; })
.attr("y", function(d) { return y(d.value[0]); })
.attr("height", function(d) { return height - margin.bottom - y(d.value[0]); });
9.2 インタラクティブ機能
クリックイベントの実装:
bars.on("click", function(d, i) {
console.log("クリック:", d, i);
// 選択状態を視覚的に表示
d3.select(this).classed("selected", !d3.select(this).classed("selected"));
// カスタムイベントの発火
if (typeof renderConfig.properties.onClick === "function") {
renderConfig.properties.onClick(d, i);
}
});
9.3 カラースケール
データに基づいて色を動的に生成:
var colorScale = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.value[0]; })])
.range(["#9ecae1", "#08519c"]);
bars.attr("fill", function(d) { return colorScale(d.value[0]); });
この開発ガイドを参考に、WebFOCUSの拡張グラフ開発を効率的に行ってください。
Discussion