layer_themes_distributed.mjs

/**
### mapp.layer.themes.distributed()
This module exports a function that applies a unique value theme to a feature based on a specified field value.
@module /layer/themes/distributed
 */

/**

 * @function distributed
 * @param {Object} theme - The theme configuration object.
 * @param {string} [theme.field='id'] - The field name used for determining the unique value.
 * @param {Object} [theme.lookup={}] - An object mapping unique values to their assigned styles.
 * @param {Array} [theme.boxes=[]] - An array of bounding boxes for the features.
 * @param {number} [theme.index=0] - The current index for assigning styles.
 * @param {Array} theme.categories - An array of category objects.
 * @param {Object} feature - The feature object.
 * @param {Object} feature.properties - The properties of the feature.
 * @returns {void}
 */
export default function (theme, feature) {
  if (!theme.lookup) {
    theme.lookup = {};
    theme.boxes = [];
    theme.index = 0;
  }

  const field = theme.field || 'id';

  // Get feature identifier for theme.
  const val = feature.properties[field];

  // Assign theme.style if val is falsy.
  if (!val) return;

  // The feature field property value already has a style assigned.
  if (theme.lookup[val]) {
    feature.style = theme.lookup[val].style;
    return;
  }

  // Get feature bounding box from geometry extent.
  const bbox = {
    extent: feature.getGeometry().getExtent(),
  };

  // Find intersecting bounding boxes with their assigned cat index.
  const intersects = theme.boxes.filter(
    (b) =>
      !(
        bbox.extent[0] > b.extent[2] ||
        bbox.extent[2] < b.extent[0] ||
        bbox.extent[1] > b.extent[3] ||
        bbox.extent[3] < b.extent[1]
      ),
  );

  // Push the current bbox into the array.
  theme.boxes.push(bbox);

  // Create a set of cat indices from intersecting bounding boxes.
  const set = new Set(intersects.map((b) => b.themeIdx));

  // Increase the current cat indix.
  theme.index++;

  // Reset cat index to 0 if the index reaches the length of the cat array.
  if (theme.index === theme.categories.length) theme.index = 0;

  // i is the cat index if not in set of intersecting boxes.
  let i = !set.has(theme.index) && parseInt(theme.index);

  // Current index is already in set of intersecting boxes.
  if (i === false) {
    // Iterate through the cat array.
    for (let free = 0; free < theme.categories.length; free++) {
      // Find an index which is not in set of intersecting bbox indices.
      if (!set.has(free)) {
        // Assign free index and break loop.
        i = free;
        break;
      }
    }
  }

  // Any index is in set of intersecting box indices.
  if (i === false) {
    // Just assign the current index. It is not possible to prevent some neighbouring cats.
    i = parseInt(theme.index);
  }

  // Assign index to the bounding box which is stored in the array of bounding boxes.
  bbox.themeIdx = i;

  // Assign the style to the lookup object for the feature field property value.
  theme.lookup[val] = theme.categories[i];

  feature.style = theme.lookup[val].style;
}