diff --git a/Asia.json b/Asia.json new file mode 100644 index 00000000..c52196d6 --- /dev/null +++ b/Asia.json @@ -0,0 +1,27 @@ +{ + "name": "world", + "children": [ + { + "name": "Asia", + "color": "#f58321", + "children": [ + {"name": "China", "weight": 14.84, "code": "CN"}, + {"name": "Japan", "weight": 5.91, "code": "JP"}, + {"name": "India", "weight": 2.83, "code": "IN"}, + {"name": "South Korea", "weight": 1.86, "code": "KR"}, + {"name": "Russia", "weight": 1.8, "code": "RU"}, + {"name": "Indonesia", "weight": 1.16, "code": "ID"}, + {"name": "Turkey", "weight": 0.97, "code": "TR"}, + {"name": "Saudi Arabia", "weight": 0.87, "code": "SA"}, + {"name": "Iran", "weight": 0.57, "code": "IR"}, + {"name": "Thaïland", "weight": 0.53, "code": "TH"}, + {"name": "United Arab Emirates", "weight": 0.5, "code": "AE"}, + {"name": "Hong Kong", "weight": 0.42, "code": "HK"}, + {"name": "Israel", "weight": 0.4, "code": "IL"}, + {"name": "Malasya", "weight": 0.4, "code": "MY"}, + {"name": "Singapore", "weight": 0.39, "code": "SG"}, + {"name": "Philippines", "weight": 0.39, "code": "PH"} + ] + } + ] + } \ No newline at end of file diff --git a/AsiaGDP.json b/AsiaGDP.json new file mode 100644 index 00000000..294724f0 --- /dev/null +++ b/AsiaGDP.json @@ -0,0 +1,18 @@ +[ +{"name": "China", "weight": 14.84}, +{"name": "Japan", "weight": 5.91}, +{"name": "India", "weight": 2.83}, +{"name": "South Korea", "weight": 1.86}, +{"name": "Russia", "weight": 1.8}, +{"name": "Indonesia", "weight": 1.16}, +{"name": "Turkey", "weight": 0.97}, +{"name": "Saudi Arabia", "weight": 0.87}, +{"name": "Iran", "weight": 0.57}, +{"name": "Thaïland", "weight": 0.53}, +{"name": "United Arab Emirates", "weight": 0.5}, +{"name": "Hong Kong", "weight": 0.42}, +{"name": "Israel", "weight": 0.4}, +{"name": "Malasya", "weight": 0.4}, +{"name": "Singapore", "weight": 0.39}, +{"name": "Philippines", "weight": 0.39} +] \ No newline at end of file diff --git a/codeBlock.html b/codeBlock.html new file mode 100644 index 00000000..aa81c2c0 --- /dev/null +++ b/codeBlock.html @@ -0,0 +1,45 @@ + + + + + + + Code Example + + + + + + +
+

All About Treemaps

+
+ +
+

Inline Code Example

+

+ This is an example of <code> element for showing + inline code. +

+ +

Code Block Example

+
+          
+  // This is a sample code snippet in JavaScript
+  function greet(name) {
+      console.log("Hello, " + name + "!");
+  }
+  
+  greet("World");
+          
+      
+
+ + + diff --git a/codeTutorial.html b/codeTutorial.html new file mode 100644 index 00000000..6e681a11 --- /dev/null +++ b/codeTutorial.html @@ -0,0 +1,233 @@ + + + + + + + Creating a Simple Voronoi Treemap + + + + +
+ +

Creating a Simple Voronoi Treemap with D3.js

+
+ + +
+

+ In order to create a simple Voronoi treemap using D3.js, follow these + steps: +

