Source: tools/services/geom.js

import Point from 'ol/geom/Point'
import MultiPoint from 'ol/geom/MultiPoint'
import { fromExtent } from 'ol/geom/Polygon'
import LineString from 'ol/geom/LineString'
import MultiLineString from 'ol/geom/MultiLineString'
import GeometryCollection from 'ol/geom/GeometryCollection'
import { convertCoordinates } from './projections'

import cspline from 'ol-ext/render/Cspline'

import {
  lineString as TurfLineString,
} from '@turf/helpers'

import turfDistance from '@turf/distance'
import turfAlong from '@turf/along'
import turfTransformRotate from '@turf/transform-rotate'

/**
 * Créer un point
 * @param {Array<number>} options coordonnées
 * @returns ol.geom.Point
 */
export function createPoint (options) {
  return new Point(options)
}

/**
 * Créer un multi-point
 * @param {Array<Coordinate>|Array<number>} options coordonnées
 * @returns ol.geom.MultiPoint
 */
export function createMultiPoint (options) {
  return new MultiPoint(options)
}

/**
 * Créer une ligne
 * @param {Array<Array<number>>} options Tableau de coordonnés
 * @returns ol.geom.LineString
 */
export function createLineString (options) {
  return new LineString(options)
}
/**
 * Créer une multi-ligne
 * @param {Array<Array<Array<number>>>} options Tableau de coordonnés
 * @returns ol.geom.LineString
 */
export function createMultiLineString (options) {
  return new MultiLineString(options)
}

/**
 * Créer un polygone a partir d'une étendue
 * @param {Array<number>} extent Etendue [minx, miny, maxx, maxy]
 * @returns ol.geom.Polygon
 */
export function createPolygonFromExtent (extent) {
  return fromExtent(extent)
}

/**
 * Créer une GeometryCollection a partir d'un tableau de géométries
 * @param {Array<ol.geom.geometry>} geometries Tableau de géométries
 * @returns ol.geom.GeometryCollection
 */
export function createGeometryCollection (geometries) {
  return new GeometryCollection(geometries)
}

/**
 * Créer une spline (courbe de bezier) a partir d'un tableau de coordonnées
 * @param {Array<number>} coords Tableau de coordonnées
 * @param {Object} options options de spline
 * @param {Number} options.tension      a [0,1] number / can be interpreted as the "length" of the tangent, default 0.5
 * @param {Number} options.resolution   size of segment to split
 * @param {Integer} options.pointsPerSeg number of points per segment to add if no resolution is provided, default add 10 points per segment
 * @param {boolean} options.toCoords Retourne un tableau points en place d'une ol.geom.LineString
 * @returns ol.geom.LineString
 */
export function createSpline (coords, options = undefined) {
  const spline = cspline(coords, options)
  return options?.toCoords ? spline : new LineString(spline)
}

/**
 * Créer une spline (courbe de bezier) à partir de deux coordonnées
 * @param {Array<number>} fromCoordinate Coordonnée de départ
 * @param {Array<number>} toCoordinate Coordonée d'arrivé
 * @param {Object} options options de spline
 * @param {Number} options.angle Angle pour l'ajout du point central
 * @param {string} options.fromProjection Projection des coordonnées
 * @param {string} options.toProjection Projection de la LineString générée
 * @param {Number} options.tension      a [0,1] number / can be interpreted as the "length" of the tangent, default 0.5
 * @param {Number} options.resolution   size of segment to split
 * @param {Integer} options.pointsPerSeg number of points per segment to add if no resolution is provided, default add 10 points per segment
 * @returns ol.geom.LineString
 */
export function createSplineBetweenPoints (fromCoordinate, toCoordinate, options) {
  const { angle = 15, fromProjection = undefined, toProjection = undefined, ...splineOptions } = options

  // turf fonctionne en wgs84
  if (fromProjection && fromProjection !== 'EPSG:4326') {
    fromCoordinate = convertCoordinates(fromCoordinate, fromProjection, 'EPSG:4326')
    toCoordinate = convertCoordinates(toCoordinate, fromProjection, 'EPSG:4326')
  }

  const angleRadian = angle * Math.PI / 180
  // calcul un point a mi-distance (calcule une hypoténuse de triangle rectangle a partir de l'angle )
  let midDistanceBetweenPoint = turfDistance(fromCoordinate, toCoordinate) / 2
  midDistanceBetweenPoint = midDistanceBetweenPoint / Math.cos(angleRadian)

  // effectue une rotation du point a mi-distance pour ajouter un point d'infléction
  const pointOnLine = turfAlong(new TurfLineString([fromCoordinate, toCoordinate]), midDistanceBetweenPoint)
  const rotatedPoint = turfTransformRotate(pointOnLine, angle, { pivot: fromCoordinate })

  let coordinates = [fromCoordinate, rotatedPoint.geometry.coordinates, toCoordinate]
  if (toProjection && toProjection !== 'EPSG:4326') {
    coordinates = convertCoordinates(coordinates, 'EPSG:4326', toProjection)
  }
  // crée la spline a partir de ces trois points
  return createSpline(coordinates, splineOptions)
}