Source: layer/CustomHeatMap.js

import Heatmap from 'ol/layer/Heatmap'
import VectorEventType from 'ol/source/VectorEventType'
import Point from 'ol/geom/Point'

import { getUid } from 'ol/util'
import { listen } from 'ol/events'

import { getCentroid } from '../tools/services/centroid'

/**
 * @param  {ol.source.Vector.VectorSourceEvent|ol.Collection.CollectionEvent} evt Event
 * @return {ol.Feature} Feature
 */
function getFeatureFromEvent (evt) {
  if (
    (evt).feature
  ) {
    return (evt).feature
  } else if (
    (evt).element
  ) {
    return (
      (evt).element
    )
  }
}

function getFeatureReplacement (feature, allGeomType = false) {
  const geomType = feature.getGeometry() ? feature.getGeometry().getType() : null
  // on garde les points tel quel
  if (geomType === 'Point') {
    return null
  }
  const newFeature = feature.clone()
  if (geomType === 'GeometryCollection') {
    // un seul point dans la collection: on l'utilise
    const geometryPoints = newFeature.getGeometry().getGeometries().filter((geom) => geom.getType() === 'Point')
    if (geometryPoints.length === 1) { newFeature.setGeometry(geometryPoints[0]) }
  } else if (geomType !== 'Point' && allGeomType) {
    const geomCoordinates = getCentroid(newFeature)
    if (geomCoordinates) {
      newFeature.setGeometry(new Point([geomCoordinates[0], geomCoordinates[1]]))
    }
  }
  return newFeature
}

class CustomHeatmap extends Heatmap {
  constructor (options) {
    options = options || { }

    super(options)

    this.listenerKeys = []

    this.originalToSourceMap = {}
    this.customPendingAdd = {}
    this.customPendingRemove = {}

    // Heatmap.call(this, options)

    const source = this.getSource()

    const handleFeatureSwap = function (feature) {
      const newFeature = getFeatureReplacement(feature, true)
      if (newFeature) {
      // garde une trace de mapping des features (au cas ou on essais d'en supprimer une via remove(feature) on reconnaitra notre copie)
        this.originalToSourceMap[getUid(feature)] = newFeature
        this.customPendingAdd[getUid(newFeature)] = 1
        // on diffère la suppression de notre feature de la source dans le temps car c'est pas "normal"
        // de supprimer une feature dans l'event d'ajout
        setTimeout(() => {
          this.customPendingRemove[getUid(feature)] = 1
          source.removeFeature(feature)
          source.addFeature(newFeature)
        }, 10)
      }
    }

    this.listenerKeys.push(
      listen(
        source,
        VectorEventType.ADDFEATURE,
        (evt) => {
          const feature = getFeatureFromEvent(evt)

          // Si c'est une feature qu'on est en train d'ajouter via cette méthode on ne fait rien.
          if (!this.customPendingAdd[getUid(feature)]) {
            handleFeatureSwap.call(this, feature)
          } else {
            delete this.customPendingAdd[getUid(feature)]
          }
        },
        this
      ),
      listen(
        source,
        VectorEventType.REMOVEFEATURE,
        (evt) => {
        // lorsque l'on supprime une feature via l'api, on vérifi si c'est pas une feature que l'on a modifié..
          const feature = getFeatureFromEvent(evt)

          if (this.customPendingRemove[getUid(feature)]) {
            delete this.customPendingRemove[getUid(feature)]
          } else {
            const clonedFeature = this.originalToSourceMap[getUid(feature)]
            if (clonedFeature) {
              source.removeFeature(clonedFeature)
            }
          }
          delete this.originalToSourceMap[getUid(feature)]
        },
        this
      )
      , listen(
        source,
        VectorEventType.CHANGEFEATURE,
        (evt) => {
        // lorsque l'on modifie une feature via l'api, on vérifi si c'est pas une feature que l'on a modifié..
          const feature = getFeatureFromEvent(evt)
          const clonedFeature = this.originalToSourceMap[getUid(feature)]
          if (clonedFeature) {
          // remove fera bien son travail
            source.removeFeature(feature)
            // puis re-ajoute la feature
            source.addFeature(feature)
          } else {
            handleFeatureSwap.call(this, feature)
          }
        },
        this
      )
    )
  }
}

export default CustomHeatmap