+
+ + +
    +
  1. +

    Initialize the HTML Structure:

    +

    + Set up the HTML structure with an SVG container where the treemap will + be rendered. +

    + <svg id="simpleVoronoi"></svg> +

    + The <svg> element with the ID "simpleVoronoi" + serves as the container for the Voronoi treemap. +

    +
  2. + + +
  3. +

    Import D3.js Library:

    +

    + Import the D3.js library by adding the following script tag just + before your custom script: +

    + <script + src="https://d3js.org/d3.v7.min.js"></script> +

    + This script tag imports the D3.js library from the official CDN, + making D3.js functionalities available in your project. +

    +
  4. + + +
  5. +

    Initialize the Voronoi Treemap:

    +

    + Define constants for SVG dimensions, data, and necessary variables. + Initialize the Voronoi treemap by calling functions to set up data, + layout, and drawing. +

    +
    
    +const HEIGHT = 500;
    +const WIDTH = 960;
    +const HALF_WIDTH = WIDTH / 2;
    +const HALF_HEIGHT = HEIGHT / 2;
    +
    +// Define your data array here
    +const data = [/* Your data array */];
    +
    +// Initialize SVG container
    +const svg = d3.select("#simpleVoronoi")
    +  .attr("width", WIDTH)
    +  .attr("height", HEIGHT);
    +
    +const TREEMAP_RADIUS = Math.min(HALF_WIDTH, HALF_HEIGHT);
    +
    +const _voronoiTreemap = d3.voronoiTreemap();
    +let hierarchy, circlingPolygon;
    +
    +const fontScale = d3.scaleLinear();
    +
    +function init(rootData) {
    +  initData();
    +  initLayout();
    +  hierarchy = d3.hierarchy({ children: rootData }).sum((d) => d.weight);
    +  _voronoiTreemap.clip(circlingPolygon)(hierarchy);
    +
    +  drawTreemap(hierarchy);
    +}
    +
    +init(data);
    +      
    +

    + This block of code initializes constants, sets up the SVG container, + and prepares data and layout for the Voronoi treemap. +

    +

    + Constants like HEIGHT and WIDTH define the + dimensions of the SVG container. Data, such as country names, weights, + and colors, are stored in the data array. The + init function initializes the treemap by calling other + functions to set up data, layout, and drawing. +

    +
  6. + + +
  7. +

    Initialize Data and Layout:

    +

    + Define functions to initialize data and layout settings for the + treemap. +

    +
    
    +function initData() {
    +  circlingPolygon = computeCirclingPolygon();
    +  fontScale.domain([3, 20]).range([8, 20]).clamp(true);
    +}
    +
    +function computeCirclingPolygon() {
    +  return [
    +    [0, 0],
    +    [WIDTH, 0],
    +    [WIDTH, HEIGHT],
    +    [0, HEIGHT],
    +  ];
    +}
    +
    +function initLayout() {
    +  const drawingArea = svg.append("g").classed("drawingArea", true);
    +  const treemapContainer = drawingArea.append("g").classed("treemap-container", true);
    +
    +  treemapContainer
    +    .append("path")
    +    .classed("world", true)
    +    .attr("transform", `translate(${-TREEMAP_RADIUS}, ${-TREEMAP_RADIUS})`)
    +    .attr("d", `M${circlingPolygon.join(",")}Z`);
    +}
    +      
    +

    + These functions initialize data and layout settings for the treemap, + including the circling polygon and font scale. +

    +

    + The initData function calculates the circling polygon and + sets the font scale based on the provided data. The + computeCirclingPolygon function calculates the vertices + of the circling polygon, while the initLayout function + sets up the SVG container and draws the circling polygon. +

    +
  8. + + +
  9. +

    Draw the Treemap:

    +

    + Create a function to draw the Voronoi treemap based on the provided + data and layout. +

    +
    
    +function drawTreemap(hierarchy) {
    +  const leaves = hierarchy.leaves();
    +
    +  const cells = svg.select(".treemap-container")
    +    .append("g")
    +    .classed("cells", true)
    +    .selectAll(".cell")
    +    .data(leaves)
    +    .enter()
    +    .append("path")
    +    .classed("cell", true)
    +    .attr("d", (d) => `M${d.polygon.join(",")}z`)
    +    .style("stroke", "black")
    +    .style("stroke-width", "10px")
    +    .style("fill", (d) => d.data.color);
    +
    +  const labels = svg.select(".treemap-container")
    +    .append("g")
    +    .classed("labels", true)
    +    .selectAll(".label")
    +    .data(leaves)
    +    .enter()
    +    .append("g")
    +    .classed("label", true)
    +    .attr("transform", (d) => `translate(${d.polygon.site.x}, ${d.polygon.site.y})`)
    +    .style("font-size", (d) => fontScale(d.data.weight));
    +
    +  labels
    +    .append("text")
    +    .classed("name", true)
    +    .html((d) => d.data.name);
    +
    +  labels
    +    .append("text")
    +    .classed("value", true)
    +    .text((d) => `${d.data.weight}%`);
    +}
    +      
    +

    + This function draws the Voronoi treemap based on the provided data and + layout settings, including cells and labels. +

    +

    + The drawTreemap function creates cells and labels for + each data point in the treemap. It appends SVG elements for cells and + labels, styles them accordingly, and positions them based on the + calculated data. The cells represent the Voronoi regions, while the + labels display country names and weights. +

    +
  10. +
