1

我正在尝试为 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
            }
          }
        }
      }
    }

  ]
}
4

1 回答 1

0

根据评论,部分挑战是获得一个 y 轴,在顶部绘制最小的数字,在底部绘制最大的数字。这可以通过一个自定义格式字符串来实现。这是一个使用标准折线图的快速而肮脏的模型。

如果将度量格式设置为“自定义”并使用不带“-”的格式字符串作为负数,例如“0;0;0”,则视觉对象将不会在 y 轴上显示“-”。这会导致您正在寻找的效果。

请注意下面的度量是-1 * SUM ( 'Table'[Column1] )。该列仅包含正值。

在此处输入图像描述

在此处输入图像描述

于 2019-11-21T01:39:19.413 回答