M. Heeb
December 27, 2023
chart3 = {
const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
// apply fixed positions found in localStorage
const fx = JSON.parse(localStorage.getItem("fixedNodes"));
if (fx && fx.length > 0)
for (const f of fx) {
const i = f.i,
fx = f.fx,
fy = f.fy;
if (i && nodes[i] && fx && fy) {
nodes[i].fx = fx;
nodes[i].fy = fy;
}
}
//create forceSimulation instance
const simulation = d3
.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).strength(d => d.strength))
.force("charge", d3.forceManyBody().strength(-300))
.force('center', d3.forceCenter(width /2, height /2))
.force("x", d3.forceX())
.force("y", d3.forceY());
//.force("link", d3.forceLink(links).id(d => d.id))
//.force("charge", d3.forceManyBody())
//selecting a svg element with D3
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const link = svg
.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", d => d.strength)
.attr("stroke", "blue");
const textElements = svg
.append('g')
.selectAll('text')
.data(nodes)
.enter().append('text')
.text(node => node.label)
.attr('font-size', 15)
.attr('dx', 15)
.attr('dy', 4)
const node = svg
.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", d => d.radius)
.attr("fill", color)
.call(drag(simulation));
svg
.append("text")
.text("Title: This is a graph")
.attr("stroke", "red")
.attr("x", 20)
.attr("y", 20);
node.append("title").text(d => d.id);
//start the simulation and define a tick functions that is executed on every simulation tick
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
textElements
.attr("x", d => d.x)
.attr("y", d => d.y)
node
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("stroke", d => (d.fx ? "#333" : "#fff"))
});
invalidation.then(() => simulation.stop());
return svg.node();
}
drag = simulation => {
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
if (!d.fx) {
d.fx = d.x;
d.fy = d.y;
} else {
d.fx = d.fy = null;
}
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
// save fixed locations to localStorage
localStorage.setItem(
"fixedNodes",
JSON.stringify(
simulation
.nodes()
.map((d, i) => ({ i, fx: d.fx, fy: d.fy }))
.filter(d => d.fx)
)
);
}
return d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
viewof data2 = Inputs.button([
["create new node data", () => data1.nodes.push(JSON.parse(JSON.stringify(noobj)))]
], {label: "create the new node data"})
noobj:
viewof data3 = Inputs.button([
["create new link data", () => data1.links.push(JSON.parse(JSON.stringify(liobj)))]
], {label: "create the new link data"})
liobj:
data1 (gelesen aus File C:-doc-projects-d3-graphen-html-quarto-graph-daten3.json):
data (internes Arbeitsarray für Visualisierung):
sites = await d3.json(
"https://neocities.org/api/info?sitename=youpi", {mode: "cors"}
)
//infos = sites.map(site => {
// const info = site.info;
// return {
// sitename: info.sitename,
// hits: info.hits,
// created_at: info.created_at,
// last_updated: info.last_updated,
// domain: info.domain,
// tags: info.tags
// }
//})
View the infos of the site:
contributors = await d3.json(
"https://api.github.com/repos/pandas-dev/pandas/stats/contributors"
)
commits = contributors.map(contributor => {
const author = contributor.author;
return {
name: author.login,
title: author.login,
group: author.type,
value: contributor.total
}
})
View the data sorted by number of commits:
---
title: "The Graph Editor/Creator (working with tabs)"
author: "M. Heeb"
date: 12.27.2023
format:
html:
toc: false
echo: false
keep-hidden: true
code-tools: true
page-layout: custom
---
```{ojs}
d3 = require("d3@5") //mit d3@7 funktioniert der Graph nicht
topojson = require("topojson")
```
```{ojs}
data1 = FileAttachment("d3-graph-daten3.json").json();
```
```{ojs}
height = 800
```
```{ojs}
Width = 600
```
::: {.panel-tabset}
## graph
```{ojs}
chart3 = {
const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map(d => Object.create(d));
// apply fixed positions found in localStorage
const fx = JSON.parse(localStorage.getItem("fixedNodes"));
if (fx && fx.length > 0)
for (const f of fx) {
const i = f.i,
fx = f.fx,
fy = f.fy;
if (i && nodes[i] && fx && fy) {
nodes[i].fx = fx;
nodes[i].fy = fy;
}
}
//create forceSimulation instance
const simulation = d3
.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).strength(d => d.strength))
.force("charge", d3.forceManyBody().strength(-300))
.force('center', d3.forceCenter(width /2, height /2))
.force("x", d3.forceX())
.force("y", d3.forceY());
//.force("link", d3.forceLink(links).id(d => d.id))
//.force("charge", d3.forceManyBody())
//selecting a svg element with D3
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);
const link = svg
.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", d => d.strength)
.attr("stroke", "blue");
const textElements = svg
.append('g')
.selectAll('text')
.data(nodes)
.enter().append('text')
.text(node => node.label)
.attr('font-size', 15)
.attr('dx', 15)
.attr('dy', 4)
const node = svg
.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", d => d.radius)
.attr("fill", color)
.call(drag(simulation));
svg
.append("text")
.text("Title: This is a graph")
.attr("stroke", "red")
.attr("x", 20)
.attr("y", 20);
node.append("title").text(d => d.id);
//start the simulation and define a tick functions that is executed on every simulation tick
simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
textElements
.attr("x", d => d.x)
.attr("y", d => d.y)
node
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("stroke", d => (d.fx ? "#333" : "#fff"))
});
invalidation.then(() => simulation.stop());
return svg.node();
}
```
```{ojs}
color = {
const scale = d3.scaleOrdinal(d3.schemeCategory10);
return d => scale(d.group);
}
```
```{ojs}
drag = simulation => {
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
if (!d.fx) {
d.fx = d.x;
d.fy = d.y;
} else {
d.fx = d.fy = null;
}
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
// save fixed locations to localStorage
localStorage.setItem(
"fixedNodes",
JSON.stringify(
simulation
.nodes()
.map((d, i) => ({ i, fx: d.fx, fy: d.fy }))
.filter(d => d.fx)
)
);
}
return d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
```
## create new Node data:
```{ojs}
noobj = new Object(); //node-obj
```
```{ojs}
//| panel: input
viewof id = Inputs.text({placeholder: "z.B. a",label: "node id: ", value: "z"})
```
```{ojs}
noobj.id = id;
```
```{ojs}
//| panel: input
viewof group = Inputs.text({placeholder: "z.B. p",label: "group: ", value: "p"})
```
```{ojs}
noobj.group = group;
```
```{ojs}
//| panel: input
viewof label = Inputs.text({placeholder: "z.B. hallo",label: "label: ", value: "z"})
```
```{ojs}
noobj.label = label;
```
```{ojs}
//| panel: input
viewof radius = Inputs.text({placeholder: "z.B. 5",label: "radius: ", value: 5})
```
```{ojs}
noobj.radius = radius;
```
```{ojs}
//| panel: input
viewof data2 = Inputs.button([
["create new node data", () => data1.nodes.push(JSON.parse(JSON.stringify(noobj)))]
], {label: "create the new node data"})
```
noobj:
```{ojs}
noobj
```
## create new Link data:
```{ojs}
liobj = new Object(); //link-obj
var1 = data.nodes.map(d => d.id) // oder d.label)
```
```{ojs}
//| panel: input
viewof source = Inputs.select(var1, {label: "from Source node (id): "})
```
```{ojs}
liobj.source = source;
```
```{ojs}
//| panel: input
viewof target = Inputs.select(var1, {label: "to Target node (id)"})
```
```{ojs}
liobj.target = target;
```
```{ojs}
//| panel: input
viewof strength = Inputs.text({placeholder: "z.B. 1",label: "strenth: ", value: 1})
```
```{ojs}
liobj.strength = strength;
```
```{ojs}
//| panel: input
viewof data3 = Inputs.button([
["create new link data", () => data1.links.push(JSON.parse(JSON.stringify(liobj)))]
], {label: "create the new link data"})
```
liobj:
```{ojs}
liobj
```
## add created data to graph
```{ojs}
//| panel: input
viewof data = Inputs.button([
["Add node- or link-data to the graph", () => data1]
], {value: data1})
```
## data view
data1 (gelesen aus File C:\Users\micha\OneDrive\Documents\R-doc-projects\hemi-d3-graphen-html-quarto\d3-graph-daten3.json):
```{ojs}
data1
```
data (internes Arbeitsarray für Visualisierung):
```{ojs}
data
```
## copy graph data to Clipboard:
```{ojs}
Inputs.button("copy graph data to clipboard", {value: null, reduce: () => navigator.clipboard.writeText(JSON.stringify(data))})
```
## neocity:
```{ojs}
sites = await d3.json(
"https://neocities.org/api/info?sitename=youpi", {mode: "cors"}
)
//infos = sites.map(site => {
// const info = site.info;
// return {
// sitename: info.sitename,
// hits: info.hits,
// created_at: info.created_at,
// last_updated: info.last_updated,
// domain: info.domain,
// tags: info.tags
// }
//})
```
View the infos of the site:
```{ojs}
sites
//Inputs.table(infos, { sort: "sitename", reverse: true })
```
## github REST API:
```{ojs}
contributors = await d3.json(
"https://api.github.com/repos/pandas-dev/pandas/stats/contributors"
)
commits = contributors.map(contributor => {
const author = contributor.author;
return {
name: author.login,
title: author.login,
group: author.type,
value: contributor.total
}
})
```
View the data sorted by number of commits:
```{ojs}
Inputs.table(commits, { sort: "value", reverse: true })
```
:::