+ + + + diff --git a/globalEconomyByGDP.json b/globalEconomyByGDP.json new file mode 100644 index 00000000..72d0e59b --- /dev/null +++ b/globalEconomyByGDP.json @@ -0,0 +1,89 @@ +{ + "name": "world", + "children": [ + { + "name": "Asia", + "color": "#ffffcc", + "children": [ + {"name": "China", "weight": 14.84, "code": "CN"}, + {"name": "Japan", "weight": 5.91, "code": "JP"}, + {"name": "India", "weight": 2.83, "code": "IN"}, + {"name": "South Korea", "weight": 1.86, "code": "KR"}, + {"name": "Russia", "weight": 1.8, "code": "RU"}, + {"name": "Indonesia", "weight": 1.16, "code": "ID"}, + {"name": "Turkey", "weight": 0.97, "code": "TR"}, + {"name": "Saudi Arabia", "weight": 0.87, "code": "SA"}, + {"name": "Iran", "weight": 0.57, "code": "IR"}, + {"name": "Thaïland", "weight": 0.53, "code": "TH"}, + {"name": "United Arab Emirates", "weight": 0.5, "code": "AE"}, + {"name": "Hong Kong", "weight": 0.42, "code": "HK"}, + {"name": "Israel", "weight": 0.4, "code": "IL"}, + {"name": "Malasya", "weight": 0.4, "code": "MY"}, + {"name": "Singapore", "weight": 0.39, "code": "SG"}, + {"name": "Philippines", "weight": 0.39, "code": "PH"} + ] + }, + { + "name": "North America", + "color": "#ffcc99", + "children": [ + {"name": "United States", "weight": 24.32, "code": "US"}, + {"name": "Canada", "weight": 2.09, "code": "CA"}, + {"name": "Mexico", "weight": 1.54, "code": "MX"} + ] + }, + { + "name": "Europe", + "color": "#ffcccc", + "children": [ + {"name": "Germany", "weight": 4.54, "code": "DE"}, + {"name": "United Kingdom", "weight": 3.85, "code": "UK"}, + {"name": "France", "weight": 3.26, "code": "FR"}, + {"name": "Italy", "weight": 2.46, "code": "IT"}, + {"name": "Spain", "weight": 1.62, "code": "ES"}, + {"name": "Netherlands", "weight": 1.01, "code": "NL"}, + {"name": "Switzerland", "weight": 0.9, "code": "CH"}, + {"name": "Sweden", "weight": 0.67, "code": "SE"}, + {"name": "Poland", "weight": 0.64, "code": "PL"}, + {"name": "Belgium", "weight": 0.61, "code": "BE"}, + {"name": "Norway", "weight": 0.52, "code": "NO"}, + {"name": "Austria", "weight": 0.51, "code": "AT"}, + {"name": "Denmark", "weight": 0.4, "code": "DK"}, + {"name": "Ireland", "weight": 0.38, "code": "IE"} + ] + }, + { + "name": "South America", + "color": "#ff99cc", + "children": [ + {"name": "Brazil", "weight": 2.39, "code": "BR"}, + {"name": "Argentina", "weight": 0.79, "code": "AR"}, + {"name": "Venezuela", "weight": 0.5, "code": "VE"}, + {"name": "Colombia", "weight": 0.39, "code": "CO"} + ] + }, + { + "name": "Australia", + "color": "#ffccff", + "children": [ + {"name": "Australia", "weight": 1.81, "code": "AU"} + ] + }, + { + "name": "Africa", + "color": "#cc99ff", + "children": [ + {"name": "Nigeria", "weight": 0.65, "code": "NG"}, + {"name": "Egypt", "weight": 0.45, "code": "EG"}, + {"name": "South Africa", "weight": 0.42, "code": "ZA"} + ] + }, + { + "name": "Rest of the World", + "color": "#ccccff", + "children": [ + {"name": "Rest of the World", "weight": 9.41, "code": "RotW"} + ] + } + ] + } \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..1db88d40 --- /dev/null +++ b/index.html @@ -0,0 +1,292 @@ + + + + + + + + + + + + + Home page + + + + + + + + +
+

Voronoi Treemap

+
+ + +
+

What are Voronoi Treemaps?

+

