Source: tools/services/projections.js

import * as proj from 'ol/proj'
import { register } from 'ol/proj/proj4'
import proj4 from 'proj4'

/**
 * Permet de convertir une coordonées dans un autre système de projection
 *
 * @param  {Array<number>} coordinates      Coordonées en entrées (point, linestring, ...)
 * @param  {string}        originProjection Système de projection en entrée
 * @param  {string}        destProjection   Système de projection en sortie
 * @return {Array<number>}                  Coordonées convertis
 */
export function convertCoordinates (coordinates, originProjection, destProjection) {
  if (originProjection === destProjection) return coordinates
  // Dans le cas d'une coordonnée simple, on applique la reprojection
  if (typeof coordinates[0] === 'number' && typeof coordinates[1] === 'number') {
    return proj.transform(coordinates, originProjection, destProjection)
  }

  // Dans le cas d'une coordonnée complexe, appel de la fonction récursivement
  return coordinates.map(coord => convertCoordinates(coord, originProjection, destProjection))
}

/**
 * converti des géométrie GeoJSON vers
 * @param {Geometry} geometry         Geométrie Geojson
 * @param  {string}  originProjection Système de projection en entrée
 * @param  {string}  destProjection   Système de projection en sortie
 * @returns {Geometry}                Géométrie convertie
 */
export function convertGeojsonGeometry (geometry, originProjection, destProjection) {
  if (originProjection === destProjection) return geometry
  if (!geometry || !geometry.type || !geometry.coordinates) {
    return null
  }

  if (geometry.type === 'GeometryCollection') {
    return {
      type: 'GeometryCollection',
      geometries: geometry.geometries.map(item =>
        convertGeojsonGeometry(item, originProjection, destProjection)),
    }
  }

  return {
    type: geometry.type,
    coordinates: convertCoordinates(geometry.coordinates, originProjection, destProjection),
  }
}

/**
 * converti des Features OpenLayers vers un systeme
 * @param {Array<ol.Feature>} geometry  Feature OpenLayers
 * @param  {string}  originProjection Système de projection en entrée
 * @param  {string}  destProjection   Système de projection en sortie
 * @returns {Array<ol.Feature>} Copie des features reprojeté
 */
export function convertFeatures (features, originProjection, destProjection) {
  if (originProjection === destProjection) return features
  return features.map((feature) => {
    const newFeature = feature.clone()
    newFeature.setId(feature.getId())

    if (newFeature.getGeometry() !== null) { newFeature.getGeometry().transform(originProjection, destProjection) }
    return newFeature
  })
}

/**
 * Permet de convertir une BBOX dans un autre système de projection
 *
 * @param  {ol.Extent} extent      BBox [minx, miny, maxx, maxy]
 * @param  {string}        originProjection Système de projection en entrée
 * @param  {string}        destProjection   Système de projection en sortie
 * @return {Array<number>}                  BBox convertis
 */
export function convertExtent (extent, originProjection, destProjection) {
  if (originProjection === destProjection) return extent
  return proj.transformExtent(extent, originProjection, destProjection)
}

/**
 * Enregistre des nouvelles projections en mémoire
 * @param {Object[]} projections
 * @param {string} projection[].name Nom de la projection (EPSG:4326)
 * @param {string} projection[].projection Définition proj4 (exemple https://epsg.io/27562)
 */
export function loadProjections (projections) {
  projections = projections.filter(({ name, projection }) => {
    if (!name || !projection) {
      console.warn('Projection mal définie', name, projection)
      return false
    }
    return true
  }).map(({ name, projection }) => [name, projection])

  proj4.defs(projections)

  // ajoute ces nouvelles projections a OpenLayers
  register(proj4)
}