tool_merge.js

import { columns } from "../helpers/utils";

/**
 * @function tool/merge
 * @description `tool.merge` is a function to join a geoJSON and a data file. It returns a GeoJSON FeatureCollection.
 * @see {@link https://observablehq.com/@neocartocnrs/handle-geometries}
 *
 * @property {Array} geom - a GeoJSON FeatureCollection
 * @property {string} geom_id - geom id
 * @property {Array} data - array containg data
 * @property {string} data_id - data id
 * @property {string} [id] - id (if ids are the same in data and geometries)
 */

export function merge({ geom, geom_id, data, data_id, id } = {}) {
  if (id) {
    geom_id = id;
    data_id = id;
  }

  // Join

  geom = JSON.parse(JSON.stringify(geom));
  let tmp = addUnderscore(
    data,
    geom.features.map((d) => d.properties),
    data_id
  );
  data = tmp[0];
  data_id = tmp[1];

  let accessor = new Map(data.map((d) => [d[data_id], d]));

  let features = [];
  geom.features.forEach((d) => {
    let obj = {};
    Object.keys(d).forEach((e) => {
      if (e == "properties") {
        Object.assign(obj, {
          properties: Object.assign(
            { ...d.properties },
            { ...accessor.get(d.properties[geom_id]) }
          ),
        });
      } else {
        Object.assign(obj, { [e]: d[e] });
      }
    });
    features.push(obj);
  });

  let featureCollection = {};
  Object.keys(geom).forEach((d) => {
    featureCollection = Object.assign(featureCollection, {
      [d]: d == "features" ? features : geom[d],
    });
  });

  // diagnostic

  let ids_geom = geom.features.map((d) => d.properties[geom_id]);
  let ids_data = data.map((d) => d[data_id]);
  let duplicate_geom = ids_geom.length - new Set(ids_geom).size;
  let duplicate_data = ids_data.length - new Set(ids_data).size;
  let intersection = ids_geom.filter((x) => ids_data.includes(x));
  let matched = intersection.filter((x) => ids_data.includes(x));
  let unmatched_geom = ids_geom.filter((x) => !ids_data.includes(x));
  let unmatched_data = ids_data.filter((x) => !ids_geom.includes(x));
  let diagnostic = {
    duplicate_data,
    duplicate_geom,
    geom: ids_geom,
    data: ids_data,
    matched,
    unmatched_geom,
    unmatched_data,
  };

  return { featureCollection, diagnostic };
}

function addUnderscore(data1, data2, id) {
  let arr1 = columns(data1);
  let arr2 = columns(data2);

  let output = [...arr1];
  while (output.filter((x) => arr2.includes(x)).length !== 0) {
    output
      .filter((x) => arr2.includes(x))
      .forEach((d) => {
        output[output.indexOf(d)] = "_" + d;
      });
  }

  let accessor = new Map(
    arr1.map((d, i) => {
      return [d, output[i]];
    })
  );

  let newArray = [];
  data1.forEach((d) => {
    let obj = {};
    Object.keys(d).forEach((e) => {
      obj[accessor.get(e)] = d[e];
    });
    newArray.push(obj);
  });

  return [newArray, accessor.get(id)];
}