+ Voronoi Treemaps are methods of visualizing hierarchical data, where + each cell represents a portion of the data and the hierarchy is encoded + by the nesting of cells within each other. Unlike traditional Treemaps, + Voronoi Tree Maps do not use rectangles to represent the data, but + rather use arbitrary polygons to represent the data. This allows for a + more organic and natural representation of the data. +

+
+ +
+

Traditional Treemap

+
+

+ This is a Traditional Treemap. Notice how the rectangles are used to + represent the data. This can lead to a more rigid and less organic + representation of the data. +

+
+ +
+
+ +
+

Voronoi Treemap

+

+ This is a Voronoi Treemap. Notice how the polygons are used to represent + the data. As you can see the data seems to have a more organic and + natural representation. +

+
+ +
+ +

+
+
+ + +
+ +
+ +

Creating a Simple Voronoi Treemap with D3.js

+
+ + +
+

+ In order to create a simple Voronoi treemap using D3.js, follow these + steps: +

+
+ + +
    +
  1. +

    Initialize the HTML Structure:

    +

    + Set up the HTML structure with an SVG container where the treemap will + be rendered. +

    + <svg id="simpleVoronoi"></svg> +

    + The <svg> element with the ID "simpleVoronoi" + serves as the container for the Voronoi treemap. +

    +
  2. + + +
  3. +

    Import D3.js Library:

    +

    + Import the D3.js library by adding the following script tag just + before your custom script: +

    + <script + src="https://d3js.org/d3.v7.min.js"></script> +

    + This script tag imports the D3.js library from the official CDN, + making D3.js functionalities available in your project. +

    +
  4. + + +
  5. +

    Initialize the Voronoi Treemap:

    +

    + Define constants for SVG dimensions, data, and necessary variables. + Initialize the Voronoi treemap by calling functions to set up data, + layout, and drawing. +

    +
    
    +const HEIGHT = 500;
    +const WIDTH = 960;
    +const HALF_WIDTH = WIDTH / 2;
    +const HALF_HEIGHT = HEIGHT / 2;
    +
    +// Define your data array here
    +const data = [/* Your data array */];
    +
    +// Initialize SVG container
    +const svg = d3.select("#simpleVoronoi")
    +  .attr("width", WIDTH)
    +  .attr("height", HEIGHT);
    +
    +const TREEMAP_RADIUS = Math.min(HALF_WIDTH, HALF_HEIGHT);
    +
    +const _voronoiTreemap = d3.voronoiTreemap();
    +let hierarchy, circlingPolygon;
    +
    +const fontScale = d3.scaleLinear();
    +
    +function init(rootData) {
    +  initData();
    +  initLayout();
    +  hierarchy = d3.hierarchy({ children: rootData }).sum((d) => d.weight);
    +  _voronoiTreemap.clip(circlingPolygon)(hierarchy);
    +
    +  drawTreemap(hierarchy);
    +}
    +
    +init(data);
    +      
    +

    + This block of code initializes constants, sets up the SVG container, + and prepares data and layout for the Voronoi treemap. +

    +

    + Constants like HEIGHT and WIDTH define the + dimensions of the SVG container. Data, such as country names, weights, + and colors, are stored in the data array. The + init function initializes the treemap by calling other + functions to set up data, layout, and drawing. +

    +
  6. + + +
  7. +

    Initialize Data and Layout:

    +

    + Define functions to initialize data and layout settings for the + treemap. +

    +
    
    +function initData() {
    +  circlingPolygon = computeCirclingPolygon();
    +  fontScale.domain([3, 20]).range([8, 20]).clamp(true);
    +}
    +
    +function computeCirclingPolygon() {
    +  return [
    +    [0, 0],
    +    [WIDTH, 0],
    +    [WIDTH, HEIGHT],
    +    [0, HEIGHT],
    +  ];
    +}
    +
    +function initLayout() {
    +  const drawingArea = svg.append("g").classed("drawingArea", true);
    +  const treemapContainer = drawingArea.append("g").classed("treemap-container", true);
    +
    +  treemapContainer
    +    .append("path")
    +    .classed("world", true)
    +    .attr("transform", `translate(${-TREEMAP_RADIUS}, ${-TREEMAP_RADIUS})`)
    +    .attr("d", `M${circlingPolygon.join(",")}Z`);
    +}
    +      
    +

    + These functions initialize data and layout settings for the treemap, + including the circling polygon and font scale. +

    +

    + The initData function calculates the circling polygon and + sets the font scale based on the provided data. The + computeCirclingPolygon function calculates the vertices + of the circling polygon, while the initLayout function + sets up the SVG container and draws the circling polygon. +

    +
  8. + + +
  9. +

    Draw the Treemap:

    +

    + Create a function to draw the Voronoi treemap based on the provided + data and layout. +

    +
    
    +function drawTreemap(hierarchy) {
    +  const leaves = hierarchy.leaves();
    +
    +  const cells = svg.select(".treemap-container")
    +    .append("g")
    +    .classed("cells", true)
    +    .selectAll(".cell")
    +    .data(leaves)
    +    .enter()
    +    .append("path")
    +    .classed("cell", true)
    +    .attr("d", (d) => `M${d.polygon.join(",")}z`)
    +    .style("stroke", "black")
    +    .style("stroke-width", "10px")
    +    .style("fill", (d) => d.data.color);
    +
    +  const labels = svg.select(".treemap-container")
    +    .append("g")
    +    .classed("labels", true)
    +    .selectAll(".label")
    +    .data(leaves)
    +    .enter()
    +    .append("g")
    +    .classed("label", true)
    +    .attr("transform", (d) => `translate(${d.polygon.site.x}, ${d.polygon.site.y})`)
    +    .style("font-size", (d) => fontScale(d.data.weight));
    +
    +  labels
    +    .append("text")
    +    .classed("name", true)
    +    .html((d) => d.data.name);
    +
    +  labels
    +    .append("text")
    +    .classed("value", true)
    +    .text((d) => `${d.data.weight}%`);
    +}
    +      
    +

    + This function draws the Voronoi treemap based on the provided data and + layout settings, including cells and labels. +

    +

    + The drawTreemap function creates cells and labels for + each data point in the treemap. It appends SVG elements for cells and + labels, styles them accordingly, and positions them based on the + calculated data. The cells represent the Voronoi regions, while the + labels display country names and weights. +

    +
  10. +
