Skip to content

Commit

Permalink
first animated treemap
Browse files Browse the repository at this point in the history
  • Loading branch information
holtzy committed Dec 3, 2024
1 parent 99887c9 commit 9c12ca5
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 285 deletions.
55 changes: 29 additions & 26 deletions pages/example/deplacement-en-france.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { Layout } from 'component/Layout';
import TitleAndDescription from 'component/TitleAndDescription';
import ChartFamilySection from 'component/ChartFamilySection';
Expand All @@ -8,8 +8,10 @@ import Link from 'next/link';
import { SankeyBumpChartDemo } from 'viz/SankeyBumpChart/SankeyBumpChartDemo';
import { LinkAsButton } from 'component/LinkAsButton';
import { Treemap } from '@/viz/TreemapFrenchTravel/Treemap';
import { data, rawData } from '@/viz/TreemapFrenchTravel/data';
import { modeDeTransport, Tree } from '@/viz/TreemapFrenchTravel/data';
import { stratify } from 'd3';
import * as d3 from 'd3';
import { Tabs, TabsList, TabsTrigger } from '@/component/UI/tabs';

const graphDescription = (
<>
Expand All @@ -18,30 +20,11 @@ const graphDescription = (
);

export default function Home() {
const groups = ['Etudes', 'Professionnel', 'Vacances'];
const target = 'Temps';
const [selectedmetric, setSelectedMetric] = useState('Distance');

const mainDataSelection = rawData['Motifs de déplacement'];
console.log('mainDataSelection', mainDataSelection);

const selectedData = Object.keys(mainDataSelection).map((key) => {
return {
parent: 'Temps',
name: key,
value: Number(mainDataSelection[key]['Temps'].replace('%', '')),
};
});
console.log('selectedData', selectedData);

const hierarchyBuilder = stratify()
.id((d) => d.name)
.parentId((d) => d.parent);
const hierarchy = hierarchyBuilder([
{ parent: undefined, name: 'Temps' },
...selectedData,
]);

console.log(hierarchy);
const data = modeDeTransport.children.find(
(node) => node.name === selectedmetric
);

return (
<Layout
Expand All @@ -64,7 +47,27 @@ export default function Home() {
here, together with its code:🙇‍♂️
</p>

<Treemap data={hierarchy} width={900} height={600} />
<Tabs defaultValue={selectedmetric} className="w-[400px]">
<TabsList>
<TabsTrigger value="temps" onClick={() => setSelectedMetric('Temps')}>
Temps
</TabsTrigger>
<TabsTrigger
value="distance"
onClick={() => setSelectedMetric('Distance')}
>
Distance
</TabsTrigger>
<TabsTrigger
value="em"
onClick={() => setSelectedMetric('Emissions')}
>
Emissions
</TabsTrigger>
</TabsList>
</Tabs>

<Treemap data={data} width={900} height={600} />

<div className="full-bleed border-t h-0 bg-gray-100 mb-3 mt-24" />
<ChartFamilySection chartFamily="flow" />
Expand Down
Binary file added public/excalidraw/dataviz-process.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions viz/TreemapFrenchTravel/Rectangle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useSpring, animated } from '@react-spring/web';

type RectangleProps = {
width: number;
height: number;
x: number;
y: number;
};

export const Rectangle = (props: RectangleProps) => {
const { x, y, width, height } = props;

const springProps = useSpring({
to: { x, y, width, height },
config: {
friction: 30,
},
});

if (y === undefined) {
return null;
}

return (
<animated.rect
x={springProps.x}
y={springProps.y}
width={springProps.width}
height={springProps.height}
opacity={0.7}
stroke="#9d174d"
fill="#9d174d"
fillOpacity={0.3}
strokeWidth={1}
rx={1}
/>
);
};
44 changes: 10 additions & 34 deletions viz/TreemapFrenchTravel/Treemap.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import { useMemo } from 'react';
import * as d3 from 'd3';
import styles from './treemap.module.css';

export type TreeNode = {
type: 'node';
name: string;
children: Tree[];
};
export type TreeLeaf = {
parent: string;
name: string;
value: number;
};

export type Tree = TreeNode | TreeLeaf;
import { Tree } from '@/viz/TreemapFrenchTravel/data';
import { Rectangle } from './Rectangle';

type TreemapProps = {
width: number;
height: number;
data: d3.HierarchyNode<unknown>;
data: Tree;
};

const colors = [
Expand All @@ -35,32 +23,20 @@ export const Treemap = ({ width, height, data }: TreemapProps) => {
const hierarchy = d3.hierarchy(data).sum((d) => d.value);
console.log('hierarchy', hierarchy);

const firstLevelGroups = hierarchy?.children?.map((child) => child.data.name);
console.log('firstLevelGroups', firstLevelGroups);

var colorScale = d3
.scaleOrdinal<string>()
.domain(firstLevelGroups || [])
.range(colors);

const treeGenerator = d3.treemap<Tree>().size([width, height]).padding(4);
const root = treeGenerator(hierarchy);
console.log('root', root);

const allShapes = root.leaves().map((leaf, i) => {
console.log('leaf', leaf);
const allShapes = root.leaves().map((leaf) => {
return (
<g key={leaf.id} className={styles.rectangle}>
<rect
<g key={leaf.id}>
<Rectangle
x={leaf.x0}
y={leaf.y0}
width={leaf.x1 - leaf.x0}
height={leaf.y1 - leaf.y0}
stroke="transparent"
fill={colorScale(leaf.data.name)}
className={'opacity-80 hover:opacity-100'}
/>
<text

{/* <text
x={leaf.x0 + 3}
y={leaf.y0 + 3}
fontSize={12}
Expand All @@ -81,14 +57,14 @@ export const Treemap = ({ width, height, data }: TreemapProps) => {
className="font-light"
>
{leaf.data.value}
</text>
</text> */}
</g>
);
});

return (
<div>
<svg width={width} height={height} className={styles.container}>
<svg width={width} height={height}>
{allShapes}
</svg>
</div>
Expand Down
Loading

0 comments on commit 9c12ca5

Please sign in to comment.