<commit_before>import Feature from "ol/Feature";
import * as proj from "ol/proj";
import VectorSource from "ol/source/Vector";
import axios from "axios/index";
import Point from "ol/geom/Point";
import GeoJSON from "ol/format/GeoJSON";
import { Style, Stroke, Fill } from "ol/style";
import VectorLayer from "ol/layer/Vector";

import { fontIsLoaded, ARROW_SIZE } from "./utils";
import { makeStyle, setUpMap, setUpPopup, fitBounds } from "./common";
import makeLayerControl from "./layerControl";
import makeSearchControl from "./searchControl";
import getFormatPopups from "./itemPopups";

import logger from "@agir/lib/utils/logger";
const log = logger(__filename);

const OFFSET = 0.00005;

function disambiguate(points) {
  const itemMap = Object.create(null);
  let key, n, i, p, angle;

  for (let p of points) {
    key = JSON.stringify(p.coordinates.coordinates);
    itemMap[key] = itemMap[key] || [];
    itemMap[key].push(p);
  }

  for (key of Object.keys(itemMap)) {
    n = itemMap[key].length;
    if (n > 1) {
      for (i = 0; i < n; i++) {
        p = itemMap[key][i];
        angle = Math.PI / 2 + (i * 2 * Math.PI) / n;
        p.coordinates.coordinates = [
          p.coordinates.coordinates[0] + OFFSET * Math.cos(angle),
          p.coordinates.coordinates[1] + OFFSET * Math.sin(angle),
        ];
      }
    }
  }
}

class Display {
  constructor(types, subtypes, listType) {
    this.types = types;
    this.formatPopup = getFormatPopups(types, subtypes)[listType];
    this.sources = {};
    this.layers = {};
    this.typeStyles = {};
    this.typePastStyles = {};
    for (let type of types) {
      this.sources[type.id] = new VectorSource();
      this.layers[type.id] = new VectorLayer({ source: this.sources[type.id] });
      this.typeStyles[type.id] = makeStyle(type);
      if (listType === "events") {
        this.typePastStyles[type.id] = makeStyle(type, { color: false });
      }
    }

    // Subtypes
    this.subtypeStyles = {};
    this.subtypePastStyles = {};
    this.popupAnchors = {};
    this.sourceForSubtype = {};
    for (let subtype of subtypes) {
      this.subtypeStyles[subtype.id] =
        makeStyle(subtype) || this.typeStyles[subtype.type];
      if (listType === "events") {
        this.subtypePastStyles[subtype.id] =
          makeStyle(subtype, {
            color: false,
          }) || this.typePastStyles[subtype.type];
      }
      this.popupAnchors[subtype.id] = subtype.popupAnchor;
      this.sourceForSubtype[subtype.id] = this.sources[subtype.type];
    }
  }

  getLayers() {
    return this.types.map((type) => this.layers[type.id]);
  }

  getFeatureFor(item) {
    const feature = new Feature({
      geometry: new Point(proj.fromLonLat(item.coordinates.coordinates)),
      popupAnchor: (this.popupAnchors[item.subtype] || -5) - ARROW_SIZE,
      popupContent: this.formatPopup(item),
    });
    feature.setId(item.id);

    if (item.subtype && this.subtypeStyles[item.subtype]) {
      if (
        typeof item.end_time === "undefined" ||
        new Date(item.end_time) > new Date()
      ) {
        feature.setStyle(this.subtypeStyles[item.subtype]);
      } else {
        feature.setStyle(this.subtypePastStyles[item.subtype]);
      }
    } else {
      feature.setStyle(this.typeStyles[item.type]);
    }

    return feature;
  }

  updateFeatures(data, hideInactive) {
    for (let item of data) {
      const feature = this.getFeatureFor(item);
      const source = item.subtype
        ? this.sourceForSubtype[item.subtype]
        : this.sources[item.type];
      if (!source) {
        return;
      }
      if (
        hideInactive &&
        !item.is_active &&
        item.location_country === "FR" &&
        source.hasFeature(feature)
      ) {
        source.removeFeature(source.getFeatureById(item.id));
        return;
      }
      if (!source.hasFeature(feature)) {
        source.addFeature(feature);
      }
    }
  }

  getControls(data) {
    return makeLayerControl(
      this.types.map((type) => ({
        label: type.label,
        color: type.color,
        layer: this.layers[type.id],
      })),
      (hideInactive) => this.updateFeatures(data, hideInactive)
    );
  }
}