+ + + + + diff --git a/index.js b/index.js new file mode 100644 index 00000000..c5e36119 --- /dev/null +++ b/index.js @@ -0,0 +1,111 @@ +const HEIGHT = 500; +const WIDTH = 960; +const HALF_WIDTH = WIDTH / 2; +const HALF_HEIGHT = HEIGHT / 2; + +const data = [ + { name: "China", weight: 14.84, color: "#ffffcc" }, + { name: "Japan", weight: 5.91, color: "#ffcc99" }, + { name: "India", weight: 2.83, color: "#ffcccc" }, + { name: "South Korea", weight: 1.86, color: "#ff99cc" }, + { name: "Russia", weight: 1.8, color: "#ffccff" }, + { name: "Indonesia", weight: 1.16, color: "#cc99ff" }, + { name: "Turkey", weight: 0.97, color: "#ccccff" }, + { name: "Saudi Arabia", weight: 0.87, color: "#99ccff" }, + { name: "Iran", weight: 0.57, color: "#ccffff" }, + { name: "Thaïland", weight: 0.53, color: "#99ffcc" }, + { name: "UAE", weight: 0.5, color: "#ccffcc" }, + { name: "Hong Kong", weight: 0.42, color: "#ccff99" }, +]; + +var svg = d3; + +const TREEMAP_RADIUS = Math.min(HALF_WIDTH, HALF_HEIGHT); + +const _voronoiTreemap = d3.voronoiTreemap(); +let hierarchy, circlingPolygon; + +const fontScale = d3.scaleLinear(); + +let drawingArea, treemapContainer; + +function init(rootData) { + initData(); + initLayout(); + hierarchy = d3.hierarchy({ children: rootData }).sum((d) => d.weight); + _voronoiTreemap.clip(circlingPolygon)(hierarchy); + + drawTreemap(hierarchy); +} + +init(data); + +function initData() { + circlingPolygon = computeCirclingPolygon(); + fontScale.domain([3, 20]).range([8, 20]).clamp(true); +} + +function computeCirclingPolygon() { + return [ + [0, 0], + [WIDTH, 0], + [WIDTH, HEIGHT], + [0, HEIGHT], + ]; +} + +function initLayout() { + svg = d3 + .select("#simpleVoronoi") + .attr("width", WIDTH) + .attr("height", HEIGHT) + .attr("transform", "translate(0, 30)"); + drawingArea = svg.append("g").classed("drawingArea", true); + treemapContainer = drawingArea.append("g").classed("treemap-container", true); + + treemapContainer + .append("path") + .classed("world", true) + .attr("transform", `translate(${-TREEMAP_RADIUS}, ${-TREEMAP_RADIUS})`) + .attr("d", `M${circlingPolygon.join(",")}Z`); +} + +function drawTreemap(hierarchy) { + const leaves = hierarchy.leaves(); + + treemapContainer + .append("g") + .classed("cells", true) + .selectAll(".cell") + .data(leaves) + .enter() + .append("path") + .classed("cell", true) + .attr("d", (d) => `M${d.polygon.join(",")}z`) + .style("stroke", "black") + .style("stroke-width", "10px") + .style("fill", (d) => d.data.color); + + const labels = treemapContainer + .append("g") + .classed("labels", true) + .selectAll(".label") + .data(leaves) + .enter() + .append("g") + .classed("label", true) + .attr( + "transform", + (d) => `translate(${d.polygon.site.x}, ${d.polygon.site.y})` + ) + .style("font-size", (d) => fontScale(d.data.weight)); + + labels + .append("text") + .classed("name", true) + .html((d) => d.data.name); + labels + .append("text") + .classed("value", true) + .text((d) => `${d.data.weight}%`); +} diff --git a/navbar.html b/navbar.html new file mode 100644 index 00000000..26ea8c0a --- /dev/null +++ b/navbar.html @@ -0,0 +1,5 @@ + diff --git a/style.css b/style.css new file mode 100644 index 00000000..596f2b17 --- /dev/null +++ b/style.css @@ -0,0 +1,150 @@ +/* Basic styling for the navigation bar */ + +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; +} + +div { +} + +h1 { + margin: 0px 0 0 0; +} + +nav { + background-color: #333; + color: #fff; + text-align: center; + padding: 10px 0; + position: fixed; /* Make the navigation bar fixed */ + width: 100%; /* Make it full width */ + top: 0; /* Stick it to the top of the screen */ + z-index: 999; +} +nav a { + color: #fff; + text-decoration: none; + padding: 0 15px; +} +nav a:hover { + /* background-color: #555; */ + color: #a52020; + transition: color 0.1s ease-in-out; +} +.container { + max-width: 800px; + margin: 20px auto; + padding: 20px; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} +pre { + background-color: #f4f4f4; + padding: 10px; + border: 1px solid #ddd; + border-radius: 5px; + overflow-x: auto; /* Enable horizontal scrollbar if needed */ +} +code { + font-family: Consolas, monospace; + font-size: 14px; + color: #333; +} + +.title { + padding: auto; + padding-top: 30px; + display: grid; + place-items: center; + font-size: 2rem; +} + +svg { + background-color: white; +} + +.cell { + stroke: white; + stroke-width: 1px; +} +.label { + text-anchor: middle; + fill: black; +} + +.label > .name { + dominant-baseline: text-after-edge; +} + +.label > .value { + dominant-baseline: text-before-edge; +} + +/* BEGIN INDEX HTML */ +.homeTitleText { + padding: auto; + padding-top: 50px; + padding-bottom: 20px; + font-size: 1.2rem; + padding-left: 20px; +} + +.simpleVoronoi { + display: flex; + padding-left: 20px; +} + +.centerText { + display: grid; + padding-top: 0px; + padding-left: 20px; + font-size: 1.5rem; +} + +.centerText h2 { + font-size: 1.3rem; + display: grid; +} + +.centerText p { + font-size: 1rem; + max-width: 900px; +} + +/* Footer */ +footer { + display: flex; + justify-content: center; + align-items: center; + padding: 50px 0 10px 0; +} + +/* END INDEX HTML */ + +/* BEGIN CODETUTORIAL HTML*/ +.codeTitle { + padding: auto; + padding-top: 120px; + font-size: 1.2rem; + padding-left: 20px; +} + +.codeIntro { + padding: auto; + padding-top: 5px; + font-size: 1rem; + max-width: 900px; + padding-left: 20px; +} + +/* END CODETUTORIAL HTML*/ + +/* BEGIN VORONOITREEMAPS HTML */ +.complexVoronoi { + display: grid; + place-content: center; + padding-top: 50px; +} diff --git a/treemap.csv b/treemap.csv new file mode 100644 index 00000000..c3d1fbb6 --- /dev/null +++ b/treemap.csv @@ -0,0 +1,18 @@ +name,parent,value +Origin,, +CN,Origin,14.84 +JP,Origin,5.91 +IN,Origin,2.83 +KR,Origin,1.86 +RU,Origin,1.8 +ID,Origin,1.16 +TR,Origin,0.97 +SA,Origin,0.87 +IR,Origin,0.57 +TH,Origin,0.53 +AE,Origin,0.5 +HK,Origin,0.42 +IL,Origin,0.4 +MY,Origin,0.4 +SG,Origin,0.39 +PH,Origin,0.39 diff --git a/treemap.js b/treemap.js new file mode 100644 index 00000000..1cc799e4 --- /dev/null +++ b/treemap.js @@ -0,0 +1,76 @@ +// set the dimensions and margins of the graph +var margin = { top: 0, right: 10, bottom: 0, left: 0 }, + width = 980 - margin.left - margin.right, + height = 520 - margin.top - margin.bottom; + +// append the svg object to the body of the page +var svg = d3 + .select("#my_dataviz") + .append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + +// Read data +d3.csv("./treemap.csv", function (data) { + // stratify the data: reformatting for d3.js + var root = d3 + .stratify() + .id(function (d) { + return d.name; + }) // Name of the entity (column name is name in csv) + .parentId(function (d) { + return d.parent; + })( + // Name of the parent (column name is parent in csv) + data + ); + root.sum(function (d) { + return +d.value; + }); // Compute the numeric value for each entity + + // Then d3.treemap computes the position of each element of the hierarchy + // The coordinates are added to the root object above + d3.treemap().size([width, height]).padding(10)(root); + + console.log(root.leaves()); + // use this information to add rectangles: + svg + .selectAll("rect") + .data(root.leaves()) + .enter() + .append("rect") + .attr("x", function (d) { + return d.x0; + }) + .attr("y", function (d) { + return d.y0; + }) + .attr("width", function (d) { + return d.x1 - d.x0; + }) + .attr("height", function (d) { + return d.y1 - d.y0; + }) + .style("stroke", "black") + .style("fill", "#ffcc99"); + + // and to add the text labels + svg + .selectAll("text") + .data(root.leaves()) + .enter() + .append("text") + .attr("x", function (d) { + return d.x0 + 10; + }) // +10 to adjust position (more right) + .attr("y", function (d) { + return d.y0 + 20; + }) // +20 to adjust position (lower) + .text(function (d) { + return d.data.name; + }) + .attr("font-size", "10px") + .attr("fill", "black"); +}); diff --git a/voronoiTreemaps.html b/voronoiTreemaps.html new file mode 100644 index 00000000..bd4c77b9 --- /dev/null +++ b/voronoiTreemaps.html @@ -0,0 +1,87 @@ + + + + + + + d3-voronoi-treemap usage + + + + + +
+ + + +
+ + + +
+ + +
+ + diff --git a/voronoiTreemaps.js b/voronoiTreemaps.js new file mode 100644 index 00000000..6bfef9c5 --- /dev/null +++ b/voronoiTreemaps.js @@ -0,0 +1,228 @@ +//begin: constants +var _2PI = 2 * Math.PI; +//end: constants + +//begin: layout conf. +var svgWidth = 960, + svgHeight = 500, + margin = { top: 10, right: 10, bottom: 10, left: 10 }, + height = svgHeight - margin.top - margin.bottom, + width = svgWidth - margin.left - margin.right, + halfWidth = width / 2, + halfHeight = height / 2, + quarterWidth = width / 4, + quarterHeight = height / 4, + titleY = 20, + legendsMinY = height - 20, + treemapRadius = 205, + treemapCenter = [halfWidth, halfHeight + 5]; +//end: layout conf. + +//begin: treemap conf. +var _voronoiTreemap = d3.voronoiTreemap(); +var hierarchy, circlingPolygon; +//end: treemap conf. + +//begin: drawing conf. +var fontScale = d3.scaleLinear(); +//end: drawing conf. + +//begin: reusable d3Selection +var svg, drawingArea, treemapContainer; +//end: reusable d3Selection + +d3.json("globalEconomyByGDP.json").then(function (rootData) { + initData(); + initLayout(rootData); + hierarchy = d3.hierarchy(rootData).sum(function (d) { + return d.weight; + }); + _voronoiTreemap.clip(circlingPolygon)(hierarchy); + + drawTreemap(hierarchy); +}); + +function initData(rootData) { + circlingPolygon = computeCirclingPolygon(treemapRadius); + fontScale.domain([3, 20]).range([8, 20]).clamp(true); +} + +function computeCirclingPolygon(radius) { + var points = 60, + increment = _2PI / points, + circlingPolygon = []; + + for (var a = 0, i = 0; i < points; i++, a += increment) { + circlingPolygon.push([ + radius + radius * Math.cos(a), + radius + radius * Math.sin(a), + ]); + } + + return circlingPolygon; +} + +function initLayout(rootData) { + svg = d3.select("svg").attr("width", svgWidth).attr("height", svgHeight); + + drawingArea = svg + .append("g") + .classed("drawingArea", true) + .attr("transform", "translate(" + [margin.left, margin.top] + ")"); + + treemapContainer = drawingArea + .append("g") + .classed("treemap-container", true) + .attr("transform", "translate(" + treemapCenter + ")"); + + treemapContainer + .append("path") + .classed("world", true) + .attr("transform", "translate(" + [-treemapRadius, -treemapRadius] + ")") + .attr("d", "M" + circlingPolygon.join(",") + "Z"); + + drawTitle(); + drawFooter(); + drawLegends(rootData); +} + +function drawTitle() { + drawingArea + .append("text") + .attr("id", "title") + .attr("transform", "translate(" + [halfWidth, titleY] + ")") + .attr("text-anchor", "middle"); + // .text("The Global Economy by GDP (as of 01/2017)"); +} + +function drawFooter() { + drawingArea + .append("text") + .classed("tiny light", true) + .attr("transform", "translate(" + [0, height] + ")") + .attr("text-anchor", "start"); + drawingArea + .append("text") + .classed("tiny light", true) + .attr("transform", "translate(" + [halfWidth, height] + ")") + .attr("text-anchor", "middle"); + drawingArea + .append("text") + .classed("tiny light", true) + .attr("transform", "translate(" + [width, height] + ")") + .attr("text-anchor", "end"); +} + +function drawLegends(rootData) { + var legendHeight = 13, + interLegend = 4, + colorWidth = legendHeight * 6, + continents = rootData.children.reverse(); + + var legendContainer = drawingArea + .append("g") + .classed("legend", true) + .attr("transform", "translate(" + [0, legendsMinY] + ")"); + + var legends = legendContainer.selectAll(".legend").data(continents).enter(); + + var legend = legends + .append("g") + .classed("legend", true) + .attr("transform", function (d, i) { + return "translate(" + [0, -i * (legendHeight + interLegend)] + ")"; + }); + + legend + .append("rect") + .classed("legend-color", true) + .attr("y", -legendHeight) + .attr("width", colorWidth) + .attr("height", legendHeight) + .style("fill", function (d) { + return d.color; + }); + legend + .append("text") + .classed("tiny", true) + .attr("transform", "translate(" + [colorWidth + 5, -2] + ")") + .text(function (d) { + return d.name; + }); + + legendContainer + .append("text") + .attr( + "transform", + "translate(" + + [0, -continents.length * (legendHeight + interLegend) - 5] + + ")" + ) + .text("Continents"); +} + +function drawTreemap(hierarchy) { + var leaves = hierarchy.leaves(); + + var cells = treemapContainer + .append("g") + .classed("cells", true) + .attr("transform", "translate(" + [-treemapRadius, -treemapRadius] + ")") + .selectAll(".cell") + .data(leaves) + .enter() + .append("path") + .classed("cell", true) + .attr("d", function (d) { + return "M" + d.polygon.join(",") + "z"; + }) + .style("fill", function (d) { + return d.parent.data.color; + }); + + var labels = treemapContainer + .append("g") + .classed("labels", true) + .attr("transform", "translate(" + [-treemapRadius, -treemapRadius] + ")") + .selectAll(".label") + .data(leaves) + .enter() + .append("g") + .classed("label", true) + .attr("transform", function (d) { + return "translate(" + [d.polygon.site.x, d.polygon.site.y] + ")"; + }) + .style("font-size", function (d) { + return fontScale(d.data.weight); + }); + + labels + .append("text") + .classed("name", true) + .html(function (d) { + return d.data.weight < 1 ? d.data.code : d.data.name; + }); + labels + .append("text") + .classed("value", true) + .text(function (d) { + return d.data.weight + "%"; + }); + + var hoverers = treemapContainer + .append("g") + .classed("hoverers", true) + .attr("transform", "translate(" + [-treemapRadius, -treemapRadius] + ")") + .selectAll(".hoverer") + .data(leaves) + .enter() + .append("path") + .classed("hoverer", true) + .attr("d", function (d) { + return "M" + d.polygon.join(",") + "z"; + }); + + hoverers.append("title").text(function (d) { + return d.data.name + "\n" + d.value + "%"; + }); +}