Source: operator/save.js

import { featureCollection } from "@turf/helpers";
import { bbox } from "@turf/bbox";
import { intersect } from "@turf/intersect";
import RBush from "rbush";
import { geoPath } from "d3-geo";
import { groups } from "d3-array";

const d3 = Object.assign({}, { geoPath, groups });

/**
 * @function polygonstogrid
 * @description Répartit des polygones (optionnellement pondérés) sur une grille polygonale.
 * @property {object} [polygons] - GeoJSON de polygones
 * @property {object} [grid] - GeoJSON de grille
 * @property {string} [var] - Champ de pondération (facultatif)
 */
export function polygonstogrid(
  opts = { grid: undefined, polygons: undefined, var: undefined }
) {
  const t0 = performance.now();

  const grid = opts.grid.features;
  const polys = opts.polygons.features;
  const gridbyindex = new Map(grid.map((d, i) => [i, d]));

  // ---- 1. Créer l’index spatial RBush ----
  const tree = new RBush();
  const items = grid.map((g, i) => {
    const [minX, minY, maxX, maxY] = bbox(g);
    return { minX, minY, maxX, maxY, i };
  });
  tree.load(items);

  const arr = [];
  const path = d3.geoPath();

  // ---- 2. Boucle sur les polygones ----
  polys.forEach((p, i) => {
    const area = path.area(p);
    const [minX, minY, maxX, maxY] = bbox(p);

    // 3. Chercher uniquement les cellules qui peuvent intersecter
    const candidates = tree.search({ minX, minY, maxX, maxY });

    // 4. Tester seulement les candidats
    for (const cand of candidates) {
      const g = gridbyindex.get(cand.i);
      const f = intersect(featureCollection([p, g]));
      if (f) {
        const areapiece = path.area(f);
        arr.push([i, cand.i, areapiece / area]);
      }
    }
  });

  // ---- 5. Calcul des valeurs ----
  const hasVar = opts.var !== undefined && opts.var !== null;

  const accessor = new Map(
    polys.map((d, i) => [
      i,
      hasVar ? parseFloat(d.properties[opts.var]) || 0 : 1,
    ])
  );

  const datagrid = d3.groups(arr, (d) => d[1]);

  function getsum(cell) {
    const vals = cell[1];
    if (hasVar) {
      // Cas pondéré
      let sum = 0;
      vals.forEach((d) => {
        sum += accessor.get(d[0]) * d[2];
      });
      return sum === 0 ? undefined : sum;
    } else {
      // Cas non pondéré : juste compter le nombre de polygones intersectés
      const uniquePolygons = new Set(vals.map((d) => d[0]));
      return uniquePolygons.size;
    }
  }

  // ---- 6. Assemblage du résultat ----
  const result = {
    type: "FeatureCollection",
    features: datagrid.map(([key, vals]) => {
      const tmp = gridbyindex.get(key);
      return {
        type: tmp.type,
        properties: { ...tmp.properties, sum: getsum([key, vals]) },
        geometry: tmp.geometry,
      };
    }),
  };

  const t1 = performance.now();
  console.log(`Temps d'exécution: ${(t1 - t0).toFixed(2)} ms`);
  return result;
}