import {
diamond,
dot,
hexbin,
random,
square,
triangle,
h3,
square_sph,
pointstogrid,
linestogrid,
polygonstogrid,
project,
} from "geogrid";
import { implantation } from "../helpers/utils";
/**
* @function tool/grid
* @description The `tool.grid` function creates a regular grid as a GeoJSON object.
* For all grid types (except "h3"), the function returns a GeoJSON with SVG coordinates in page layout.
* For type "h3", it returns geographic coordinates (latitude and longitude).
* @see {@link https://observablehq.com/@neocartocnrs/regular-grids}
* @param {object} svg - The SVG container or map object
* @param {object} opts - Options
* @property {string} [type="square"] - Grid type ("square", "dot", "diamond", "hexbin"/"hex", "triangle", "arbitrary"/"random", "h3"/"h3geo"/"hexgeo"/"hexbingeo").
* @property {number} [step=50] - Grid step (except for "h3" type).
* @property {boolean} [overflow=true] - If true, the grid may exceed the bounding box.
* @property {number} [level=0] - Geographical hexbin level ("h3" type only). From 0 (large hexagons) to 4 (small hexagons).
* @property {object} [domain] - GeoJSON defining the extent (h3 only).
* @property {boolean} [rewind] - Rewind polygons (h3 only).
* @property {object} [data] - GeoJSON input for statistical computation.
* @example
* geoviz.tool.grid(svg, { type: "diamond", step: 100 });
*/
export function grid(svg, opts = {}) {
const {
coords = "geo",
data = undefined,
var: variable = undefined, // <-- alias pour var
values = false,
step = 50,
type = "square",
level = 0,
overflow = true,
domain = undefined,
rewind = undefined,
ratio = false,
ratio_factor = 1,
} = opts;
// --- Create grid ---
let grid;
switch (type) {
case "square":
grid = square({ step, width: svg.width, height: svg.height, overflow });
break;
case "arbitrary":
case "random":
grid = random({ step, width: svg.width, height: svg.height, overflow });
break;
case "dot":
grid = dot({ step, width: svg.width, height: svg.height, overflow });
break;
case "diamond":
grid = diamond({ step, width: svg.width, height: svg.height, overflow });
break;
case "hexbin":
case "hex":
grid = hexbin({ step, width: svg.width, height: svg.height, overflow });
break;
case "triangle":
grid = triangle({ step, width: svg.width, height: svg.height, overflow });
break;
case "square_spherical":
case "square_sph":
grid = square_sph({
step,
domain,
});
break;
case "h3":
case "h3geo":
case "hexgeo":
case "hexbingeo":
grid = h3({ level, domain, rewind });
break;
default:
throw new Error(`Unknown grid type: ${type}`);
}
// --- If data is provided, compute stats ---
let output = grid; // valeur par défaut = la grille seule
if (data) {
console.log("data exists");
let geom = data;
if (coords !== "svg" && grid.geo === false) {
geom = project(JSON.parse(JSON.stringify(data)), {
projection: svg.projection,
});
}
const typeData = implantation(data);
switch (typeData) {
case 1:
output = pointstogrid({
grid,
points: geom,
var: variable,
values,
spherical: grid.geo ? true : false,
});
break;
case 2:
output = linestogrid({
grid,
lines: geom,
var: variable,
values,
spherical: grid.geo ? true : false,
});
break;
case 3:
output = polygonstogrid({
grid,
polygons: geom,
var: variable,
values,
spherical: grid.geo ? true : false,
});
break;
default:
console.warn("Unknown data geometry type:", typeData);
output = grid;
}
}
// Compute ratio ?
if (ratio === true && Array.isArray(variable) && variable.length === 2) {
output.features.forEach((feature) => {
const val1 = feature.properties[variable[0]];
const val2 = feature.properties[variable[1]];
feature.properties.ratio = (val1 / val2) * ratio_factor;
});
}
// Unproject ?
return output;
}