export default async function listMap(
  htmlElementId,
  {
    endpoint,
    listType,
    types,
    subtypes,
    bounds,
    focusGeometry,
    showSearch = true,
    showActiveControl = true,
    showLayerControl = true,<commit_msg>fix: list map feature updating function<commit_after>import Feature from "ol/Feature";
import * as proj from "ol/proj";
import VectorSource from "ol/source/Vector";
import axios from "axios/index";
import Point from "ol/geom/Point";
import GeoJSON from "ol/format/GeoJSON";
import { Style, Stroke, Fill } from "ol/style";
import VectorLayer from "ol/layer/Vector";

import { fontIsLoaded, ARROW_SIZE } from "./utils";
import { makeStyle, setUpMap, setUpPopup, fitBounds } from "./common";
import makeLayerControl from "./layerControl";
import makeSearchControl from "./searchControl";
import getFormatPopups from "./itemPopups";

import logger from "@agir/lib/utils/logger";
const log = logger(__filename);

const OFFSET = 0.00005;

function disambiguate(points) {
  const itemMap = Object.create(null);
  let key, n, i, p, angle;

  for (let p of points) {
    key = JSON.stringify(p.coordinates.coordinates);
    itemMap[key] = itemMap[key] || [];
    itemMap[key].push(p);
  }

  for (key of Object.keys(itemMap)) {
    n = itemMap[key].length;
    if (n > 1) {
      for (i = 0; i < n; i++) {
        p = itemMap[key][i];
        angle = Math.PI / 2 + (i * 2 * Math.PI) / n;
        p.coordinates.coordinates = [
          p.coordinates.coordinates[0] + OFFSET * Math.cos(angle),
          p.coordinates.coordinates[1] + OFFSET * Math.sin(angle),
        ];
      }
    }
  }
}

class Display {
  constructor(types, subtypes, listType) {
    this.types = types;
    this.formatPopup = getFormatPopups(types, subtypes)[listType];
    this.sources = {};
    this.layers = {};
    this.typeStyles = {};
    this.typePastStyles = {};
    for (let type of types) {
      this.sources[type.id] = new VectorSource();
      this.layers[type.id] = new VectorLayer({ source: this.sources[type.id] });
      this.typeStyles[type.id] = makeStyle(type);
      if (listType === "events") {
        this.typePastStyles[type.id] = makeStyle(type, { color: false });
      }
    }

    // Subtypes
    this.subtypeStyles = {};
    this.subtypePastStyles = {};
    this.popupAnchors = {};
    this.sourceForSubtype = {};
    for (let subtype of subtypes) {
      this.subtypeStyles[subtype.id] =
        makeStyle(subtype) || this.typeStyles[subtype.type];
      if (listType === "events") {
        this.subtypePastStyles[subtype.id] =
          makeStyle(subtype, {
            color: false,
          }) || this.typePastStyles[subtype.type];
      }
      this.popupAnchors[subtype.id] = subtype.popupAnchor;
      this.sourceForSubtype[subtype.id] = this.sources[subtype.type];
    }
  }

  getLayers() {
    return this.types.map((type) => this.layers[type.id]);
  }

  getFeatureFor(item) {
    const feature = new Feature({
      geometry: new Point(proj.fromLonLat(item.coordinates.coordinates)),
      popupAnchor: (this.popupAnchors[item.subtype] || -5) - ARROW_SIZE,
      popupContent: this.formatPopup(item),
    });
    feature.setId(item.id);

    if (item.subtype && this.subtypeStyles[item.subtype]) {
      if (
        typeof item.end_time === "undefined" ||
        new Date(item.end_time) > new Date()
      ) {
        feature.setStyle(this.subtypeStyles[item.subtype]);
      } else {
        feature.setStyle(this.subtypePastStyles[item.subtype]);
      }
    } else {
      feature.setStyle(this.typeStyles[item.type]);
    }

    return feature;
  }

  updateFeatures(data, hideInactive) {
    for (let item of data) {
      const feature = this.getFeatureFor(item);
      const source = item.subtype
        ? this.sourceForSubtype[item.subtype]
        : this.sources[item.type];
      if (!source) {
        continue;
      }
      if (hideInactive && !item.is_active && item.location_country === "FR") {
        source.hasFeature(feature) &&
          source.removeFeature(source.getFeatureById(item.id));
        continue;
      }
      if (!source.hasFeature(feature)) {
        source.addFeature(feature);
      }
    }
  }

  getControls(data) {
    return makeLayerControl(
      this.types.map((type) => ({
        label: type.label,
        color: type.color,
        layer: this.layers[type.id],
      })),
      (hideInactive) => this.updateFeatures(data, hideInactive)
    );
  }
}

export default async function listMap(
  htmlElementId,
  {
    endpoint,
    listType,
    types,
    subtypes,
    bounds,
    focusGeometry,
    showSearch = true,
    showActiveControl = true,
    showLayerControl = true,
{'fraction_non_alphanumeric': 0.14174560216508797, 'fraction_numerical': 0.0029318899413622013, 'mean_word_length': 2.7077759197324416, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1670203', 'n_tokens_mistral': 2910, 'n_tokens_neox': 2875, 'n_words': 562}