diff --git a/front/package.json b/front/package.json
index e521b3f..d7ccf80 100644
--- a/front/package.json
+++ b/front/package.json
@@ -9,6 +9,7 @@
"dependencies": {
"@fontsource/nunito-sans": "^5.2.7",
"@tailwindcss/postcss": "^4.1.17",
+ "d3": "^7.9.0",
"next": "^15.4.1",
"postcss": "^8.5.6",
"react": "^19.1.0",
diff --git a/front/src/app/embalse/[embalse]/page.tsx b/front/src/app/embalse/[embalse]/page.tsx
index a50d99c..b3262d1 100644
--- a/front/src/app/embalse/[embalse]/page.tsx
+++ b/front/src/app/embalse/[embalse]/page.tsx
@@ -1,4 +1,5 @@
-import Link from "next/link";
+import { mockData } from "../../model/reservoir-data";
+import { ReservoirGauge } from "./reservoir-gauge";
interface Props {
params: Promise<{ embalse: string }>;
@@ -6,9 +7,17 @@ interface Props {
export default async function EmbalseDetallePage({ params }: Props) {
const { embalse } = await params;
+
+ const reservoirData = mockData;
+
return (
-
Detalle del embalse: {embalse}
+
);
}
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-arcs.business.ts b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-arcs.business.ts
new file mode 100644
index 0000000..07a779d
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-arcs.business.ts
@@ -0,0 +1,40 @@
+import * as d3 from "d3";
+import { arcConfig } from "./model";
+
+type ArcGroup = d3.Selection;
+
+interface DrawArcParams {
+ arcGroup: ArcGroup;
+ endAngle: number;
+ fillColor: string;
+}
+
+const createArcGenerator = (endAngle: number) => {
+ return d3
+ .arc()
+ .innerRadius(arcConfig.innerRadius)
+ .outerRadius(arcConfig.outerRadius)
+ .startAngle(arcConfig.startAngle)
+ .endAngle(endAngle)
+ .cornerRadius(arcConfig.cornerRadius);
+};
+
+// TODO: add unit tests for calculateFilledAngle
+
+export const calculateFilledAngle = (percentage: number): number => {
+ // Ensure percentage is within valid range [0, 1]
+ const normalized = Math.max(0, Math.min(1, percentage));
+ // Total sweep of the arc (from start to end)
+ const totalAngle = arcConfig.endAngle - arcConfig.startAngle;
+ // Calculate where the filled arc should end based on percentage
+ return arcConfig.startAngle + normalized * totalAngle;
+};
+
+export const drawArc = ({ arcGroup, endAngle, fillColor }: DrawArcParams) => {
+ const arcGenerator = createArcGenerator(endAngle);
+
+ arcGroup
+ .append("path")
+ .attr("d", arcGenerator as any)
+ .style("fill", fillColor);
+};
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-arcs.component.tsx b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-arcs.component.tsx
new file mode 100644
index 0000000..e9d4cb8
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-arcs.component.tsx
@@ -0,0 +1,53 @@
+"use client";
+
+import * as d3 from "d3";
+import { useEffect, useRef } from "react";
+import { calculateFilledAngle, drawArc } from "./gauge-arcs.business";
+import { arcConfig, gaugeDimensions } from "./model";
+
+interface GaugeArcsProps {
+ percentage: number;
+}
+
+export const GaugeArcs = ({ percentage }: GaugeArcsProps) => {
+ const svgRef = useRef(null);
+
+ useEffect(() => {
+ if (!svgRef.current) return;
+
+ const svg = d3.select(svgRef.current);
+ svg.selectAll("*").remove();
+
+ // Center position
+ const centerX = gaugeDimensions.width / 2;
+ const centerY = arcConfig.outerRadius;
+
+ // Create centered group
+ const arcGroup = svg
+ .append("g")
+ .attr("transform", `translate(${centerX}, ${centerY})`);
+
+ // 1. Background arc (--color-total-water, full)
+ drawArc({
+ arcGroup,
+ endAngle: arcConfig.endAngle,
+ fillColor: "var(--color-total-water)",
+ });
+
+ // 2. Filled arc (primary color, based on percentage prop)
+ const filledEndAngle = calculateFilledAngle(percentage);
+ drawArc({
+ arcGroup,
+ endAngle: filledEndAngle,
+ fillColor: "var(--color-primary)",
+ });
+ }, [percentage]);
+
+ return (
+
+ );
+};
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-information.component.tsx b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-information.component.tsx
new file mode 100644
index 0000000..7479665
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/gauge-information.component.tsx
@@ -0,0 +1,20 @@
+interface Props {
+ percentage: number;
+ measurementDate: string;
+}
+
+export const GaugeInformation = ({ percentage, measurementDate }: Props) => {
+ const displayPercentage = `${Math.round(percentage * 100)}`;
+
+ return (
+
+
+ {displayPercentage}
+ %
+
+
+ {measurementDate}
+
+
+ );
+};
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/index.ts b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/index.ts
new file mode 100644
index 0000000..e73c0af
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/index.ts
@@ -0,0 +1,2 @@
+export * from "./gauge-arcs.component";
+export * from "./gauge-information.component";
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/model.ts b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/model.ts
new file mode 100644
index 0000000..fb87310
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/components/model.ts
@@ -0,0 +1,25 @@
+interface GaugeDimensions {
+ width: number;
+ height: number;
+}
+
+interface ArcConfig {
+ innerRadius: number;
+ outerRadius: number;
+ startAngle: number;
+ endAngle: number;
+ cornerRadius: number;
+}
+
+export const gaugeDimensions: GaugeDimensions = {
+ width: 220,
+ height: 184,
+};
+
+export const arcConfig: ArcConfig = {
+ innerRadius: 90,
+ outerRadius: 110,
+ startAngle: -Math.PI * 0.75,
+ endAngle: Math.PI * 0.75,
+ cornerRadius: 12,
+};
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/gauge-chart.component.tsx b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/gauge-chart.component.tsx
new file mode 100644
index 0000000..b28c872
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/gauge-chart.component.tsx
@@ -0,0 +1,31 @@
+import { gaugeDimensions } from "./components/model";
+import { GaugeInformation } from "./components";
+import { GaugeArcs } from "./components/gauge-arcs.component";
+
+interface Props {
+ percentage: number;
+ measurementDate: string;
+}
+
+export const GaugeChart = ({ percentage, measurementDate }: Props) => {
+ return (
+
+ {/* The SVG arc */}
+
+
+ {/* Center text */}
+
+
+
+
+ );
+};
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/index.ts b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/index.ts
new file mode 100644
index 0000000..1b2c7de
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-chart/index.ts
@@ -0,0 +1 @@
+export * from "./gauge-chart.component";
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-legend.component.tsx b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-legend.component.tsx
new file mode 100644
index 0000000..6cc484b
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/gauge-legend.component.tsx
@@ -0,0 +1,26 @@
+interface Props {
+ currentVolume: number;
+ totalCapacity: number;
+}
+
+export const GaugeLegend = ({ currentVolume, totalCapacity }: Props) => {
+ return (
+
+ {/* Embalsada (filled water) - uses primary color */}
+
+
+
+ Embalsada: {currentVolume}m³
+
+
+
+ {/* Total capacity - uses total-water color */}
+
+
+
+ Total: {totalCapacity}m³
+
+
+
+ );
+};
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/index.ts b/front/src/app/embalse/[embalse]/reservoir-gauge/index.ts
new file mode 100644
index 0000000..315d231
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/index.ts
@@ -0,0 +1 @@
+export * from "./reservoir-gauge";
diff --git a/front/src/app/embalse/[embalse]/reservoir-gauge/reservoir-gauge.tsx b/front/src/app/embalse/[embalse]/reservoir-gauge/reservoir-gauge.tsx
new file mode 100644
index 0000000..568f6d7
--- /dev/null
+++ b/front/src/app/embalse/[embalse]/reservoir-gauge/reservoir-gauge.tsx
@@ -0,0 +1,28 @@
+import { ReservoirData } from "../../../model/reservoir-data";
+import { GaugeChart } from "./gauge-chart";
+import { GaugeLegend } from "./gauge-legend.component";
+
+interface Props extends ReservoirData {
+ name: string;
+}
+
+export const ReservoirGauge = ({
+ name,
+ currentVolume,
+ totalCapacity,
+ measurementDate,
+}: Props) => {
+ // const percentage = currentVolume / totalCapacity;
+ // TODO: replace hardcoded % for real reservoir filled water percentage
+
+ return (
+
+
Embalse de {name}
+
+
+
+ );
+};
diff --git a/front/src/app/globals.css b/front/src/app/globals.css
index 7025098..f0275c1 100644
--- a/front/src/app/globals.css
+++ b/front/src/app/globals.css
@@ -24,6 +24,9 @@
/* Title color */
--color-title: #051c1f;
+ /* Graphic total water */
+ --color-total-water: #26d6ed;
+
/* Accesible visited link color */
--color-visited-link: #257782;
diff --git a/front/src/app/model/reservoir-data.ts b/front/src/app/model/reservoir-data.ts
new file mode 100644
index 0000000..6ace67b
--- /dev/null
+++ b/front/src/app/model/reservoir-data.ts
@@ -0,0 +1,11 @@
+export interface ReservoirData {
+ currentVolume: number;
+ totalCapacity: number;
+ measurementDate: string;
+}
+
+export const mockData: ReservoirData = {
+ currentVolume: 1500,
+ totalCapacity: 50000,
+ measurementDate: "25/12/2025",
+};
diff --git a/package-lock.json b/package-lock.json
index cc9078a..1397bce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
"dependencies": {
"@fontsource/nunito-sans": "^5.2.7",
"@tailwindcss/postcss": "^4.1.17",
+ "d3": "^7.9.0",
"next": "^15.4.1",
"postcss": "^8.5.6",
"react": "^19.1.0",
@@ -1416,6 +1417,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/cookie": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
@@ -1474,6 +1484,407 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/d3": {
+ "version": "7.9.0",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+ "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "3",
+ "d3-axis": "3",
+ "d3-brush": "3",
+ "d3-chord": "3",
+ "d3-color": "3",
+ "d3-contour": "4",
+ "d3-delaunay": "6",
+ "d3-dispatch": "3",
+ "d3-drag": "3",
+ "d3-dsv": "3",
+ "d3-ease": "3",
+ "d3-fetch": "3",
+ "d3-force": "3",
+ "d3-format": "3",
+ "d3-geo": "3",
+ "d3-hierarchy": "3",
+ "d3-interpolate": "3",
+ "d3-path": "3",
+ "d3-polygon": "3",
+ "d3-quadtree": "3",
+ "d3-random": "3",
+ "d3-scale": "4",
+ "d3-scale-chromatic": "3",
+ "d3-selection": "3",
+ "d3-shape": "3",
+ "d3-time": "3",
+ "d3-time-format": "4",
+ "d3-timer": "3",
+ "d3-transition": "3",
+ "d3-zoom": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-axis": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+ "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-brush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+ "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "3",
+ "d3-transition": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+ "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-contour": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+ "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-delaunay": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+ "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+ "license": "ISC",
+ "dependencies": {
+ "delaunator": "5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+ "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-drag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+ "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-selection": "3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+ "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+ "license": "ISC",
+ "dependencies": {
+ "commander": "7",
+ "iconv-lite": "0.6",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json.js",
+ "csv2tsv": "bin/dsv2dsv.js",
+ "dsv2dsv": "bin/dsv2dsv.js",
+ "dsv2json": "bin/dsv2json.js",
+ "json2csv": "bin/json2dsv.js",
+ "json2dsv": "bin/json2dsv.js",
+ "json2tsv": "bin/json2dsv.js",
+ "tsv2csv": "bin/dsv2dsv.js",
+ "tsv2json": "bin/dsv2json.js"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-fetch": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+ "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dsv": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+ "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-quadtree": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-geo": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+ "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.5.0 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+ "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-polygon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+ "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-quadtree": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+ "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-random": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+ "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+ "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-interpolate": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+ "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-transition": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+ "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3",
+ "d3-dispatch": "1 - 3",
+ "d3-ease": "1 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-timer": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "d3-selection": "2 - 3"
+ }
+ },
+ "node_modules/d3-zoom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+ "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-dispatch": "1 - 3",
+ "d3-drag": "2 - 3",
+ "d3-interpolate": "1 - 3",
+ "d3-selection": "2 - 3",
+ "d3-transition": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/daisyui": {
"version": "5.5.5",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.5.tgz",
@@ -1496,6 +1907,15 @@
"node": ">=6"
}
},
+ "node_modules/delaunator": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
+ "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
+ "license": "ISC",
+ "dependencies": {
+ "robust-predicates": "^3.0.2"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"license": "MIT",
@@ -1998,6 +2418,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/is-arrayish": {
"version": "0.3.2",
"license": "MIT",
@@ -3162,6 +3591,12 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/robust-predicates": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+ "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
+ "license": "Unlicense"
+ },
"node_modules/rollup": {
"version": "4.46.4",
"dev": true,
@@ -3200,6 +3635,12 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"license": "MIT"