1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Power BI カスタムビジュアル開発 : ChartUtils でチャートや凡例、ラベルを描写する

Last updated at Posted at 2018-06-16

ChartUtils

ChartUtils はカスタムビジュアルを作るためのインターフェースのセットです。軸の管理や凡例の操作、データのラベルの管理などが行えるなど、ビジュアルに関する重要な機能を提供します。

インストール

以下手順で追加。

1. npm コマンドでインストール。

npm install powerbi-visuals-utils-chartutils --save

2. tsconfig.json の files に型情報を追加。

tsconfig.json
{
  "compilerOptions": {
  ...
  },
  "files": [
    ..., 
    "node_modules/powerbi-visuals-utils-chartutils/lib/index.d.ts"
  ]
}

3. pbiviz.json の externalJS に追加。

pbiviz.json
{
  ...
  "externalJS": [
    ...,
    "node_modules/powerbi-visuals-utils-chartutils/lib/index.js"
  ],
  ...
}

4. デザインの情報を style/visual.less に追加。

visual.less
@import (less) "node_modules/powerbi-visuals-utils-interactivityutils/lib/index.css";
@import (less) "node_modules/powerbi-visuals-utils-chartutils/lib/index.css";
...

機能

Axis Helper

Axis Helper はチャートの軸の操作を簡単にします。

powerbi.extensibility.utils.chart.axis モジュールは多くの機能を提供するため、以下にいくつかよく使うものを紹介します。

getRecommendedNumberOfTicksForYAxis

チャート領域の高さによって最適な Y 軸の項目数を返す。

function getRecommendedNumberOfTicksForYAxis(availableWidth: number) 

getRecommendedNumberOfTicksForYAxis の例

import axis = powerbi.extensibility.utils.chart.axis;

