Gebruikersdag 2018
Ontwikkel Custom Visuals met D3.jsArthur Graus
Arthur GrausPower BI Trainer | Consultant | [email protected]://www.arthurgraus.nl
.PBIXQ
uery
DAX
Dataset
Report
ODATA RESTHTML
OLEDB MDX SQL
Publish
PowerView
PowerPivot
PowerQuery
DA
X
DA
X
• Install Power BI Visual Tools CLI (pbiviz)
• Create new visual project
• Add external libraries (f.e. D3.js, jQuery, Angular, React)
• Develop in Visual Studio Code
• Test/ debug
• Build and package
• Import in Power BI Report
Demo: HelloWorld Custom Visual
<svg width="200" height="200"><circle cx="30" cy="30" r="20" fill="green"/><circle x="70" cy="70" r="20" fill="purple"/><circle cx="110" cy="100" r="20" fill="red"/>
</svg>
var people = [
{ "x_axis": 30, "y_axis": 30, "radius": 20, "color" : "green" …},
{ "x_axis": 70, "y_axis": 70, "radius": 20, "color" : "purple" …},
{ "x_axis": 110, "y_axis": 100, "radius": 20, "color" : "red“s…}];
var svgContainer = d3.select("body").append("svg")
.attr("width", 200)
.attr("height", 200);
var circles = svgContainer.selectAll("circle") .data(people)
circles.enter().append("circle");
circles.attr("cx", function (d) { return d.x_axis; })
.attr("cy", function (d) { return d.y_axis; })
.attr("r", function (d) { return d.radius; })
.style("fill", function(d) { return d.color; });
circles.exit().remove();
var people = [
{ "x_axis": 30, "y_axis": 30, "radius": 40, "color" : "green" },
{ "x_axis": 70, "y_axis": 70, "radius": 20, "color" : "black"},
{ "x_axis": 110, "y_axis": 100, "radius": 20, "color" : "red"}];
var circles = svgContainer.selectAll("circle") .data(people)
circles.enter().append("circle");
circles.attr("cx", function (d) { return d.x_axis; })
.attr("cy", function (d) { return d.y_axis; })
.attr("r", function (d) { return d.radius; })
.style("fill", function(d) { return d.color; });
circles.exit().remove();
<svg width="200" height="200"><circle cx="30" cy="30" r="20" fill="green"/><circle cx="70" cy="70" r="20" fill="purple"/><circle cx="110" cy="100" r="20" fill="red"/>
</svg>
40black
var people = [
{ "x_axis": 30, "y_axis": 30, "radius": 40, "color" : "green" },
{ "x_axis": 70, "y_axis": 70, "radius": 20, "color" : "black"},
{ "x_axis": 110, "y_axis": 100, "radius": 20, "color" : "red"}];
var circles = svgContainer.selectAll("circle") .data(people);
circles.enter().append("circle");
circles.attr("cx", function (d) { return d.x_axis; })
.attr("cy", function (d) { return d.y_axis; })
.attr("r", function (d) { return d.radius; })
.style("fill", function(d) { return d.color; });
circles.exit().remove();
<svg width="200" height="200"><circle cx="30" cy="30" r="40" fill="green"/><circle cx="70" cy="70" r="20" fill="black"/><circle cx="110" cy="100" r="20" fill="red"/>
</svg>
Demo 01: D3.js Data Binding in action
//A Create Circles (Databinding)...
//B Create Force Simulationthis.simulation = this.d3.forceSimulation();
//C Define forcesthis.simulation
// keep entire simulation balanced around screen center.force('center_nodes', this.d3.forceCenter(this.width / 2, this.height / 2))// avoid collision with padding.force('collision', this.d3.forceCollide(function (d) {return d.r + 5; })
.radius(function (d: any) {return (d.r) * 1.5}));
//D Bind Model Data: Nodesthis.simulation
.nodes(this.people);
//E Register events this.simulation
//each tick all the forces are executed and the x and y positions updated.on('tick', () => { this.circles.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; });})
//simulation is finished.on('end', () => {this.showDebugInfo('Simulation finished, alpha < alphaMin');
});
let people = [{
id: 1,name: "The big boss",color: "Red",r: 50
},{
id: 2,name: "John",color: "Green",r: 30
},{
id: 3,name: "Julia",color: "Yellow",r: 20
},
{id: 4,name: "Mike",color: "Blue",r: 25
},{
id: 5,name: "Jessica",color: "Purple",r: 20
},{
id: 6,name: "Steven",color: "Black",r: 30
}];
Demo 02: Force Layout: Nodes
this.simulation.nodes(this.people);
this.simulation.force('center_nodes', this.d3.forceCenter(this.width / 2, this.height / 2)).force('push_them_apart', this.d3.forceManyBody().strength(100)).force('collision_detect', this.d3.forceCollide()
.radius(function (d: any) {return (d.r) * 1.5}))
.force("link_nodes_together", this.d3.forceLink().links(this.connections).distance(function (d) {
return (d.source.r + d.target.r) * 1.5 }) );
this.simulation.on('tick', () => {this.circles.attr('cx', function (d) { return d.x; })
.attr('cy', function (d) { return d.y; });
this.lines.attr("x1", function (d) {return d.source.x;}).attr("y1", function (d) {return d.source.y;}).attr("x2", function (d) {return d.target.x;}).attr("y2", function (d) {return d.target.y;});
})
let connections = [
{
source: this.people[0],
target: this.people[1]
}, {
source: this.people[0],
target: this.people[2]
}, {
source: this.people[0],
target: this.people[3]
}, {
source: this.people[3],
target: this.people[4]
}, {
source: this.people[3],
target: this.people[5]
}
];
Demo 03: Force Simulation: Nodes + Links
capabilites.json:
"dataRoles": [{
"displayNameKey": "Visual_Source",
"name": "SourceName",
"kind": "Grouping",
"displayName": "Source: Name (required)"
},{
"displayNameKey": "Visual_Target",
"name": "TargetName",
"kind": "Grouping",
"displayName": "Target: Name (required)"
},
...
{
"displayNameKey": "Visual_SourceSize",
"name": "SourceSize",
"kind": "Measure",
"displayName": "Source: Size"
},{
"displayNameKey": "Visual_TargetSize",
"name": "TargetSize",
"kind": "Measure",
"displayName": "Target: Size"
}]
Data View Mappings Documentation
capabilites.json:
"dataViewMappings": [{ "table": {
"rows": {
"select": [
{"for": { "in": "SourceName"}},
{"for": { "in": "SourceSize"}},
{"for": { "in": "SourceColor"}},
{"for": { "in": "TargetName"}},
{"for": { "in": "TargetSize"}},
{"for": { "in": "TargetColor"}}
]
}
}
}]
table.columns:"0": {
"roles": {"SourceName": true},
"type": {...},
"displayName": "ManagerName",
"queryName": "Employees.ManagerName",...
},
"1": {
"roles": {"TargetName": true},
"type": {...},
"displayName": "EmployeeName",
"queryName": "Employees.EmployeeName",
...
},
"2": {
"roles": {"SourceColor": true},
"type": {...},
"displayName": "ManagerDepartment",
"queryName": "Employees.ManagerDepartment",...
},...
table.rows:[
[
null,
"Paul",
null,
"Board",
null,
220000
],
[
"Evy",
"Carleen",
"HR",
"HR",
80000,
45000
]...]
let people = [{
id: 1,name: "Paul",color: "Red",r: 50
},{
id: 2,name: "Evy",color: "Green",r: 30
},{
id: 3,name: "Carleen",color: "Green",r: 16
},
{
];
let connections = [
{
source: null,
target: this.people[0]
}, {
source: this.people[1],
target: this.people[2]
}, {
}, {
source:
target:
}, {
source:
target:
}
];
Demo 04: DataView
Demo 05: Social Network Graph
Gebruikersdag 2018
Bedankt!Vergeet nietde evaluatie in tevullen!Scan de QR code.