我正在尝试为Power BI制作此视觉效果,但是遇到一个奇怪的问题。这应该是支持在X和Y轴上均具有度量的条形图(将转换为折线图)。当我尝试用我的数据绘图时,我得到了一个没有错误的空白视觉。我添加的格式设置选项(例如工具提示或切换选项)甚至都不会出现在视觉格式选项窗格中。我已经搞砸了,无论我做什么,我都无法做到这一点,也无法真正改变它,只是缺少将随机字符放入代码中以破坏语法的能力。我什至尝试使用过去曾修改过的较旧的可视文件,并确保我没有做错任何事情,例如滥用模块或放置不当。我什至重新导入了我所有的模块,检查了变量,检查了错别字,等等。我什至尝试删除功能中的一部分,该部分允许将度量放入轴中,以查看其是否可以使用法线值列进行绘制。不幸的是无济于事。 也许我的眼睛停滞了,我错过了明显的东西,或者比这更复杂的东西。
非常感谢您的帮助。即使这与问题无关。
"use strict";
import "@babel/polyfill";
import "./../style/visual.less";
import powerbi from "powerbi-visuals-api";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions;
import VisualObjectInstance = powerbi.VisualObjectInstance;
import DataView = powerbi.DataView;
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
import * as d3 from "d3";
import { VisualSettings } from "./settings";
import ISelectionManager = powerbi.extensibility.ISelectionManager;
import { ChartDataPoint, ChartViewModel } from "./viewmodels/model";
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
import * as DataViewObject from 'powerbi-visuals-utils-dataviewutils';
interface DataPoints {
duration: number;
value: number;
details: number;
wells: string;
colour: string;
identity: powerbi.visuals.ISelectionId;
highlighted: boolean;
};
interface ViewModel {
dataPoints: DataPoints[];
maxValue: number;
highlights: boolean;
};
export class Visual implements IVisual {
private host: IVisualHost;
private svg: d3.Selection<SVGElement>;
private barGroup: d3.Selection<SVGElement>;
private viewModel: ViewModel;
private locale: string;
private selectionManager: ISelectionManager;
private xAxisGroup: d3.Selection<SVGElement>;
private yAxisGroup: d3.Selection<SVGElement>;
private settings = {
axis: {
x: {
padding: {
default: 50,
value: 50
},
show: {
default: true,
value: true
}
},
y: {
padding: {
default: 50,
value: 50
}
},
border: {
top: {
default: 10,
value: 10
}
}
}
}
constructor(options: VisualConstructorOptions) {
this.host = options.host;
this.svg = d3.select(options.element)
.append(".svg")
.classed("Visual", true);
this.barGroup = this.svg.append("g")
.classed("bar-group", true); //this was chart
this.xAxisGroup = this.svg.append("g")
.classed("x-axis", true);
this.selectionManager = this.host.createSelectionManager();
this.yAxisGroup = this.svg.append("g")
.classed("y-axis", true);
}
//This contains the 'canvas', its scaling, and how it can or cannot interact
public update(options: VisualUpdateOptions) {
//this.updateSettings(options);
let viewModel = this.getViewModel(options);
let width = options.viewport.width;
let height = options.viewport.height;
let xAxisPadding = this.settings.axis.x.show.value ? this.settings.axis.x.padding.value : 0;
// let yAxisPadding = this.settings.axis.y.show.value ? this.settings.axis.y.padding.value : 0;
this.svg.attr({
width: width,
height: height
});
let yScale = d3.scale.linear()
.domain([0, this.viewModel.maxValue])
.range([height - xAxisPadding, 0 + this.settings.axis.border.top.value]);
let xScale = d3.scale.linear()
.domain(viewModel.dataPoints.map(d => d.duration))
// .rangeRoundBands([yAxisPadding, width], this.xPadding);
let xAxis = d3.svg.axis() //come back to this later if it causes issues. https://www.youtube.com/watch?v=zLNfXxDsa-s&list=PL6z9i4iVbl8C2mtjFlH3ECb3q00eFDLAG&index=14 3:40 in
.scale(xScale)
.orient("bottom")
.tickSize(.5);
let yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.tickSize(.5);
this.xAxisGroup
.call(xAxis)
.attr({
transform: "translate(0 " + (height - xAxisPadding) + ")"
})
.style({
fill: "#777777"
})
.selectAll("text")
.attr({
"text-anchor": "end",
"font-size": "x-small"
});
this.yAxisGroup
.call(yAxis)
.attr({
transform: "translate(" + this.settings.axis.y.padding + ",0)"
})
.style({
fill: "#777777"
})
.selectAll("text")
.style({
"text-anchor": "end",
"font-size": "x-small"
});
let bars = this.barGroup
.selectAll(".bar") //keep an eye on this. was '.lines'
.data(viewModel.dataPoints);
bars.enter()
.append("svg")
.classed("bar", true); //this was chart
bars
.attr({
//width: xScale.range(),
height: d => height = yScale(d.value) - xAxisPadding,
x: d => xScale(d.duration),
})
.style({
fill: d => d.colour,
"fill-opacity": d => viewModel.highlights ? d.highlighted ? 1.0 : 0.5 : 1.0
})
.on("click", (d) => {
this.selectionManager
.select(d.identity, true)
.then(ids => {
bars.style({
"fill-opacity": ids.length > 0 ?
d => ids.indexOf(d.identity) >= 0 ? 1.0 : 0.5
: 1.0
});
})
});
bars.exit()
.remove();
}
/* private updateSettings(options: VisualUpdateOptions) {
this.settings.axis.x.show.value = DataViewObjects.getValue
(options.dataViews[0].metadata.objects, {
objectName: "xAxis",
propertyName: "show"
})
}*/
private getViewModel(options: VisualUpdateOptions): ViewModel {
let dv = options.dataViews;
let viewModel: ViewModel = {
dataPoints: [],
maxValue: 0,
highlights: false
};
/* if (!dv
|| !dv[0]
|| !dv[0].categorical
|| !dv[0].categorical.categories
|| !dv[0].categorical.categories[0].source
|| !dv[0].categorical.values)
return viewModel;*/
let view = dv[0].categorical;
let categories = view.categories[0];
let values = view.values[0];
let highlights = values.highlights;
for (let i = 0, len = Math.max(categories.values.length, values.values.length); i < len; i++) {
viewModel.dataPoints.push({
duration: <number>values.values[i],
value: <number>values.values[i],
details: <number>categories.values[i],
wells: <string>categories.values[i],
colour: this.host.colorPalette.getColor(<string>categories.values[i]).value,
identity: this.host.createSelectionIdBuilder()
.withCategory(categories, i)
.createSelectionId(),
highlighted: highlights ? highlights[i] ? true : false : false
});
}
viewModel.maxValue = d3.max(viewModel.dataPoints, d => d.value);
viewModel.highlights = viewModel.dataPoints.filter(d => d.highlighted).length > 0;
return viewModel;
}
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
let propertyGroupName = options.objectName;
let properties: VisualObjectInstance[] = [];
switch (propertyGroupName) {
case "xAxisGroup":
properties.push({
objectName: propertyGroupName,
properties: {
show: this.settings.axis.x.show.value
},
selector: null
});
break;
};
return properties
}
}
对于任何好奇的人,我的最终目标(最终)是要有一个可以支持任一轴上的度量(计算列)的折线图。例如,X轴的日期非常普遍。我还希望能够使用可以采用日期范围(例如9/5 / 2019-9 / 10/2019)的度量并将其转换为持续时间。预期输出为1-5。
例如,Y轴度量可以执行以下操作。.例如,显示满足某些参数的所有值。
{
"supportsHighlight": true,
"dataRoles": [
{
"displayName": "Y Axis",
"name": "values",
"kind": "Grouping"
},
{
"displayName": "Details",
"name": "details",
"kind": "Grouping"
},
{
"displayName": "Duration",
"name": "duration",
"kind": "Measure"
}
],
"objects": {
"xAxis": {
"displayName": "X Axis",
"properties": {
"show": {
"displayName": "Show X Axis",
"type": {
"bool": true
}
}
}
},
"dataPoint": {
"displayName": "Data colors",
"properties": {
"defaultColor": {
"displayName": "Default color",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"showAllDataPoints": {
"displayName": "Show all",
"type": {
"bool": true
}
},
"fill": {
"displayName": "Fill",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"fillRule": {
"displayName": "Color saturation",
"type": {
"fill": {}
}
},
"fontSize": {
"displayName": "Text Size",
"type": {
"formatting": {
"fontSize": true
}
}
}
}
}
},
"dataViewMappings": [
{
"categorical": {
"categories": {
"for": {
"in": "duration"
},
"dataReductionAlgorithm": {
"top": {
"count": 450
}
}
},
"values": {
"group": {
"by": "values",
"select": [
{
"bind": {
"to": "details"
}
}
]
},
"dataReductionAlgorithm": {
"top": {
"count": 450
}
}
}
}
}
]
}
答案 0 :(得分:0)
根据评论,挑战的一部分是获得y轴,该轴在顶部绘制最小的数字,在底部绘制最大的数字。这可以通过custom format-string for a measure完成。这是使用标准折线图的快速而又肮脏的模型。
如果将度量格式设置为“自定义”,并使用不带“-”的格式字符串表示负数,例如“ 0; 0; 0”,视觉效果将不会在y轴上显示“-”。这会带来您想要的效果。
下面请注意,度量为-1 * SUM ( 'Table'[Column1] )
。该列仅包含正值。