public update(options: VisualUpdateOptions) {
...

    let yAxis = d3.svg.axis()
        .scale(yAxisScale)
        .orient('left')
        .ticks(axis.getRecommendedNumberOfTicksForYAxis(options.viewport.height));
...

[高さがない場合は 5]
image.png
[高さがある場合 8]
image.png

getRecommendedNumberOfTicksForXAxis

チャート領域の幅によって最適な X 軸の値の項目数を返す。

function getRecommendedNumberOfTicksForXAxis(availableWidth: number): number;

getRecommendedNumberOfTicksForXAxis の例

import axis = powerbi.extensibility.utils.chart.axis;

public update(options: VisualUpdateOptions) {
...

    let xAxis = d3.svg.axis()
        .scale(xScale)
        .ticks(axis.getRecommendedNumberOfTicksForXAxis(width))
        .orient('bottom');
...

getBestNumberOfTicks

最小値、最大値、メジャーの値、項目数の最大から最適な項目数を返す。

function getBestNumberOfTicks(min: number, max: number, valuesMetadata: DataViewMetadataColumn[], maxTickCount: number, isDateTime?: boolean): number;

getBestNumberOfTicks の例

import axis = powerbi.extensibility.utils.chart.axis;

public update(options: VisualUpdateOptions) {
...
    let yAxis = d3.svg.axis()
        .scale(yAxisScale)
        .orient('left')
        .ticks(axis.getBestNumberOfTicks( 
                this.viewModel.dataMin,
                this.viewModel.dataMax, 
                options.dataViews[0].metadata.columns,
                6)
        );
...

contains

元の文字列が指定した文字列を含むか確認する。

function contains(source: string, substring: string): boolean;

contains の例

import axis = powerbi.extensibility.utils.chart.axis;

axis.contains("Microsoft Power BI Visuals", "Power BI"); // true

getCategoryValueType

カテゴリー列のタイプ (ValueType) を返す。タイプがない場合はテキスト型として処理。

function getCategoryValueType(metadataColumn: DataViewMetadataColumn, isScalar?: boolean): ValueType;

getCategoryValueType の例

import axis = powerbi.extensibility.utils.chart.axis;

public update(options: VisualUpdateOptions) {
...
    let categorySource = options.dataViews[0].categorical.categories[0].source;
    let categoryType = axis.getCategoryValueType(categorySource, true);
...

isOrdinal

type がテキスト、ブーリアン、バーコードまたは郵便番号か確認。詳細はソースコードを参照。

function isOrdinal(type: ValueTypeDescriptor): boolean;

isOrdinal の例

import axis = powerbi.extensibility.utils.chart.axis;

public update(options: VisualUpdateOptions) {
...
    let categorySource = options.dataViews[0].categorical.categories[0].source;
    let categoryType = axis.getCategoryValueType(categorySource, true);
    axis.isOrdinal(categoryType); 
...

isDateTime

type が日付型か確認。

function isDateTime(type: ValueTypeDescriptor): boolean;

isDateTime の例

import axis = powerbi.extensibility.utils.chart.axis;

public update(options: VisualUpdateOptions) {
...
    let categorySource = options.dataViews[0].categorical.categories[0].source;
    let categoryType = axis.getCategoryValueType(categorySource, true);
    axis.isDateTime(categoryType); 
...

getCategoryThickness

実際のカテゴリの幅を取得。

function getCategoryThickness(scale: any): number;

getCategoryThickness の例

import axis = powerbi.extensibility.utils.chart.axis;

    let range = [0, 100];
    let domain = [0, 10];
    let scale = d3.scale.linear().domain(domain).range(range);
    let actualThickness = axis.getCategoryThickness(scale);

createOrdinalScale

D3 の OrdinalScale を作成。

function createOrdinalScale(pixelSpan: number, dataDomain: any[], outerPaddingRatio?: number, innerPaddingRatio?: number, useRangePoints?: boolean): d3.scale.Ordinal<any, any>;

createOrdinalScale の例

import axis = powerbi.extensibility.utils.chart.axis;

public update(options: VisualUpdateOptions) {
...
    let xScale = axis.createOrdinalScale(
        options.viewport.width,
        this.viewModel.dataPoints.map(d => d.category)
    );
...

Legend service

Legend service を使うとカスタムビジュアルに凡例を簡単に表示できます。

powerbi.visuals モジュールは以下の機能を提供します。

createLegend

凡例オブジェクトの作成。描写は別途必要。

function createLegend(
    legendParentElement: HTMLElement,                   // 凡例を配置する親エレメント
    interactive: boolean,                               // 凡例がインタラクティブか指定
    interactivityService: IInteractivityService,        // 凡例をクリックした際のインタラクションのハンドルをするサービス 
    isScrollable: boolean = false,                      // 凡例でスクロールを使うか
    legendPosition: LegendPosition = LegendPosition.Top // 凡例の位置
): ILegend;

createLegend の例

import legend = powerbi.extensibility.utils.chart.legend;
...

export class Visual implements IVisual {

    private seriesLegend: legend.ILegend;

    public init(options: VisualConstructorOptions) {
        ...
        this.interactivityService = createInteractivityService(options.host);

        this.seriesLegend= legend.createLegend(
            options.element,
            options.interactivity && options.interactivity.isInteractiveLegend,
            this.interactivityService,
            true,
            legend.LegendPosition.Left);
        }
...

ILegend

凡例の操作に必要な関数を定義したインターフェース

export interface ILegend {
    getMargins(): IViewport;
    isVisible(): boolean;
    changeOrientation(orientation: LegendPosition): void;
    getOrientation(): LegendPosition;
    drawLegend(data: LegendData, viewport: IViewport): any;
    /**
     * Reset the legend by clearing it
     */
    reset(): void;
}

drawLegend

SVG 上に凡例を描写

function drawLegend(data: LegendData, viewport: IViewport): void;

drawLegend の例

以下の例では表示する凡例データがある場合、設定から取得した位置に凡例を表示。データが無ければ上部に配置して表示しない。

private renderLegend(): void {
    if (!this.isInteractive) {
        let legendObjectProperties = this.data.legendObjectProperties;
        if (legendObjectProperties) {
            let legendData = this.data.legendData;
            LegendData.update(legendData, legendObjectProperties);
            let position = <string>legendObjectProperties[legendProps.position];
            if (position)
                this.legend.changeOrientation(LegendPosition[position]);

            this.legend.drawLegend(legendData, this.parentViewport);
        } else {
            this.legend.changeOrientation(LegendPosition.Top);
            this.legend.drawLegend({ dataPoints: [] }, this.parentViewport);
        }
    }
}

DataLabelUtils

DataLabelUtils データラベルの管理を行うユーティリティー

powerbi.extensibility.utils.chart.dataLabel.utils モジュールは以下の機能を提供

drawDefaultLabelsForDataPointChart

データ用のラベルを描写

function drawDefaultLabelsForDataPointChart(
    data: any[],                   // ラベル用のデータ
    context: d3.Selection<any>,    // ラベルを表示する領域
    layout: ILabelLayout,          // ラベルのデータや位置などのレイアウト
    viewport: IViewport,           // Viewport
    isAnimator?: boolean,          // アニメーションの有無
    animationDuration?: number,    // アニメーション時間
    hasSelection?: boolean,        // 選択されたアイテムの透過率設定
    hideCollidedLabels?: Boolean   // 重なるラベルを非表示にするか指定
  ): d3.selection.Update<any>;

drawDefaultLabelsForDataPointChart の例

データポイントが座標の情報など必要なものを持っている前提。

import dataLabelUtils = powerbi.extensibility.utils.chart.dataLabel.utils;

dataLabelUtils.drawDefaultLabelsForDataPointChart(
    this.viewModel.dataPoints, 
    this.svg, 
    this.getLayout(), 
    this.viewport, 
    true, 
    1, 
    false, 
    false);

// データラベル用のレイアウトを返す
private getLayout(): dataLabel.ILabelLayout {
    return {
        labelText: (dataGroup: BarChartDataPoint) => {
            return dataLabelUtils.getLabelFormattedText({
                label: dataGroup.value,
                maxWidth: this.viewport.width,
            });
        },
        labelLayout: {
            x: (d: BarChartDataPoint) => {
                return d.x;
            },
            y: (d: BarChartDataPoint) => {
                return d.y;
            }
        },
        filter: (dataGroup: BarChartDataPoint) => {
            return true; // フィルターはせず、すべてのデータを返す
        },
        style: {
            "fill": "black",
            "font-family": "helvetica,arial,sans-serif"
        },
    };
}

getLabelFormattedText

ラベルのフォーマットオプションの取得

function getLabelFormattedText(options: LabelFormattedTextOptions): string

getLabelFormattedText の例

import dataLabelUtils = powerbi.extensibility.utils.chart.dataLabel.utils;

let options: LabelFormattedTextOptions = {
    text: 'some text',
    fontFamily: 'sans',
    fontSize: '15',
    fontWeight: 'normal',
};

dataLabelUtils.getLabelFormattedText(options);

まとめ

軸や凡例を描写するのは結構面倒なため、このユーティリティーは使い方を覚えればとても役に立ちます。是非試してください。

目次に戻る

参考

ChartUtils

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?