Source: tools/services/centroid.js

import Feature from 'ol/Feature'

import Point from 'ol/geom/Point'
import Circle from 'ol/geom/Circle'
import GeometryCollection from 'ol/geom/GeometryCollection'
import MultiPoint from 'ol/geom/MultiPoint'
import Polygon from 'ol/geom/Polygon'
import MultiPolygon from 'ol/geom/MultiPolygon'
import LineString from 'ol/geom/LineString'
import MultiLineString from 'ol/geom/MultiLineString'

/**
 * Permet de récupérer le centroid d'une feature
 * Les geometries prisent en compte sont Point, Polygon, LineString
 *
 * @param  {ol.Feature | ol.geom.SimpleGeometry} featureOrGeom Feature ou Geometry
 * @return {Array}              Centroid ou false;
 */
export function getCentroid (featureOrGeom) {
  let polygons, isPair, lineIndex

  const geom = featureOrGeom.constructor === Feature ? featureOrGeom.getGeometry() : featureOrGeom
  switch (geom.constructor) {
    case Point:
      return geom.getCoordinates()
    case Circle:
      return geom.getCenter()
    case GeometryCollection:
    case MultiPoint: {
      const [minx, miny, maxx, maxy] = geom.getExtent()
      const x = minx + ((maxx - minx) / 2)
      const y = miny + ((maxy - miny) / 2)
      return [x, y]
    }
    case Polygon:
      return geom.getInteriorPoint().getCoordinates()
    case MultiPolygon:
      // Tri décroissant par superficie (pour utiliser le centroid du max)
      polygons = geom.getPolygons().sort((a, b) => b.getArea() - a.getArea())
      return polygons[0].getInteriorPoint().getCoordinates()
    case LineString:
      return geom.getCoordinateAt(0.5)
    case MultiLineString:
      // re-changement d'algo:
      // si nombre de string impair on prend la moitié de la string du milieu
      // si pair la coordoonné du croisement de celles du milieu
      isPair = geom.getLineStrings().length % 2 === 0
      lineIndex = Math.floor(geom.getLineStrings().length / 2)
      return isPair ? geom.getLineString(lineIndex).getLastCoordinate() : geom.getLineString(lineIndex).getCoordinateAt(0.5)
      // changement d'algo, prend une coordonnées au milieu des coordonées a plat
    default:
      return null
  }
}
/**
   * Permet de retourner uniquement un point pour une géométrie
   * Si ce n'est pas possible, on ne retourne rien .
   *
   * @param {ol.geom.Geometry} geometry Géométrie à traiter
   * @param {'centroid'| 'start' | 'end'} [position='centroid'] Position du point a retourner
   * @returns {ol.geom.Point | null} Point ou null
   */
export function getPointForGeometry (geometry, position = 'centroid') {
  if (!geometry) {
    return null
  }

  const usableGeometry = geometry.getType() === 'GeometryCollection'
    ? geometry.getGeometriesArray()?.[0]
    : geometry

  // collection vide...
  if (!usableGeometry) {
    return null
  }

  // pour point ou on perd en performance a recréer une geom de type point a partir des coordonnées extraites de lui-même
  if (usableGeometry.getType() === 'Point') {
    return usableGeometry
  }

  switch (position) {
    case 'start': {
      const coord = usableGeometry.getFirstCoordinate()
      return coord !== null ? new Point(coord) : null
    }
    case 'end': {
      const coord = usableGeometry.getLastCoordinate()
      return coord !== null ? new Point(coord) : null
    }
    default: {
      const coord = getCentroid(usableGeometry)
      return coord !== null ? new Point(coord) : null
    }
  }
}

export function getPointsforGeometries (geometryOrGeometryCollection, position = 'centroid') {
  const geometries = geometryOrGeometryCollection.getType() === 'GeometryCollection'
    ? geometryOrGeometryCollection.getGeometriesArray()
    : geometryOrGeometryCollection.getType() === 'MultiLineString'
      ? geometryOrGeometryCollection.getLineStrings()
      : geometryOrGeometryCollection.getType() === 'MultiPolygon'
        ? geometryOrGeometryCollection.getPolygons()
        : geometryOrGeometryCollection.getType() === 'MultiPoint'
          ? geometryOrGeometryCollection.getPoints()
          : [geometryOrGeometryCollection]

  const coordinates = geometries.reduce((acc, geometry) => {
    if (['MultiLineString', 'MultiPolygon', 'MultiPoint'].includes(geometry.getType())) {
      // pour les multi on récupère les points de chaque géométrie
      const points = getPointsforGeometries(geometry, position)
      return acc.concat(points.getCoordinates())
    }
    const point = getPointForGeometry(geometry, position)
    acc.push(point.getCoordinates())
    return acc
  }, [])
  return new MultiPoint(coordinates)
}