Skip to content

Instantly share code, notes, and snippets.

@sineline
Last active August 8, 2018 14:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sineline/f4f4074919777079d59cbf7551bc1d47 to your computer and use it in GitHub Desktop.
Save sineline/f4f4074919777079d59cbf7551bc1d47 to your computer and use it in GitHub Desktop.
PBI Pies Array
{
"dataRoles": [{
"displayName": "Category Data",
"name": "Category",
"kind": "Grouping"
},
{
"displayName": "Paths",
"name": "Paths",
"kind": "Grouping",
"description": "The value used to customize the shape of the data points",
"requiredTypes": [{
"text": true
}]
},
{
"displayName": "Values",
"name": "Values",
"kind": "Measure",
"requiredTypes": [{
"numeric": true
}, {
"integer": true
}]
},
{
"displayName": "Measure",
"name": "measure",
"kind": "Measure",
"requiredTypes": [{
"numeric": true
}, {
"integer": false
}]
},
{
"displayName": "Measure Color",
"name": "measureColor",
"kind": "Measure",
"requiredTypes": [{
"numeric": true
}, {
"integer": false
}]
}
],
"dataViewMappings": [{
"conditions": [{
"Category": {
"min": 0,
"max": 3
},
"Paths": {
"min": 0,
"max": 1
},
"Values": {
"min": 0,
"max": 1
},
"Measure": {
"min": 0,
"max": 1
}
}],
"categorical": {
"categories": {
"for": {
"in": "Category"
},
"dataReductionAlgorithm": {
"top": {}
}
},
"values": {
"group": {
"by": "Paths",
"select": [{
"for": {
"in": "Values"
}
},
{
"for": {
"in": "Category"
}
}
],
"dataReductionAlgorithm": {
"top": {}
}
}
}
}
},
{
"categorical": {
"categories": {
"for": {
"in": "Category"
},
"dataReductionAlgorithm": {
"top": {}
}
},
"values": {
"select": [{
"bind": {
"to": "measure"
}
},
{
"bind": {
"to": "measureColor"
}
}
]
}
}
}
],
"objects": {
"colorSelector": {
"displayName": "Data Colors",
"properties": {
"fill": {
"displayName": "Color",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
}
}
},
"generalView": {
"displayName": "General View",
"properties": {
"opacity": {
"displayName": "Bars Opacity",
"type": {
"integer": true
}
},
"showHelpLink": {
"displayName": "Show Help Button",
"type": {
"bool": true
}
}
}
},
"fontFamily": {
"displayName": "Font",
"properties": {
"MarkerShape": {
"displayName": "Marker shape",
"type": {
"enumeration": [{
"displayName": "Default",
"description": "helvetica, arial, sans-serif",
"value": "helvetica, arial, sans-serif"
},
{
"displayName": "Arial",
"value": "Arial"
},
{
"displayName": "Arial Black",
"value": "\"Arial Black\""
}
]
}
}
}
},
"arcsFormatting": {
"displayName": "Bold Column Formatting",
"properties": {
"fill": {
"displayName": "Color",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
}
}
},
"columnFormatting_original": {
"displayName": "Bold Column Formatting Original",
"properties": {
"fontBold": {
"displayName": "Font Bold",
"type": { "bool": true }
}
}
},
"stackColorsFormatting": {
"displayName": "stackColorsFormatting",
"properties": {
"fill": {
"displayName": "Color",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
}
}
}
},
"tooltips": {
"supportedTypes": {
"default": true,
"canvas": true
},
"roles": [
"Tooltips"
]
}
}
/*
* Power BI Visualizations
*
* Copyright (c) Microsoft Corporation
* All rights reserved.
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the ""Software""), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace powerbi.extensibility.visual {
// powerbi.extensibility.utils.tooltip
import TooltipEnabledDataPoint = powerbi.extensibility.utils.tooltip.TooltipEnabledDataPoint;
// powerbi.extensibility.utils.interactivity
import SelectableDataPoint = powerbi.extensibility.utils.interactivity.SelectableDataPoint;
export interface pieChartData {
dataPoints: pieChartDataPoint[]; // Flat data points
}
export interface pieChartDataPoint extends TooltipEnabledDataPoint,SelectableDataPoint {
quantity?: number;
color?: string;
category?: string;
arcCategory?: DataViewCategoricalColumn;
identity: DataViewValueColumns;
key: string;
highlight?: boolean;
index?: number;
selector?: ISelectionId;
}
export interface Data {
quantity: number;
category: string;
colour: string;
}
export interface PieChartLayout {
rows: number;
columns: number;
totalArea: number;
}
export interface PieChartViewModel {
count: number;
labelsArray: Array<string>;
category: DataViewCategoryColumn;
values: DataViewValueColumns;
paths: Array<string>;
arcs?: DataViewMatrixNode;
arcStacks?: any;
}
export interface SinglePieChartInitOptions {
root: d3.Selection<Element>;
path: string;
}
export interface SinglePieChartUpdateOptions {
x: number;
y: number;
width: number;
height: number;
fontFamily?: string;
value: number;
pieChartData: any;
text: string;
category: DataViewCategoryColumn;
path: DataViewCategoryColumn;
categoryIndex: number;
measure: any;
arcs?: DataViewMatrixNode;
dataPoints?: pieChartData; // Flat data points
SelManager?: ISelectionManager;
host?: IVisualHost;
}
export interface ISinglePieChart {
init(options: SinglePieChartInitOptions): void;
destroy?(): void;
update?(options: SinglePieChartUpdateOptions): void;
}
}
{
"visual": {
"name": "PieGridChart",
"displayName": "PieGridChart",
"visualClassName": "Visual",
"version": "0.9.0",
"description": "Automatic grod of pie charts based on code from Waffle chart /github.com/kiewic/PowerBI-WaffleChart",
"supportUrl": "",
"gitHubUrl": "",
"guid": "PieGridChartA77870CAA4064FECA027036FD6AA68F5"
},
"apiVersion": "1.13.0",
"author": {
"name": "",
"email": "sineline"
},
"assets": {
"icon": "assets/icon.png"
},
"externalJS": [
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.js",
"node_modules/powerbi-visuals-utils-colorutils/lib/index.js",
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
"node_modules/d3/d3.min.js",
"node_modules/lodash/lodash.min.js",
"node_modules/globalize/lib/globalize.js",
"node_modules/globalize/lib/cultures/globalize.culture.en-US.js",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.js",
"node_modules/powerbi-visuals-utils-svgutils/lib/index.js",
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
"node_modules/powerbi-visuals-utils-formattingutils/lib/index.js"
],
"style": "style/visual.less",
"capabilities": "capabilities.json",
"dependencies": "dependencies.json"
}
{
"name": "visual",
"dependencies": {
"d3": "3.5.5",
"lodash": "^4.17.10",
"powerbi-models": "^1.0.8",
"powerbi-visuals-utils-chartutils": "^1.7.0",
"powerbi-visuals-utils-colorutils": "^1.1.0",
"powerbi-visuals-utils-dataviewutils": "^1.4.1",
"powerbi-visuals-utils-formattingutils": "^3.0.2",
"powerbi-visuals-utils-interactivityutils": "^3.2.0",
"powerbi-visuals-utils-svgutils": "^1.1.0",
"powerbi-visuals-utils-tooltiputils": "^1.0.1",
"powerbi-visuals-utils-typeutils": "^1.1.0",
"tslint": "^3.15.1"
},
"devDependencies": {
"@types/d3": "3.5.36",
"karma-typescript-preprocessor": "0.3.1",
"powerbi-visuals-tools": "1.13.1",
"typescript": "2.9.2",
"typings": "^2.1.1"
}
}
{
"compilerOptions": {
"allowJs": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "ES5",
"sourceMap": true,
"out": "./.tmp/build/visual.js"
},
"files": [
".api/v1.13.0/PowerBI-visuals.d.ts",
"node_modules/powerbi-models/dist/models-noexports.d.ts",
"node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-tooltiputils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-svgutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-formattingutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-colorutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-chartutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-interactivityutils/lib/index.d.ts",
"src/settings.ts",
"src/visual.ts",
"src/dataInterfaces.ts"
]
}
/*
* Power BI Visual CLI
*/
module powerbi.extensibility.visual {
import DataViewObjects = powerbi.extensibility.utils.dataview.DataViewObjects;
import DataRoleHelper = powerbi.extensibility.utils.dataview.DataRoleHelper;
import DataViewObjectsParser = powerbi.extensibility.utils.dataview.DataViewObjectsParser;
// powerbi.extensibility
import IVisual = powerbi.extensibility.IVisual;
import IColorPalette = powerbi.extensibility.IColorPalette;
// powerbi.visuals
import ISelectionId = powerbi.visuals.ISelectionId;
// powerbi.extensibility.utils.color
import ColorHelper = powerbi.extensibility.utils.color.ColorHelper;
// powerbi.extensibility.utils.type
import PixelConverter = powerbi.extensibility.utils.type.PixelConverter;
// powerbi.extensibility.utils.tooltip
import TooltipEventArgs = powerbi.extensibility.utils.tooltip.TooltipEventArgs;
import ITooltipServiceWrapper = powerbi.extensibility.utils.tooltip.ITooltipServiceWrapper;
import TooltipEnabledDataPoint = powerbi.extensibility.utils.tooltip.TooltipEnabledDataPoint;
import createTooltipServiceWrapper = powerbi.extensibility.utils.tooltip.createTooltipServiceWrapper;
// powerbi.extensibility.utils.svg
import translate = powerbi.extensibility.utils.svg.translate;
import CssConstants = powerbi.extensibility.utils.svg.CssConstants;
import ClassAndSelector = powerbi.extensibility.utils.svg.CssConstants.ClassAndSelector;
import createClassAndSelector = powerbi.extensibility.utils.svg.CssConstants.createClassAndSelector;
// powerbi.extensibility.utils.formatting
import valueFormatter = powerbi.extensibility.utils.formatting.valueFormatter;
import IValueFormatter = powerbi.extensibility.utils.formatting.IValueFormatter;
// powerbi.extensibility.utils.chart.legend
import createLegend = powerbi.extensibility.utils.chart.legend.createLegend;
import ILegend = powerbi.extensibility.utils.chart.legend.ILegend;
import Legend = powerbi.extensibility.utils.chart.legend;
import LegendData = powerbi.extensibility.utils.chart.legend.LegendData;
import LegendIcon = powerbi.extensibility.utils.chart.legend.LegendIcon;
import LegendPosition = powerbi.extensibility.utils.chart.legend.LegendPosition;
// powerbi.extensibility.utils.interactivity
import IInteractiveBehavior = powerbi.extensibility.utils.interactivity.IInteractiveBehavior;
import IInteractivityService = powerbi.extensibility.utils.interactivity.IInteractivityService;
// DevTools do not support yet `export const PieChartRoleNames`.
var PieChartRoleNames = {
category: 'Category',
values: 'Values',
measure: 'Neasure',
maxValue: 'MaxValue',
paths: 'Paths'
};
// DevTools do not support yet `export const PieChartProps`.
var PieChartProps = {
dataPoint: {
defaultColor: <DataViewObjectPropertyIdentifier>{ objectName: 'dataPoint', propertyName: 'defaultColor' },
fill: { objectName: 'dataPoint', propertyName: 'fill' }
},
};
export class Visual implements IVisual {
private target: HTMLElement;
private host: IVisualHost;
private updateCount: number;
private settings: pieChartsSettings;
private static DefaultText = "Invalid DV";
private root: d3.Selection<Element>;
private dataView: DataView;
private defaultFont: string;
public stackOriginalFields: Array<any>;
private singlePieChartArray: Array<SinglePieChart>;
private count: number;
private defaultDataPointColor: string;
public selectionManager: ISelectionManager;
public selectionIdBuilder: ISelectionIdBuilder;
private colorPalette: IColorPalette;
private dataPointColors: any;
private category;
private static LegendPropertyIdentifier: DataViewObjectPropertyIdentifier = { objectName: "group", propertyName: "fill" };
constructor(options: VisualConstructorOptions) {
this.target = options.element;
this.host = options.host;
this.updateCount = 0;
this.settings = this.settings;
this.defaultDataPointColor = "Coral";
this.selectionManager = options.host.createSelectionManager();
this.root = d3.select(options.element).append("svg");
this.colorPalette = options.host.colorPalette;
this.selectionIdBuilder = options.host.createSelectionIdBuilder();
}
public update(options: VisualUpdateOptions) {
var debugMessage = undefined;
//this.settings.Array = Visual.parseSettings(options && options.dataViews && options.dataViews[0]);
if (!options.dataViews || !options.dataViews[0]) return;
var dataView = (this.dataView = options.dataViews[0]);
var dataViewAggregate = (this.dataView = options.dataViews[1]);
var viewport = options.viewport;
var viewModel: PieChartViewModel = Visual.converter(dataView);
viewModel.paths = dataView.categorical.values
.grouped()
["__proto__"].map(function(d) {
return d.values[0]["source"].groupName;
});
this.category = dataView.categorical.values
.grouped()
["__proto__"].map(function(d) {
return d.values[0]["source"].groupName;
});
console.log("ding");
let dataViews = options.dataViews; //options: VisualUpdateOptions
let categorical = dataViews[0].categorical;
let dataValues = categorical.values;
var dada = dataViews[0].matrix.rows.root.children;
let colorPalette: IColorPalette = this.host.colorPalette;
for(let dataValue of dataValues) {
let values = dataValue.values;
for(let i = 0, len = dataValue.values.length; i < len; i++) {
let selectionId = this.host.createSelectionIdBuilder()
.withCategory(categorical.categories[0], i)
.withMeasure(dataValue.source.queryName)
.withSeries(categorical.values, categorical.values[i])
.createSelectionId();
console.log("selectionId",selectionId);
}
}
this.initPies(viewModel.count, viewModel.arcs);
this.initSelectionManager(viewModel);
if (dataView.metadata && dataView.metadata.objects) {
var defaultColor = DataViewObjects.getFillColor(dataView.metadata.objects, PieChartProps.dataPoint.defaultColor);
if (defaultColor) {
this.defaultDataPointColor = defaultColor;
}
}
this.root.attr({
height: viewport.height,
width: viewport.width
});
var bestLayout = Visual.getBestLayout(options.viewport, this.count);
var PieViewport = Visual.getPieSize({
width: options.viewport.width / bestLayout.columns,
height: options.viewport.height / bestLayout.rows
});
function generateDataPoints(objeto, indice, host) {
// let selectionId = this.host.createSelectionIdBuilder()
// .withCategory(categorical.categories[0], i)
// .withMeasure(dataValue.source.queryName)
// .withSeries(categorical.values, categorical.values[i])
// .createSelectionId();
let caca: pieChartData = { dataPoints: objeto.categorical.values.map(
function(d, i) {
var cat = objeto.categorical.categories[indice];
console.log("cat", cat);
var temp: pieChartDataPoint = {
identity: d.identity,
quantity: Number(d.values[indice]),
category: cat,
color: host.colorPalette.getColor(
d["__proto__"].source.groupName
).value,
arcCategory: d["__proto__"].source.groupName,
key: d.identity.key,
highlight: false,
selected: false,
index: i,
selector: host.createSelectionIdBuilder().withMeasure(d.source.queryName).withSeries(d, categorical.values[indice]).createSelectionId()
// selector: this.host.createSelectionIdBuilder().withCategory(d.category, indice).createSelectionId()
};
return temp;
}
) };
return caca;
}
var globalX = viewport.width / 2 - (PieViewport.width * bestLayout.columns) / 2;
var globalY = viewport.height / 2 - (PieViewport.height * bestLayout.rows) / 2;
for (var i = 0; i < this.count; i++) {
var localX = globalX + PieViewport.width * (i % bestLayout.columns);
var localY = globalY + PieViewport.height * Math.floor(i / bestLayout.columns);
console.log("rebuilding singlePieChartObj");
var singlePieChartObj = {
x: localX,
y: localY,
path: null,
measure: !isNaN(Number(dataViewAggregate.categorical.values[0].values[i])) ? Number(Math.round(Number(String(dataViewAggregate.categorical.values[0].values[i]) + "e2")) + "e-2") : dataViewAggregate.categorical.values[0].values[i],
width: PieViewport.width,
height: PieViewport.height,
fontFamily: this.defaultFont,
value: null,
category: dataView.categorical.categories[i],
pieChartData: dataView.categorical.values.map(function(d) { return d.values[i];}),
text: viewModel.labelsArray && viewModel.labelsArray[i] ? viewModel.labelsArray[i] : "(Blank)",
//category: dataView.categorical.values.grouped()["__proto__"].map(function(d) {return d.values[0]["source"].groupName;}),
categoryIndex: i,
arcs: dataView.matrix.columns.root,
dataPoints: null,
SelManager: this.selectionManager,
host: this.host
}; //generateDataPoints(dataView,i), // Flat data points
singlePieChartObj.dataPoints = generateDataPoints(dataView, i, this.host);
this.singlePieChartArray[i].update(singlePieChartObj);
}
}
private static formatCategoryValue(value: PrimitiveValue | null, type: ValueTypeDescriptor): string {
if (type.dateTime) {
let formatter: IValueFormatter;
formatter = valueFormatter.create({
format: "O",
value: value,
value2: value,
tickCount: 6
});
return formatter.format(value);
}
return value && value.toString();
}
public static converter(dataView: DataView): PieChartViewModel {
var labelsArray: Array<string> = [];
var arcsArray: Array<string> = [];
var category0: DataViewCategoryColumn;
var arcCategories: DataViewCategoryColumn;
var arcIdentities: DataViewScopeIdentity[];
var valueColumns: DataViewValueColumns;
// var arcIdentities = viewModel.arcs.children.map(function(d) { return d.identity })
// viewModel.arcs = arcIdentities;
console.log("wtf");
if (dataView.categorical.categories && dataView.categorical.categories.length > 0) {
let categories = dataView.categorical.categories;
let values = dataView.categorical.values;
category0 = categories[0];
valueColumns = values;
var rowsCount = category0.values.length;
labelsArray = [];
for (let i = 0; i < rowsCount; i++) {
let labelPieces: Array<string> = [];
for (let j = 0; j < categories.length; j++) {
labelPieces.push(this.formatCategoryValue(categories[j].values[i], categories[j].source.type));
}
labelsArray.push(labelPieces.join("|"));
}
var sliceCount = dataView.categorical.values.grouped();
for (let j = 0; j < sliceCount.length; j++) {
arcsArray.push(this.formatCategoryValue(sliceCount[j].values[0]["__proto__"].source.groupName, sliceCount[j].values[0]["__proto__"].source.type));
}
}
var paths: Array<string>;
var totals: Array<number>;
var count: number;
count = labelsArray.length;
// if (dada.length > 0 ) {
// for(var i=0; i < this.ColumnValues.length; i++)
// {
// let defaultColor: Fill = {
// solid: {
// color: colorPalette.getColor(this.ColumnValues[i] + '').value
// }
// };
// var objn = { FieldName: this.ColumnValues[i] };
// objn["Color"] = Visual.getCategoricalObjectValue<Fill>(this.ColumnValues[i], i, 'colorSelector', 'fill', defaultColor).solid.color,
// objn["LegendLabel"] = this.ColumnValues[i];
// objn["selectionId"] = Visual.getSelectionIds(element.dataViews[0],hst)
// this.stackOriginalFields.push(objn);
// }
// }
var viewModel: PieChartViewModel = { labelsArray: labelsArray, category: category0, values: valueColumns, paths: paths, count: count };
//arcs: arcPieces
return viewModel;
}
private initPies(newCount: number, paths: DataViewMatrixNode): void {
// We need to remove all Pies before creating new ones.
if (this.singlePieChartArray) {
for (var i = 0; i < this.count; i++) {
this.singlePieChartArray[i].remove();
}
}
this.count = newCount;
// Create new Pies.
this.singlePieChartArray = new Array();
for (var i = 0; i < this.count; i++) {
this.singlePieChartArray[i] = new SinglePieChart();
this.singlePieChartArray[i].init({
root: this.root,
path: paths ? paths[i] : null
});
}
}
private initSelectionManager(category: PieChartViewModel) {
var innerText = this.root.selectAll(" g.pieChart > g.innerCircle > text");
var innerCircle = this.root.selectAll(" g.pieChart > g.innerCircle > circle");
var Title = this.root.selectAll("g.Title > text");
if (!category) {
return;
}
console.log("initSelectionManager");
// Bound data with join-by-index.
innerText.data(category.category.identity);
innerCircle.data(category.category.identity);
Title.data(category.category.identity);
// Using a local variable to avoid errors later when using 'this'.
var selectionManager = this.selectionManager;
var host = this.host;
var selections = [innerText, innerCircle, Title];
this.root.on("click", function() {
selectionManager.clear().then(() => {
// innerCircle.style('opacity', 1),
innerText.style("opacity", 1), Title.style("opacity", 1);
});
});
Title.on("click", function(d) {
let index = category.category.identity.indexOf(d);
let selectionId = host
.createSelectionIdBuilder()
.withCategory(category.category, index)
.createSelectionId();
let event = <MouseEvent>d3.event;
selectionManager
.select(
selectionId,
event.ctrlKey || event.metaKey
)
.then(ids => {
let selectedElements = Title.filter(d => {
let localIndex = category.category.identity.indexOf(
d
);
let localId = host
.createSelectionIdBuilder()
.withCategory(category.category, localIndex)
.createSelectionId();
return ids.some((id: ISelectionId) =>
(<visuals.ISelectionId>id).equals(localId)
);
});
if (ids.length > 0) {
Title.style("opacity", 0.4);
selectedElements.style("opacity", 1);
} else {
Title.style("opacity", 1);
}
});
(<Event>d3.event).stopPropagation();
});
innerText.on("click", function(d) {
let index = category.category.identity.indexOf(d);
let selectionId = host
.createSelectionIdBuilder()
.withCategory(category.category, index)
.createSelectionId();
let event = <MouseEvent>d3.event;
selectionManager
.select(
selectionId,
event.ctrlKey || event.metaKey
)
.then(ids => {
let selectedElements = innerText.filter(d => {
let localIndex = category.category.identity.indexOf(
d
);
let localId = host
.createSelectionIdBuilder()
.withCategory(category.category, localIndex)
.createSelectionId();
return ids.some((id: ISelectionId) =>
(<visuals.ISelectionId>id).equals(localId)
);
});
if (ids.length > 0) {
innerText.style("opacity", 0.4);
selectedElements.style("opacity", 1);
} else {
innerText.style("opacity", 1);
}
});
(<Event>d3.event).stopPropagation();
});
}
private getColor(objects: DataViewObjects, defaultDataPointColor: string): string {
var colorHelper = new ColorHelper(this.colorPalette, PieChartProps.dataPoint.fill, defaultDataPointColor);
return colorHelper.getColorForMeasure(objects, "");
}
private static getBestLayout(viewport: IViewport, count: number): PieChartLayout {
var bestLayout = { rows: 0, columns: 0, totalArea: 0 };
// Find the best layout (rows x columns) by choosing the layout with larger area.
for (var i = 1; i <= count; i++) {
for (var j = 1; j <= count; j++) {
if (i * j >= count) {
var currentArea: PieChartLayout = Visual.calculateArea(viewport, count, i, j);
if (currentArea.totalArea > bestLayout.totalArea) {
bestLayout = currentArea;
}
}
}
}
return bestLayout;
}
private static calculateArea(viewport: IViewport, count: number, rows: number, columns: number): PieChartLayout {
var PieViewport: IViewport = Visual.getPieSize({
width: viewport.width / columns,
height: viewport.height / rows
});
var totalArea = PieViewport.width * PieViewport.height * count;
return { rows: rows, columns: columns, totalArea: totalArea };
}
private static getPieSize(viewport: IViewport): IViewport {
// Cross-multiplications:
// 600 : 800
// viewport.width : height
// width : viewport.height
if ((viewport.width * 800) / 600 <= viewport.height) {
return { width: viewport.width, height: (viewport.width * 800) / 600 };
}
return { width: (viewport.height * 600) / 800, height: viewport.height };
}
// public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
// let objectName: string = options.objectName;
// let objectEnumeration: VisualObjectInstance[] = [];
// console.log(" Visual ", VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options));
// return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
// }
/**
* Enumerates through the objects defined in the capabilities and adds the properties to the format pane
*
* @function
* @param {EnumerateVisualObjectInstancesOptions} options - Map of defined objects
*/
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration {
let objectName = options.objectName;
let objectEnumeration: VisualObjectInstance[] = [];
console.log("Enumerating:", objectName);
switch (objectName) {
case "enableAxis":
objectEnumeration.push({
objectName: objectName,
properties: {
show: "", // this.barChartSettings.enableAxis.show,
fill: "" // this.barChartSettings.enableAxis.fill,
},
selector: null
});
break;
case "colorSelector":
for (let ArcDataPoint of this.category) {
objectEnumeration.push({
objectName: ArcDataPoint,
displayName: ArcDataPoint,
properties: {
fill: {
solid: {
color: "red"
}
}
},
selector: ArcDataPoint // ArcDataPoint.selectionId.getSelector()
});
}
break;
case "fontFamily":
console.log("fontFamily");
objectEnumeration.push({
objectName: options.objectName,
properties: {
MarkerShape: this.defaultFont
},
selector: null
});
break;
case "generalView":
objectEnumeration.push({
objectName: objectName,
properties: {
opacity: "", // this.barChartSettings.generalView.opacity,
showHelpLink: "" //this.barChartSettings.generalView.showHelpLink
},
validValues: {
opacity: {
numberRange: {
min: 10,
max: 100
}
}
},
selector: null
});
break;
case "arcsFormatting":
//https://community.powerbi.com/t5/Developer/Create-Custom-visuals-Set-bar-color/td-p/408006
console.log("arcs f");
break;
case "columnFormatting_original":
var metadataColumns: DataViewMatrixNode[] = this.dataView.matrix.rows.root.children;
for (var i = 0; i < metadataColumns.length; i++) {
//var currentColumn: DataViewMetadataColumn = metadataColumns[i];
objectEnumeration.push({
objectName: objectName,
displayName: null, // currentColumn.groupName.toString(),
properties: {
fontBold: null
},
selector: { metadata: null }
});
}
break;
case "stackColorsFormatting":
console.log("declarations of enumeration");
var metadataColumns2: DataViewMetadataColumn[] = this.dataView.metadata.columns.filter( function(d) { if (d.groupName) { return true; } } );
var matrixColumns: DataViewMatrixNode = this.dataView.matrix.columns.root;
var categoryValues: DataViewValueColumns = this.dataView.categorical.values["__proto__"].filter( function(d) { if (d.identity) { return true; } } );
var c = this.stackOriginalFields;
console.log("dumping stack", c);
for (let barDataPoint of metadataColumns2) {
console.log("create selection id?");
objectEnumeration.push({
objectName: objectName,
displayName: barDataPoint.groupName.toString(),
properties: {
fill:this.getArcColor<object>(barDataPoint.objects,objectName,"fill",null)
},
//selector: barDataPoint.LegendLabel
//selector:barDataPoint.selectionId.getSelector() //Its not working
//selector:barDataPoint.selectionId.getSelector()
// this.selectionIdBuilder.withColumnIdentity(barDataPoint).createSelectionId()
selector: { metadata: barDataPoint.queryName }
});
}
break;
}
return objectEnumeration;
}
/**
* Destroy runs when the visual is removed. Any cleanup that the visual needs to
* do should be done here.
*
* @function
*/
private getTooltipData(value: any): VisualTooltipDataItem[] {
//let language = getLocalizedString(this.locale, "LanguageKey");
return [{ displayName: value.category, value: value.value.toString(), color: value.color, header: "header" }]; // language && "displayed language " + language
}
private createHelpLinkElement(): Element {
let linkElement = document.createElement("a");
linkElement.textContent = "?";
linkElement.setAttribute("title", "Open documentation");
linkElement.setAttribute("class", "helpLink");
linkElement.addEventListener("click", () => {
this.host.launchUrl("https://github.com/Microsoft/PowerBI-visuals/blob/master/Readme.md#developing-your-first-powerbi-visual");
});
return linkElement;
}
public destroy(): void {
this.root = null;
for (var i = 0; i < this.count; i++) {
this.singlePieChartArray[i].destroy();
}
}
public getValue<T>(objects: DataViewScopeIdentity, objectName: string, propertyName: string, defaultValue: T): T {
//https://community.powerbi.com/t5/Developer/custom-visual-enumeration-issues/td-p/146866
console.log("getValue was called");
if (objects) {
let object = objects[objectName];
if (object) {
let property: T = <T>object[propertyName];
if (property !== undefined) {
return property;
}
}
}
return defaultValue;
}
public getArcColor<T>(objects: DataViewObject, objectName: string, propertyName: string, defaultValue: T): T {
//https://community.powerbi.com/t5/Developer/custom-visual-enumeration-issues/td-p/146866
console.log("getArcColor was called");
if (objects) {
let object = objects[objectName];
if (object) {
let property: T = <T>object[propertyName];
if (property !== undefined) {
return property;
}
}
}
return defaultValue;
}
public static getCategoricalObjectValue<T>(category: DataViewCategoryColumn, index: number, objectName: string, propertyName: string, defaultValue: T): T {
let categoryObjects = category.objects;
if (categoryObjects) {
let categoryObject: DataViewObject = categoryObjects[index];
if (categoryObject) {
let object = categoryObject[objectName];
if (object) {
console.log("object" + object);
let property: T = <T>object[propertyName];
if (property !== undefined) {
return property;
}
}
}
}
return defaultValue;
}
private static getSelectionIds(dataView: DataView, host: IVisualHost): powerbi.visuals.ISelectionId[] {
const queryName: string = dataView.table.columns[0].queryName;
return dataView.table.identity.map(
(identity: DataViewScopeIdentity) => {
const categoryColumn: DataViewCategoryColumn = {
source: {
queryName,
displayName: null
},
values: null,
identity: [identity]
};
return host
.createSelectionIdBuilder()
.withCategory(categoryColumn, 0)
.withMeasure(queryName)
.createSelectionId();
}
);
}
}
export class SinglePieChart implements ISinglePieChart {
private PieG: d3.Selection<Element>;
private PieGH: d3.Selection<Element>;
private backgroundRect: d3.Selection<Element>;
private dotG: d3.Selection<Element>;
private dotBackgroundRect: d3.Selection<Element>;
private dotRect: SVGRect;
private dotArray: Array<Array<d3.Selection<Element>>>;
private textG: d3.Selection<Element>;
private percentageText: d3.Selection<Element>;
private descText: d3.Selection<Element>;
private options: SinglePieChartUpdateOptions;
private target: HTMLElement;
private textNode: Text;
private pieChartContainer: d3.Selection<SVGElement>;
private arc: d3.svg.Arc<pieChartDataPoint>;;
private host: IVisualHost;
private svg: d3.Selection<SVGElement>;
private g: any;
private pie: any;
private selectionManager: ISelectionManager;
public remove() {
this.PieG.remove();
}
public init(options: SinglePieChartInitOptions): void {
this.PieG = options.root.append('g');
this.backgroundRect = this.PieG
.append('rect');
this.PieG.attr('class', 'singlePie');
this.PieGH = this.PieG.append("g").classed("pieChart", true);;
// Add a class to query this groups by class.
let svg = this.svg = this.PieG.selectAll("g");
this.dotG = this.PieGH;
this.dotBackgroundRect = this.PieGH.append('rect');
this.textG = this.PieG.append('g').attr("class", "Title");
this.descText = this.textG
.append('text')
.style('cursor', 'pointer')
.style('stroke', 'Red')
.style('stroke-width', '0px')
.attr('text-anchor', 'middle');
}
public update(options: SinglePieChartUpdateOptions) {
console.log("here maybe");
this.options = options;
let Data: pieChartDataPoint[] = options.dataPoints.dataPoints
let width = options.width;
let height = options.height;
let radius = Math.min(width, height) / 2.1;
this.svg.attr({
width: width,
height: height,
});
let colourValues = d3.scale.category20c();
this.backgroundRect
.style({
fill: 'transparent',
'width': options.width,
'height': options.height,
});
var dotGMargin = 0;
var PieMargin = 20;
var dotGX = dotGMargin;
var dotGY = dotGMargin;
var dotGHeight = Math.min(options.height, options.width);
var chartSide = 0;
if (dotGHeight >= dotGMargin * 2) {
chartSide = dotGHeight - dotGMargin * 2;
}
this.dotBackgroundRect.style({
//'fill': 'LightCyan',
fill: 'transparent',
'width': chartSide,
'height': chartSide,
});
console.log("liada");
var insideHolderSize = radius - 0.5;
this.arc = d3.svg.arc<pieChartDataPoint>()
.innerRadius(insideHolderSize)
.outerRadius(radius - PieMargin);
this.pie = d3.layout.pie<pieChartDataPoint>()
.value((slice: pieChartDataPoint) => {
return slice.quantity;
})
.value(function(d) {
console.log(d);
return d.quantity});
let fill = (d =>
//colourValues(d.data.category)
d.data.color
);
let tf = d => `translate(${this.arc.centroid(d)})`;
let text = d => d.data.category;
this.g = this.PieGH.selectAll('.arc')
.data(this.pie( options.dataPoints.dataPoints))
.enter()
.append('g').attr('transform', 'translate(' + chartSide / 2 + ',' + chartSide / 2 + ')')
.attr('class', 'arc')
.data(this.pie(Data)).attr("align", "center");
this.g.append('path')
.attr('d', this.arc)
.attr('fill', fill)
.style("stroke", "transparent")
.style("stroke-width", "0.8");
this.PieG.attr('transform', 'matrix(1 0 0 1 ' + options.x + ' ' + options.y + ')');
var descTextY = chartSide + 10 / 2 + 10;
this.descText.style({
'fill': 'DarkSlateGray',
// 'font-size': 25 + 'px',
'font-family': options.fontFamily,
}).text(this.options.text).style("font-size", function (d) {
return Math.min(radius, radius / this.getComputedTextLength() * 28) + "px";
})
// .attr("y", ".35em")
.attr({
'y': descTextY + 'px',
'x': options.width / 2,
});
var centerBubbleH = this.svg
.append("g")
.attr("class", "innerCircle");
centerBubbleH
.append("circle")
.attr("r", insideHolderSize * 0.20)
.style("fill", "transparent")
.attr("transform", "translate(" + chartSide / 2 + "," + chartSide / 2 + ")");
centerBubbleH
.append("text")
.style("font-size", function(d) {
return Math.min(radius - 10, (radius / this.getComputedTextLength()) * 24) + "px";
})
.attr("y", ".35em")
.text(this.options.measure)
.attr("transform", "translate(" + chartSide / 2 + "," + chartSide / 2 + ")")
.style("cursor", "pointer")
.style("stroke", "Red")
.style("stroke-width", "0px")
.attr("text-anchor", "middle");
this.initPieSelectionManager(options);
}
private initPieSelectionManager(category:SinglePieChartUpdateOptions) {
console.log("ArcInit", category);
var Arcs = this.PieGH.selectAll(".arc");
if (!category) {
return;
}
console.log("identities object here");
Arcs.data(category.dataPoints.dataPoints);
var selectionManager = category.SelManager;
var host = category.host;
Arcs.on('click', function (d) {
console.log("Arc click");
let index = d.index;
let selectionId = d.selector;
//let selectionId = host.createSelectionIdBuilder().withSeries(categorical.values,valueGroupsValue).createSelectionId();
let event = <MouseEvent>d3.event;
selectionManager.select(selectionId, event.ctrlKey || event.metaKey).then(ids => {
let selectedElements = Arcs.filter(d => {
let localIndex = d.index; //.indexOf(d);
let localId = host.createSelectionIdBuilder().withCategory(d.identity, d.index).createSelectionId();
return ids.some((id: ISelectionId) => (<visuals.ISelectionId>id).equals(localId));
});
if (ids.length > 0) {
Arcs.style('opacity', 0.4);
selectedElements.style('opacity', 0.4);
} else {
Arcs.style('opacity', 1);
}
});
(<Event>d3.event).stopPropagation();
});
}
public destroy(): void {
}
public getText(): string {
return this.options.text;
}
public getIdentity(): DataViewScopeIdentity {
return this.options.category.identity[this.options.categoryIndex];
}
public getColor(): string {
return null; // this.options.color;
}
public getFont(): string {
return this.options.fontFamily; // this.options.color;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment