-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Changes to intro page - embedded from observable Signed-off-by: Orhun Ucak <[email protected]> * Prettier website and enabled iframe in config Signed-off-by: Orhun Ucak <[email protected]> * New line + Signed-off-by test Signed-off-by: Orhun Ucak <[email protected]> * Second Signoff test Signed-off-by: Orhun Ucak <[email protected]> * Third Signoff test Signed-off-by: Orhun Ucak <[email protected]> * Image was changed Signed-off-by: Orhun Ucak <[email protected]> * Image paths corrected Signed-off-by: Orhun Ucak <[email protected]> * Observable abandoned for 3 plots Signed-off-by: Orhun Ucak <[email protected]> * Added newlines to html Signed-off-by: Orhun Ucak <[email protected]> * Added newlines to html Signed-off-by: Orhun Ucak <[email protected]> * Added newlines at the ends of the data files Signed-off-by: Orhun Ucak <[email protected]> * Ran pre-commit Signed-off-by: Orhun Ucak <[email protected]> --------- Signed-off-by: Orhun Ucak <[email protected]>
- Loading branch information
1 parent
3088fb0
commit 147467e
Showing
12 changed files
with
7,060 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
<div id="chart-container" style="height: 550px; "> | ||
<div id="dataset-selector" style="margin-bottom: 20px;"></div> | ||
<div id="donut"></div> | ||
</div> | ||
<script type="module"> | ||
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm"; | ||
import * as Plot from "https://cdn.jsdelivr.net/npm/@observablehq/[email protected]/+esm"; | ||
|
||
console.log("Plot.js script loaded"); | ||
|
||
console.log("Attempting to load CSV data from /data"); | ||
d3.csv("/vehicle_signal_specification/data/piechartnotexpanded.csv").then(data => { | ||
console.log("CSV Data loaded successfully:", data); | ||
|
||
const width = 800; // Assuming a fixed width for the chart | ||
const height = Math.min(500, width / 2); | ||
const outerRadius = height / 2 - 10; | ||
const innerRadius = outerRadius * 0.70; | ||
|
||
const colorMap = { | ||
"Attribute": "#06D6A0", // Green for attributes | ||
"Branches": "#26547C", // Blue for nodes with children | ||
"Actuators": "#EF476F", // Red for actuators | ||
"Sensors": "#FFD166", // Yellow for sensors | ||
|
||
}; | ||
|
||
const svg = d3.create("svg") | ||
.attr("viewBox", [-width / 2, -height / 2, width, height]); | ||
|
||
svg.append("title") | ||
.text("Pie Chart Showing Distribution of Data Categories"); | ||
|
||
const arc = d3.arc() | ||
.innerRadius(innerRadius) | ||
.outerRadius(outerRadius); | ||
|
||
const pie = d3.pie().sort(null).value(d => d.value); | ||
|
||
const path = svg.append("g") | ||
.selectAll("path"); | ||
|
||
// Update chart function | ||
function updateChart(dataset) { | ||
console.log(`Updating chart with dataset: ${dataset}`); | ||
const dataTransformed = data.map(d => ({ type: d.Type, value: d[dataset] })); | ||
const pieData = pie(dataTransformed); | ||
|
||
const totalValue = d3.sum(dataTransformed, d => d.value); | ||
|
||
path.data(pieData) | ||
.join( | ||
enter => enter.append("path") | ||
.attr("fill", d => colorMap[d.data.type] || "#ccc") | ||
.attr("d", arc) | ||
.each(function(d) { this._current = d; }), | ||
update => update | ||
.transition().duration(750) | ||
.attrTween("d", arcTween), | ||
exit => exit.remove() | ||
); | ||
|
||
// Add text labels, excluding those with a value of 0 | ||
svg.selectAll("text").remove(); // Remove previous text elements | ||
|
||
svg.append("g") | ||
.attr("font-family", "sans-serif") | ||
.attr("font-size", 10) | ||
.attr("font-weight", "bold") | ||
.attr("text-anchor", "middle") | ||
.attr("fill", "white") | ||
.selectAll("text") | ||
.data(pieData.filter(d => d.data.value > 0)) // Filter out segments with value 0 | ||
.join("text") | ||
.attr("transform", d => `translate(${arc.centroid(d)})`) | ||
.call(text => text.append("tspan") | ||
.attr("y", "-0.4em") | ||
.text(d => d.data.type)) | ||
.call(text => text.filter(d => (d.endAngle - d.startAngle) > 0.25).append("tspan") | ||
.attr("x", 0) | ||
.attr("y", "0.7em") | ||
.attr("fill-opacity", 0.7) | ||
.text(d => d.data.value.toLocaleString("en-US"))); | ||
|
||
// Add total value at the center of the chart | ||
svg.append("text") | ||
.attr("font-family", "sans-serif") | ||
.attr("font-size", 50) | ||
.attr("font-weight", "bold") | ||
.attr("text-anchor", "middle") | ||
.attr("fill", "#333333") | ||
.attr("dy", "0.35em") | ||
.text(totalValue); | ||
svg.append("text") | ||
.attr("font-family", "sans-serif") | ||
.attr("font-size", 16) | ||
.attr("font-weight", "bold") | ||
.attr("text-anchor", "middle") | ||
.attr("fill", "gray") | ||
.attr("dy", "3.2em") | ||
.text("total"); | ||
} | ||
|
||
// Store the displayed angles in _current. | ||
// Then, interpolate from _current to the new angles. | ||
// During the transition, _current is updated in-place by d3.interpolate. | ||
function arcTween(a) { | ||
const i = d3.interpolate(this._current, a); | ||
this._current = i(0); | ||
return t => arc(i(t)); | ||
} | ||
|
||
// Initialize the chart with the default dataset "V5" | ||
console.log("Initializing chart with dataset: V5"); | ||
updateChart("V5"); | ||
|
||
console.log("Appending plot to #donut"); | ||
document.getElementById("donut").appendChild(svg.node()); | ||
console.log("Plot appended successfully"); | ||
|
||
// Create dataset selector | ||
console.log("Creating dataset selector"); | ||
const selectorContainer = document.getElementById("dataset-selector"); | ||
const datasets = new Map([["V2.0", "V2"], ["V3.0", "V3"], ["V4.0", "V4"], ["V5.0", "V5"]]); | ||
|
||
const radioForm = document.createElement("form"); | ||
radioForm.style.display = "inline-block"; // Ensure form is inline-block for center alignment | ||
|
||
// Add a label before the radio buttons | ||
const formLabel = document.createElement("span"); | ||
formLabel.textContent = "Release: "; | ||
formLabel.style.fontWeight = "500"; | ||
formLabel.style.marginTop = "20px"; | ||
formLabel.style.marginRight = "10px"; | ||
radioForm.appendChild(formLabel); | ||
|
||
datasets.forEach((value, key) => { | ||
const label = document.createElement("label"); | ||
label.style.marginLeft = "15px"; // Add space between options | ||
label.style.fontWeight = "500"; // Semibold text style | ||
label.style.display = "inline-block"; // Ensure radio buttons are aligned horizontally | ||
|
||
const input = document.createElement("input"); | ||
input.type = "radio"; | ||
input.name = "dataset"; | ||
input.value = value; | ||
|
||
if (value === "V5") input.checked = true; // Default selection | ||
input.addEventListener("change", () => { | ||
console.log(`Dataset changed to: ${value}`); | ||
updateChart(value); // Trigger chart change based on the selected value | ||
}); | ||
|
||
label.appendChild(input); | ||
label.appendChild(document.createTextNode(key)); | ||
|
||
radioForm.appendChild(label); | ||
}); | ||
|
||
selectorContainer.appendChild(radioForm); | ||
console.log("Dataset selector created successfully"); | ||
}).catch(error => { | ||
console.error("Error loading CSV data:", error); | ||
document.getElementById("donut").innerText = "Error loading data. Please check the console for details."; | ||
}); | ||
|
||
console.log("Script execution complete"); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
<div id="sankey-chart-container" style="width: 100%; height: 720px;"></div> | ||
<script type="module"> | ||
import * as d3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm"; | ||
import { csv } from "https://cdn.jsdelivr.net/npm/d3-fetch@3/+esm"; | ||
import { sankey as d3Sankey, sankeyLinkHorizontal } from "https://cdn.jsdelivr.net/npm/[email protected]/+esm"; | ||
|
||
const margin = { top: 10, right: 10, bottom: 10, left: 10 }; | ||
const width = 928 - margin.left - margin.right; | ||
const height = 720 - margin.top - margin.bottom; | ||
|
||
const sankey = d3Sankey() | ||
.nodeSort((a, b) => b.value - a.value) | ||
.linkSort((a, b) => b.value - a.value) | ||
.nodeWidth(7) | ||
.nodePadding(20) | ||
.extent([[0, 5], [width, height - 5]]); | ||
|
||
const color = d3.scaleOrdinal(["static"], ["#ccc"]).unknown("#ccc"); | ||
|
||
const colorMap = { | ||
attribute: "#06D6A0", // Green for attributes | ||
actuator: "#EF476F", // Red for actuators | ||
sensor: "#FFD166" // Yellow for sensors | ||
}; | ||
|
||
const svg = d3.create("svg") | ||
.attr("viewBox", [0, 0, width, height]) | ||
.attr("width", width) | ||
.attr("height", height) | ||
.attr("style", "max-width: 100%; height: auto;"); | ||
|
||
const container = document.getElementById("sankey-chart-container"); | ||
container.appendChild(svg.node()); | ||
|
||
function drawChart(data) { | ||
const graph = _graph(data, d3); | ||
|
||
const { nodes, links } = sankey({ | ||
nodes: graph.nodes.map(d => Object.create(d)), | ||
links: graph.links.map(d => Object.create(d)) | ||
}); | ||
|
||
let highlightedNode = null; | ||
|
||
function highlight(node) { | ||
const relatedLinks = links.filter(link => link.source === node || link.target === node); | ||
const relatedNodes = new Set(relatedLinks.flatMap(link => [link.source, link.target])); | ||
|
||
resetHighlight(); | ||
|
||
// Highlight related links | ||
svg.selectAll("path").attr("stroke", d => | ||
relatedLinks.includes(d) ? "#ff8800" : color(d.names[0]) // Use a distinct highlight color for links | ||
).attr("opacity", d => | ||
relatedLinks.includes(d) ? 0.7 : 1 // Set a moderate opacity for highlighted links | ||
); | ||
} | ||
|
||
function resetHighlight() { | ||
// Reset all links to their original color and full opacity | ||
svg.selectAll("path") | ||
.attr("stroke", d => color(d.names[0])) | ||
.attr("opacity", 1); | ||
} | ||
|
||
function handleClick(event, d) { | ||
if (highlightedNode === d) { | ||
// If clicking the same node again, reset the highlight | ||
highlightedNode = null; | ||
resetHighlight(); | ||
} else { | ||
// Highlight the clicked node's related links | ||
highlightedNode = d; | ||
highlight(d); | ||
} | ||
} | ||
|
||
// Draw nodes | ||
svg.append("g") | ||
.selectAll("rect") | ||
.data(nodes) | ||
.join("rect") | ||
.attr("x", d => d.x0) | ||
.attr("y", d => d.y0) | ||
.attr("height", d => d.y1 - d.y0) | ||
.attr("width", d => d.x1 - d.x0) | ||
.attr("fill", d => { | ||
// Check node type and apply appropriate color | ||
if (d.name === "attribute") return "#06D6A0"; // Green for attribute | ||
if (d.name === "actuator") return "#EF476F"; // Red for actuator | ||
if (d.name === "sensor") return "#FFD166"; // Yellow for sensor | ||
return "#cc8800"; // Default gray color | ||
}) | ||
.style("cursor", "pointer") | ||
.on("click", handleClick) // Add click event listener | ||
.append("title") | ||
.text(d => `${d.name}\n${d.value.toLocaleString()}`); | ||
|
||
// Draw links | ||
svg.append("g") | ||
.attr("fill", "none") | ||
.selectAll("g") | ||
.data(links) | ||
.join("path") | ||
.attr("d", sankeyLinkHorizontal()) | ||
.attr("stroke", d => color(d.names[0])) | ||
.attr("stroke-width", d => d.width) | ||
.style("mix-blend-mode", "multiply") | ||
.on("mouseover", function(event, d) { | ||
// Show the tooltip | ||
d3.select(this).attr("stroke", "#ab5b00").attr("opacity", 0.7); | ||
}) | ||
.on("mouseout", function(event, d) { | ||
// Hide the tooltip | ||
d3.select(this).attr("stroke", color(d.names[0])).attr("opacity", 1); | ||
}) | ||
.append("title") | ||
.text(d => `${d.names.join(" → ")}\n${d.value.toLocaleString()}`); | ||
|
||
// Add node labels | ||
svg.append("g") | ||
.style("font", "18px sans-serif") | ||
.selectAll("text") | ||
.data(nodes) | ||
.join("text") | ||
.attr("x", d => d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6) | ||
.attr("y", d => (d.y1 + d.y0) / 2) | ||
.attr("dy", "0.35em") | ||
.attr("text-anchor", d => d.x0 < width / 2 ? "start" : "end") | ||
.text(d => d.name) | ||
.append("tspan") | ||
.attr("fill-opacity", 0.7) | ||
.text(d => ` ${d.value.toLocaleString()}`); | ||
} | ||
|
||
function _graph(data, d3) { | ||
const keys = data.columns.slice(0, -1); | ||
let index = -1; | ||
const nodes = []; | ||
const nodeByKey = new d3.InternMap([], JSON.stringify); | ||
const indexByKey = new d3.InternMap([], JSON.stringify); | ||
const links = []; | ||
|
||
for (const k of keys) { | ||
for (const d of data) { | ||
const key = [k, d[k]]; | ||
if (nodeByKey.has(key)) continue; | ||
const node = { name: d[k] }; | ||
nodes.push(node); | ||
nodeByKey.set(key, node); | ||
indexByKey.set(key, ++index); | ||
} | ||
} | ||
|
||
for (let i = 1; i < keys.length; ++i) { | ||
const a = keys[i - 1]; | ||
const b = keys[i]; | ||
const prefix = keys.slice(0, i + 1); | ||
const linkByKey = new d3.InternMap([], JSON.stringify); | ||
for (const d of data) { | ||
const names = prefix.map(k => d[k]); | ||
const value = d.value || 1; | ||
let link = linkByKey.get(names); | ||
if (link) { link.value += value; continue; } | ||
link = { | ||
source: indexByKey.get([a, d[a]]), | ||
target: indexByKey.get([b, d[b]]), | ||
names, | ||
value | ||
}; | ||
links.push(link); | ||
linkByKey.set(names, link); | ||
} | ||
} | ||
|
||
return { nodes, links }; | ||
} | ||
|
||
const dataUrl = "/vehicle_signal_specification/data/modified_vss_compact_metadata copy.csv"; | ||
csv(dataUrl).then(data => drawChart(data)); | ||
</script> |
Oops, something went wrong.