зеркало из https://github.com/Azure/hpc-analytics.git
custom visuals for powerbi
This commit is contained in:
Родитель
6533c8633e
Коммит
14d88c1a0b
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"dataRoles": [
|
||||
{
|
||||
"displayName": "Utility",
|
||||
"name": "utility",
|
||||
"kind": "GroupingOrMeasure"
|
||||
}
|
||||
],
|
||||
"dataViewMappings": [
|
||||
{
|
||||
"table": {
|
||||
"rows": {
|
||||
"for": {"in": "utility"}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module powerbi.extensibility.visual {
|
||||
"use strict";
|
||||
import DataViewObjectsParser = powerbi.extensibility.utils.dataview.DataViewObjectsParser;
|
||||
|
||||
export class VisualSettings extends DataViewObjectsParser {
|
||||
public dataPoint: dataPointSettings = new dataPointSettings();
|
||||
}
|
||||
|
||||
export class dataPointSettings {
|
||||
// Default color
|
||||
public defaultColor: string = "";
|
||||
// Show all
|
||||
public showAllDataPoints: boolean = true;
|
||||
// Fill
|
||||
public fill: string = "";
|
||||
// Color saturation
|
||||
public fillRule: string = "";
|
||||
// Text Size
|
||||
public fontSize: number = 12;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* Power BI Visual CLI
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module powerbi.extensibility.visual {
|
||||
"use strict";
|
||||
export class Visual implements IVisual {
|
||||
private host: IVisualHost;
|
||||
private svg: d3.Selection<SVGAElement>;
|
||||
private container: d3.Selection<SVGAElement>;
|
||||
private segments: d3.Selection<SVGAElement>[];
|
||||
private baseLines: d3.Selection<SVGAElement>[];
|
||||
private labels: d3.Selection<SVGAElement>[];
|
||||
|
||||
private dateTextCursors: number[];
|
||||
|
||||
constructor(options: VisualConstructorOptions) {
|
||||
this.svg = d3.select(options.element).append('svg').classed('myVisual', true);
|
||||
this.container = this.svg.append("g").classed('container', true);
|
||||
this.segments = [];
|
||||
this.baseLines = [];
|
||||
this.labels = [];
|
||||
this.dateTextCursors = [];
|
||||
}
|
||||
|
||||
private addDateText(x, y, timeMs) {
|
||||
let dateTextWidth: number = 60;
|
||||
let conflict: boolean = false;
|
||||
for (let cursor of this.dateTextCursors) {
|
||||
if (Math.abs(cursor - x) < dateTextWidth) {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conflict)
|
||||
return;
|
||||
|
||||
let dateTime: Date = new Date(timeMs);
|
||||
let dateString: string = String(dateTime.getMonth() + 1) + "/" +
|
||||
String(dateTime.getDate()) + "/" +
|
||||
String(dateTime.getFullYear());
|
||||
let hours: number = dateTime.getHours();
|
||||
let minutes: number = dateTime.getMinutes();
|
||||
let seconds: number = dateTime.getSeconds();
|
||||
let timeString: string = "";
|
||||
timeString += (hours < 10 ? "0" : "") + String(hours);
|
||||
timeString += (minutes < 10 ? ":0" : ":") + String(minutes);
|
||||
timeString += (seconds < 10 ? ":0" : ":") + String(seconds);
|
||||
let date: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
date.text(dateString)
|
||||
.attr({
|
||||
x: x,
|
||||
y: y,
|
||||
dy: "0.4em",
|
||||
"text-anchor": "middle",
|
||||
"dominant-baseline": "hanging"
|
||||
})
|
||||
.style("font-size", "11px")
|
||||
.style("fill", "Gray");
|
||||
this.labels.push(date);
|
||||
let time: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
time.text(timeString)
|
||||
.attr({
|
||||
x: x,
|
||||
y: y,
|
||||
dy: "1.4em",
|
||||
"text-anchor": "middle",
|
||||
"dominant-baseline": "hanging"
|
||||
})
|
||||
.style("font-size", "11px")
|
||||
.style("fill", "Gray");
|
||||
this.labels.push(time);
|
||||
this.dateTextCursors.push(x);
|
||||
}
|
||||
|
||||
public update(options: VisualUpdateOptions) {
|
||||
let dataView: DataView = options.dataViews[0];
|
||||
let textSize: string = "11px";
|
||||
let textColor: string = "Gray";
|
||||
let width: number = options.viewport.width;
|
||||
let height: number = options.viewport.height;
|
||||
let marginL: number = 30;
|
||||
let marginR: number = 30;
|
||||
let marginT: number = 30;
|
||||
let marginB: number = 30;
|
||||
let rangeX: number = width - marginL - marginR;
|
||||
let rangeY: number = height - marginT - marginB;
|
||||
|
||||
this.svg.attr({
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
for (let segment of this.segments)
|
||||
segment.remove();
|
||||
this.segments = [];
|
||||
for (let line of this.baseLines)
|
||||
line.remove();
|
||||
this.baseLines = [];
|
||||
for (let label of this.labels)
|
||||
label.remove();
|
||||
this.labels = [];
|
||||
this.dateTextCursors = [];
|
||||
|
||||
let rows: number[][] = dataView.table.rows as number[][];
|
||||
|
||||
let minX: number = rows[0][0];
|
||||
let maxX: number = rows[rows.length - 1][0];
|
||||
let maxUsage: number = 0;
|
||||
for (let row of rows) {
|
||||
maxUsage = Math.max(row[2], row[3], maxUsage);
|
||||
}
|
||||
let step: number = 8;
|
||||
let rectWidth: number = 8;
|
||||
|
||||
let shares: number = Math.floor(maxUsage / step);
|
||||
for (let i = 0; i < shares + 1; i++) {
|
||||
let lineX: d3.Selection<SVGAElement> = this.container.append("line").classed("line", true);
|
||||
lineX.attr("x1", marginL - 10)
|
||||
.attr("y1", marginT + rangeY * (maxUsage - step * i) / maxUsage)
|
||||
.attr("x2", marginL + rangeX + 10)
|
||||
.attr("y2", marginT + rangeY * (maxUsage - step * i) / maxUsage)
|
||||
.attr("stroke-width", 1.5)
|
||||
.attr("stroke", "LightGray");
|
||||
this.baseLines.push(lineX);
|
||||
let label: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
label.text(String(step * i))
|
||||
.attr({
|
||||
x: marginL - 10,
|
||||
y: marginT + rangeY * (maxUsage - step * i) / maxUsage,
|
||||
dx: "-0.4em",
|
||||
"text-anchor": "end",
|
||||
"dominant-baseline": "central"
|
||||
})
|
||||
.style("font-size", textSize)
|
||||
.style("fill", textColor);
|
||||
this.labels.push(label);
|
||||
}
|
||||
|
||||
let passedNodes: number[] = [];
|
||||
let colors: string[] = [ "#6182A2", "#788E3E", "#EA812D", "#554640", "#9DAEB0", "#a2c3a4",
|
||||
"#AE5A31", "#B9AC78", "#2A4539", "#7F2F29", "#2e5266", "#313b72" ];
|
||||
let nextColor: number = 0;
|
||||
let colorTextCursor: number = 10;
|
||||
let colorTextWidth: number = 50;
|
||||
let jobColors: {Id: number, Color: string}[] = [];
|
||||
for (let row of rows) {
|
||||
let nodeId: number = row[1];
|
||||
let passed: boolean = false;
|
||||
for (let id of passedNodes) {
|
||||
if (id == nodeId) {
|
||||
passed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (passed)
|
||||
continue;
|
||||
|
||||
passedNodes.push(nodeId);
|
||||
if (nodeId == 0)
|
||||
continue;
|
||||
|
||||
let jobId = row[1];
|
||||
let color: string = "";
|
||||
for (let jobColor of jobColors) {
|
||||
if (jobColor.Id == jobId) {
|
||||
color = jobColor.Color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (color == "") {
|
||||
color = colors[nextColor % colors.length];
|
||||
nextColor++;
|
||||
jobColors.push({Id: jobId, Color: color});
|
||||
|
||||
if (nextColor < colors.length) {
|
||||
let room: number = 10;
|
||||
let circle: d3.Selection<SVGAElement> = this.container.append("circle").classed("circle", true);
|
||||
circle.attr({
|
||||
cx: colorTextCursor,
|
||||
cy: room,
|
||||
r: 5,
|
||||
})
|
||||
.attr("fill", color);
|
||||
this.labels.push(circle);
|
||||
|
||||
let label: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
label.text(String(jobId))
|
||||
.attr({
|
||||
x: colorTextCursor,
|
||||
y: room,
|
||||
dx: "0.5em",
|
||||
dy: "0.1em",
|
||||
"text-anchor": "start",
|
||||
"dominant-baseline": "middle"
|
||||
})
|
||||
.style("font-size", textSize)
|
||||
.style("fill", textColor);
|
||||
this.labels.push(label);
|
||||
colorTextCursor += colorTextWidth;
|
||||
}
|
||||
}
|
||||
|
||||
let prevX: number = -1;
|
||||
let prevDirection: number = 1;
|
||||
for (let row of rows) {
|
||||
if (row[1] != nodeId)
|
||||
continue;
|
||||
|
||||
let currTimeMs: number = row[0];
|
||||
let prevUsage: number = row[2];
|
||||
let currUsage: number = row[3];
|
||||
let currX: number = marginL + Math.round(rangeX * (currTimeMs - minX) / (maxX - minX));
|
||||
let prevY: number = marginT + Math.round(rangeY * (maxUsage - prevUsage) / maxUsage);
|
||||
let currY: number = marginT + Math.round(rangeY * (maxUsage - currUsage) / maxUsage);
|
||||
|
||||
if (prevX != -1) {
|
||||
let line: d3.Selection<SVGAElement> = this.container.append("line").classed("line", true);
|
||||
line.attr("x1", String(prevX))
|
||||
.attr("y1", String(prevY))
|
||||
.attr("x2", String(currX))
|
||||
.attr("y2", String(prevY))
|
||||
.attr("stroke-width", 3)
|
||||
.attr("stroke", color);
|
||||
this.segments.push(line);
|
||||
}
|
||||
|
||||
let rectX: number = 0;
|
||||
let rectY: number = 0;
|
||||
let rectHeight: number = 0;
|
||||
let currDirection: number = 0;
|
||||
if (prevY > currY) {
|
||||
rectX = currX;
|
||||
rectY = currY;
|
||||
rectHeight = prevY - currY + 1;
|
||||
currDirection = -1;
|
||||
} else {
|
||||
rectX = currX - 8;
|
||||
rectY = prevY;
|
||||
rectHeight = currY - prevY + 1;
|
||||
currDirection = 1;
|
||||
}
|
||||
if (prevX == -1 || prevDirection == currDirection || currX - prevX > rectWidth) {
|
||||
let rect: d3.Selection<SVGAElement> = this.container.append("rect").classed("rect", true);
|
||||
rect.attr("x", String(rectX))
|
||||
.attr("y", String(rectY))
|
||||
.attr("width", String(rectWidth))
|
||||
.attr("height", String(rectHeight))
|
||||
.attr("fill", color);
|
||||
this.segments.push(rect);
|
||||
prevDirection = currDirection;
|
||||
prevX = currX;
|
||||
}
|
||||
|
||||
this.addDateText(currX, marginT + rangeY, currTimeMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"dataRoles": [
|
||||
{
|
||||
"displayName": "Utility",
|
||||
"name": "utility",
|
||||
"kind": "GroupingOrMeasure"
|
||||
}
|
||||
],
|
||||
"dataViewMappings": [
|
||||
{
|
||||
"table": {
|
||||
"rows": {
|
||||
"for": {"in": "utility"}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module powerbi.extensibility.visual {
|
||||
"use strict";
|
||||
import DataViewObjectsParser = powerbi.extensibility.utils.dataview.DataViewObjectsParser;
|
||||
|
||||
export class VisualSettings extends DataViewObjectsParser {
|
||||
public dataPoint: dataPointSettings = new dataPointSettings();
|
||||
}
|
||||
|
||||
export class dataPointSettings {
|
||||
// Default color
|
||||
public defaultColor: string = "";
|
||||
// Show all
|
||||
public showAllDataPoints: boolean = true;
|
||||
// Fill
|
||||
public fill: string = "";
|
||||
// Color saturation
|
||||
public fillRule: string = "";
|
||||
// Text Size
|
||||
public fontSize: number = 12;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* Power BI Visual CLI
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module powerbi.extensibility.visual {
|
||||
"use strict";
|
||||
export class Visual implements IVisual {
|
||||
private host: IVisualHost;
|
||||
private svg: d3.Selection<SVGAElement>;
|
||||
private container: d3.Selection<SVGAElement>;
|
||||
private rectangles: d3.Selection<SVGAElement>[];
|
||||
private baseLines: d3.Selection<SVGAElement>[];
|
||||
private labels: d3.Selection<SVGAElement>[];
|
||||
|
||||
private dateTextCursors: number[];
|
||||
|
||||
constructor(options: VisualConstructorOptions) {
|
||||
this.svg = d3.select(options.element).append('svg').classed('myVisual', true);
|
||||
this.container = this.svg.append("g").classed('container', true);
|
||||
this.rectangles = [];
|
||||
this.baseLines = [];
|
||||
this.labels = [];
|
||||
|
||||
this.dateTextCursors = [];
|
||||
}
|
||||
|
||||
private addDateText(x, y, timeMs) {
|
||||
let dateTextWidth: number = 60;
|
||||
let conflict: boolean = false;
|
||||
for (let cursor of this.dateTextCursors) {
|
||||
if (Math.abs(cursor - x) < dateTextWidth) {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conflict)
|
||||
return;
|
||||
|
||||
let dateTime: Date = new Date(timeMs);
|
||||
let dateString: string = String(dateTime.getMonth() + 1) + "/" +
|
||||
String(dateTime.getDate()) + "/" +
|
||||
String(dateTime.getFullYear());
|
||||
let hours: number = dateTime.getHours();
|
||||
let minutes: number = dateTime.getMinutes();
|
||||
let seconds: number = dateTime.getSeconds();
|
||||
let timeString: string = "";
|
||||
timeString += (hours < 10 ? "0" : "") + String(hours);
|
||||
timeString += (minutes < 10 ? ":0" : ":") + String(minutes);
|
||||
timeString += (seconds < 10 ? ":0" : ":") + String(seconds);
|
||||
let date: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
date.text(dateString)
|
||||
.attr({
|
||||
x: x,
|
||||
y: y,
|
||||
dy: "0.4em",
|
||||
"text-anchor": "middle",
|
||||
"dominant-baseline": "hanging"
|
||||
})
|
||||
.style("font-size", "11px")
|
||||
.style("fill", "Gray");
|
||||
this.labels.push(date);
|
||||
let time: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
time.text(timeString)
|
||||
.attr({
|
||||
x: x,
|
||||
y: y,
|
||||
dy: "1.4em",
|
||||
"text-anchor": "middle",
|
||||
"dominant-baseline": "hanging"
|
||||
})
|
||||
.style("font-size", "11px")
|
||||
.style("fill", "Gray");
|
||||
this.labels.push(time);
|
||||
this.dateTextCursors.push(x);
|
||||
}
|
||||
|
||||
public update(options: VisualUpdateOptions) {
|
||||
let dataView: DataView = options.dataViews[0];
|
||||
let textSize: string = "11px";
|
||||
let textColor: string = "Gray";
|
||||
let width: number = options.viewport.width;
|
||||
let height: number = options.viewport.height;
|
||||
let marginL: number = 50;
|
||||
let marginR: number = 30;
|
||||
let marginT: number = 30;
|
||||
let marginB: number = 30;
|
||||
let rangeX: number = width - marginL - marginR;
|
||||
let rangeY: number = height - marginT - marginB;
|
||||
|
||||
this.svg.attr({
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
for (let rect of this.rectangles)
|
||||
rect.remove();
|
||||
this.rectangles = [];
|
||||
for (let line of this.baseLines)
|
||||
line.remove();
|
||||
this.baseLines = [];
|
||||
for (let label of this.labels)
|
||||
label.remove();
|
||||
this.labels = [];
|
||||
this.dateTextCursors = [];
|
||||
|
||||
let rows: number[][] = dataView.table.rows as number[][];
|
||||
|
||||
interface Node {
|
||||
Id: number,
|
||||
Size: number,
|
||||
Base: number,
|
||||
};
|
||||
|
||||
let nodes: Node[] = [];
|
||||
let minX: number = -1;
|
||||
let maxX: number = -1;
|
||||
for (let row of rows) {
|
||||
let nodeId: number = row[2];
|
||||
let nodeSize: number = row[3];
|
||||
let passed: boolean = false;
|
||||
for (let node of nodes) {
|
||||
if (node.Id == nodeId) {
|
||||
passed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!passed)
|
||||
nodes.push({Id: nodeId, Size: nodeSize, Base: 0});
|
||||
|
||||
if (minX == -1) {
|
||||
minX = row[0];
|
||||
maxX = row[1];
|
||||
} else {
|
||||
minX = Math.min(row[0], minX);
|
||||
maxX = Math.max(row[1], maxX);
|
||||
}
|
||||
}
|
||||
|
||||
nodes.sort(function(a, b) {
|
||||
if (a.Id < b.Id)
|
||||
return -1;
|
||||
if (a.Id > b.Id)
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
let interval: number = 1 / 4;
|
||||
let totalShares: number = -interval;
|
||||
for (let node of nodes) {
|
||||
node.Base = totalShares + interval;
|
||||
totalShares += node.Size + interval;
|
||||
}
|
||||
|
||||
for (let node of nodes) {
|
||||
if (node.Base == 0) {
|
||||
let lineX: d3.Selection<SVGAElement> = this.container.append("line").classed("line", true);
|
||||
lineX.attr("x1", marginL - 10)
|
||||
.attr("y1", marginT + rangeY)
|
||||
.attr("x2", marginL + rangeX + 10)
|
||||
.attr("y2", marginT + rangeY)
|
||||
.attr("stroke-width", 1.5)
|
||||
.attr("stroke", "LightGray");
|
||||
this.labels.push(lineX);
|
||||
}
|
||||
let lineX: d3.Selection<SVGAElement> = this.container.append("line").classed("line", true);
|
||||
lineX.attr("x1", marginL - 10)
|
||||
.attr("y1", marginT + rangeY * (totalShares - (node.Base + node.Size)) / totalShares - 2)
|
||||
.attr("x2", marginL + rangeX + 10)
|
||||
.attr("y2", marginT + rangeY * (totalShares - (node.Base + node.Size)) / totalShares - 2)
|
||||
.attr("stroke-width", 1.5)
|
||||
.attr("stroke", "LightGray");
|
||||
this.labels.push(lineX);
|
||||
|
||||
let label: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
label.text(String(node.Id))
|
||||
.attr({
|
||||
x: marginL - 10,
|
||||
y: marginT + rangeY * (totalShares - (node.Base + node.Size / 2)) / totalShares,
|
||||
dx: "-0.8em",
|
||||
"text-anchor": "end",
|
||||
"dominant-baseline": "central"
|
||||
})
|
||||
.style("font-size", textSize)
|
||||
.style("fill", textColor);
|
||||
this.labels.push(label);
|
||||
}
|
||||
|
||||
let colors: string[] = [ "#6182A2", "#788E3E", "#EA812D", "#554640", "#9DAEB0", "#a2c3a4",
|
||||
"#AE5A31", "#B9AC78", "#2A4539", "#7F2F29", "#2e5266", "#313b72" ];
|
||||
let nextColor: number = 0;
|
||||
let colorTextCursor: number = 10;
|
||||
let colorTextWidth: number = 50;
|
||||
let jobColors: {Id: number, Color: string}[] = [];
|
||||
for (let row of rows) {
|
||||
let startTimeMs: number = row[0];
|
||||
let endTimeMs: number = row[1];
|
||||
let node: {Id: number, Size: number, Base: number} = null;
|
||||
for (let n of nodes) {
|
||||
if (n.Id == row[2]) {
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
let coreId: number = row[4];
|
||||
let jobId: number = row[5];
|
||||
|
||||
let color: string = "";
|
||||
for (let jobColor of jobColors) {
|
||||
if (jobColor.Id == jobId) {
|
||||
color = jobColor.Color;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (color == "") {
|
||||
color = colors[nextColor % colors.length];
|
||||
nextColor++;
|
||||
jobColors.push({Id: jobId, Color: color});
|
||||
|
||||
if (nextColor < colors.length) {
|
||||
let room: number = 10;
|
||||
let circle: d3.Selection<SVGAElement> = this.container.append("circle").classed("circle", true);
|
||||
circle.attr({
|
||||
cx: colorTextCursor,
|
||||
cy: room,
|
||||
r: 5,
|
||||
})
|
||||
.attr("fill", color);
|
||||
this.labels.push(circle);
|
||||
|
||||
let label: d3.Selection<SVGAElement> = this.container.append("text").classed("text", true);
|
||||
label.text(String(jobId))
|
||||
.attr({
|
||||
x: colorTextCursor,
|
||||
y: room,
|
||||
dx: "0.5em",
|
||||
dy: "0.1em",
|
||||
"text-anchor": "start",
|
||||
"dominant-baseline": "middle"
|
||||
})
|
||||
.style("font-size", textSize)
|
||||
.style("fill", textColor);
|
||||
this.labels.push(label);
|
||||
colorTextCursor += colorTextWidth;
|
||||
}
|
||||
}
|
||||
|
||||
let rectX: number = Math.round(marginL + rangeX * (startTimeMs - minX) / (maxX - minX));
|
||||
let rectY: number = Math.round(marginT + rangeY * (totalShares - (node.Base + coreId) - 1) / totalShares);
|
||||
let rectWidth: number = Math.round(rangeX * (endTimeMs - startTimeMs) / (maxX - minX));
|
||||
let rectHeight: number = Math.round(rangeY * 1 / totalShares - 1.5);
|
||||
if (rectWidth == 0)
|
||||
rectWidth = 1;
|
||||
let rect: d3.Selection<SVGAElement> = this.container.append("rect").classed("rect", true);
|
||||
rect.attr("rx", "4")
|
||||
.attr("ry", "4")
|
||||
.attr("x", String(rectX))
|
||||
.attr("y", String(rectY))
|
||||
.attr("width", String(rectWidth))
|
||||
.attr("height", String(rectHeight))
|
||||
.attr("fill", color);
|
||||
this.rectangles.push(rect);
|
||||
|
||||
this.addDateText(rectX, marginT + rangeY, startTimeMs);
|
||||
this.addDateText(rectX + rectWidth, marginT + rangeY, endTimeMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче