All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lucuma.core.math.RightAscension.scala Maven / Gradle / Ivy

There is a newer version: 0.108.0
Show newest version
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.core.math

import cats.Order
import cats.Show
import lucuma.core.math.parser.AngleParsers
import lucuma.core.optics.Format
import monocle.*

/**
 * Celestial longitude, measured eastward along the celestial equator from the vernal equinox to the
 * hour circle of the point in question. This class is a simple newtype wrapper for an [[HourAngle]].
 * @see The helpful [[https://en.wikipedia.org/wiki/Right_ascension Wikipedia]] article.
 * @param toHourAngle the underlying hour angle
 */
final case class RightAscension(toHourAngle: HourAngle) {

  /**
   * Offset this `RightAscension` by the given hour angle.
   * @group Operations
   */
  def offset(ha: HourAngle): RightAscension =
    RightAscension(toHourAngle + ha)

  /**
   * Flip this `RightAscension` 180°, as might be required when offsetting coordinates causes the
   * associated declination to cross a pole.
   * @group Operations
   */
  def flip: RightAscension =
    RightAscension(toHourAngle.flip)

  /** Forget that this [[RightAscension]] wraps an [[HourAngle]]. */
  def toAngle: Angle =
    toHourAngle

  /** This RightAscension in radians [0 .. 2π). Approximate. */
  def toRadians: Double =
    toAngle.toDoubleRadians

  override def toString: String =
    RightAscension.fromStringHMS.taggedToString("RA", this)

}

object RightAscension extends RightAscensionOptics {

  /**
   * Construct a `RightAscension` from an angle in radians.
   * @group Constructors
   */
  def fromRadians(rad: Double): RightAscension =
    RightAscension(Angle.hourAngle.get(Angle.fromDoubleRadians(rad)))

  /**
   * Construct a `RightAscension` from an angle in degrees. Approximate
   * @group Constructors
   */
  def fromDoubleDegrees(deg: Double): RightAscension =
    fromHourAngle.get(HourAngle.fromDoubleDegrees(deg))

  /**
   * The `RightAscension` at zero degrees.
   * @group Constructors
   */
  val Zero: RightAscension =
    RightAscension(HourAngle.HourAngle0)

  /**
   * Unlike arbitrary angles, it is common to order right asensions starting at zero hours.
   * @group Typeclass Instances
   */
  given Order[RightAscension] =
    Order.by(_.toHourAngle.toMicroseconds)

  /* @group Typeclass Instances */
  given Show[RightAscension] =
    Show.fromToString

}

trait RightAscensionOptics { this: RightAscension.type =>

  val fromHourAngle: Iso[HourAngle, RightAscension] =
    Iso(RightAscension(_))(_.toHourAngle)

  val fromStringHMS: Format[String, RightAscension] =
    HourAngle.fromStringHMS.andThen(fromHourAngle)

  val fromAngleExact: Prism[Angle, RightAscension] =
    Angle.hourAngleExact.andThen(fromHourAngle)

  val lenientFromStringHMS: Format[String, RightAscension] =
    Format[String, RightAscension](
      AngleParsers.hms.parseAll(_).toOption.map(RightAscension.fromHourAngle.get),
      RightAscension.fromStringHMS.reverseGet
    )

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy