// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2006 Mathias Froehlich <mathias.froehlich@web.de>

// SGGeoc 头文件
// 定义了地心坐标类SGGeoc，用于表示地球上的位置 lon为经度, lat为纬度, radius为距离地球中心的距离

#ifndef SGGeoc_H
#define SGGeoc_H

#include <simgear/constants.h>	// 包含常量定义的头文件
 
// #define SG_GEOC_NATIVE_DEGREE

/// Class representing a geocentric location
class SGGeoc {
public:
  /// Default constructor, initializes the instance to lat = lon = lat = 0
  // 默认构造函数，将实例初始化为 lat = lon = lat = 0
  SGGeoc(void);

  /// Factory from angular values in radians and radius in ft
  // 工厂方法：从弧度值和半径（单位：英尺）创建实例
  static SGGeoc fromRadFt(double lon, double lat, double radius);
  /// Factory from angular values in degrees and radius in ft
  // 工厂方法：从角度值和半径（单位：英尺）创建实例
  static SGGeoc fromDegFt(double lon, double lat, double radius);
  /// Factory from angular values in radians and radius in m
  // 工厂方法：从弧度值和半径（单位：米）创建实例
  static SGGeoc fromRadM(double lon, double lat, double radius);
  /// Factory from angular values in degrees and radius in m
  // 工厂方法：从角度值和半径（单位：米）创建实例
  static SGGeoc fromDegM(double lon, double lat, double radius);
  /// Factory to convert position from a cartesian position assumed to be
  /// in wgs84 measured in meters
  /// Note that this conversion is relatively expensive to compute
  // 工厂方法：从ECEF坐标（单位：米）创建实例, 使用WGS84坐标系, 注意这个转换耗时较长
  static SGGeoc fromCart(const SGVec3<double>& cart);
  /// Factory to convert position from a geodetic position
  /// Note that this conversion is relatively expensive to compute
  // 工厂方法：从地理坐标创建实例, 注意这个转换耗时较长
  static SGGeoc fromGeod(const SGGeod& geod);

  /// Return the geocentric longitude in radians
  // 返回经度（单位：弧度）
  double getLongitudeRad(void) const;
  /// Set the geocentric longitude from the argument given in radians
  // 设置经度（单位：弧度）
  void setLongitudeRad(double lon);

  /// Return the geocentric longitude in degrees
  // 返回经度（单位：度）
  double getLongitudeDeg(void) const;
  /// Set the geocentric longitude from the argument given in degrees
  // 设置经度（单位：度）
  void setLongitudeDeg(double lon);

  /// Return the geocentric latitude in radians
  // 返回纬度（单位：弧度）
  double getLatitudeRad(void) const;
  /// Set the geocentric latitude from the argument given in radians
  // 设置纬度（单位：弧度）
  void setLatitudeRad(double lat);

  /// Return the geocentric latitude in degrees
  // 返回纬度（单位：度）
  double getLatitudeDeg(void) const;
  /// Set the geocentric latitude from the argument given in degrees
  // 设置纬度（单位：度）
  void setLatitudeDeg(double lat);

  /// Return the geocentric radius in meters
  // 返回距地心距离（单位：米）
  double getRadiusM(void) const;
  /// Set the geocentric radius from the argument given in meters
  // 设置距地心距离（单位：米）
  void setRadiusM(double radius);

  /// Return the geocentric radius in feet
  // 返回距地心距离（单位：英尺）
  double getRadiusFt(void) const;
  /// Set the geocentric radius from the argument given in feet
  // 设置距地心距离（单位：英尺）
  void setRadiusFt(double radius);

  /// 通过航向和距离计算一个新的地心坐标（单位：弧度），
  /// 航向是相对于当前坐标的航向，距离是相对于当前坐标的距离
  SGGeoc advanceRadM(double course, double distance) const;
  /// 返回两个地心坐标之间的航向（单位：弧度）
  static double courseRad(const SGGeoc& from, const SGGeoc& to);
  /// 返回两个地心坐标之间的航向（单位：度）
  static double courseDeg(const SGGeoc& from, const SGGeoc& to);
  /// 返回两个地心坐标之间的距离（单位：米）
  static double distanceM(const SGGeoc& from, const SGGeoc& to);

  // Compare two geocentric positions for equality
  // 比较两个地心坐标是否相等
  bool operator == ( const SGGeoc & other ) const;
private:
  /// This one is private since construction is not unique if you do
  /// not know the units of the arguments, use the factory methods for
  /// that purpose
  // 私有构造函数，因为构造函数不是唯一的，如果不知道参数的单位，请使用工厂方法
  SGGeoc(double lon, double lat, double radius);

  /// The actual data, angles in degree, radius in meters
  /// The rationale for storing the values in degrees is that most code places
  /// in flightgear/terragear use degrees as a nativ input and output value.
  /// The places where it makes sense to use radians is when we convert
  /// to other representations or compute rotation matrices. But both tasks
  /// are computionally intensive anyway and that additional 'toRadian'
  /// conversion does not hurt too much
  /// 实际数据：角度以度为单位，半径以米为单位
  /// 选择以度为单位存储值的原因是，flightgear/terragear中的大多数代码
  /// 都使用度作为原生的输入和输出值。
  /// 使用弧度更有意义的情况是在我们进行坐标转换
  /// 或计算旋转矩阵时。但这两项任务本身计算量就很大，
  /// 所以额外的"转换为弧度"操作并不会造成太大影响
  double _lon; // 经度
  double _lat; // 纬度
  double _radius; // 距地心距离
};

inline
SGGeoc::SGGeoc(void) :
  _lon(0), _lat(0), _radius(0)
{
}

inline
SGGeoc::SGGeoc(double lon, double lat, double radius) :
  _lon(lon), _lat(lat), _radius(radius)
{
}

inline
SGGeoc
SGGeoc::fromRadFt(double lon, double lat, double radius)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return SGGeoc(lon*SGD_RADIANS_TO_DEGREES, lat*SGD_RADIANS_TO_DEGREES,
                radius*SG_FEET_TO_METER);
#else
  return SGGeoc(lon, lat, radius*SG_FEET_TO_METER);
#endif
}

inline
SGGeoc
SGGeoc::fromDegFt(double lon, double lat, double radius)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return SGGeoc(lon, lat, radius*SG_FEET_TO_METER);
#else
  return SGGeoc(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS,
                radius*SG_FEET_TO_METER);
#endif
}

inline
SGGeoc
SGGeoc::fromRadM(double lon, double lat, double radius)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return SGGeoc(lon*SGD_RADIANS_TO_DEGREES, lat*SGD_RADIANS_TO_DEGREES,
                radius);
#else
  return SGGeoc(lon, lat, radius);
#endif
}

inline
SGGeoc
SGGeoc::fromDegM(double lon, double lat, double radius)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return SGGeoc(lon, lat, radius);
#else
  return SGGeoc(lon*SGD_DEGREES_TO_RADIANS, lat*SGD_DEGREES_TO_RADIANS,
                radius);
#endif
}

inline
SGGeoc
SGGeoc::fromCart(const SGVec3<double>& cart)
{
  SGGeoc geoc;
  SGGeodesy::SGCartToGeoc(cart, geoc);
  return geoc;
}

inline
SGGeoc
SGGeoc::fromGeod(const SGGeod& geod)
{
  SGVec3<double> cart;
  SGGeodesy::SGGeodToCart(geod, cart);
  SGGeoc geoc;
  SGGeodesy::SGCartToGeoc(cart, geoc);
  return geoc;
}

inline
double
SGGeoc::getLongitudeRad(void) const
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return _lon*SGD_DEGREES_TO_RADIANS;
#else
  return _lon;
#endif
}

inline
void
SGGeoc::setLongitudeRad(double lon)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  _lon = lon*SGD_RADIANS_TO_DEGREES;
#else
  _lon = lon;
#endif
}

inline
double
SGGeoc::getLongitudeDeg(void) const
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return _lon;
#else
  return _lon*SGD_RADIANS_TO_DEGREES;
#endif
}

inline
void
SGGeoc::setLongitudeDeg(double lon)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  _lon = lon;
#else
  _lon = lon*SGD_DEGREES_TO_RADIANS;
#endif
}

inline
double
SGGeoc::getLatitudeRad(void) const
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return _lat*SGD_DEGREES_TO_RADIANS;
#else
  return _lat;
#endif
}

inline
void
SGGeoc::setLatitudeRad(double lat)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  _lat = lat*SGD_RADIANS_TO_DEGREES;
#else
  _lat = lat;
#endif
}

inline
double
SGGeoc::getLatitudeDeg(void) const
{
#ifdef SG_GEOC_NATIVE_DEGREE
  return _lat;
#else
  return _lat*SGD_RADIANS_TO_DEGREES;
#endif
}

inline
void
SGGeoc::setLatitudeDeg(double lat)
{
#ifdef SG_GEOC_NATIVE_DEGREE
  _lat = lat;
#else
  _lat = lat*SGD_DEGREES_TO_RADIANS;
#endif
}

inline
double
SGGeoc::getRadiusM(void) const
{
  return _radius;
}

inline
void
SGGeoc::setRadiusM(double radius)
{
  _radius = radius;
}

inline
double
SGGeoc::getRadiusFt(void) const
{
  return _radius*SG_METER_TO_FEET;
}

inline
void
SGGeoc::setRadiusFt(double radius)
{
  _radius = radius*SG_FEET_TO_METER;
}

inline
SGGeoc
SGGeoc::advanceRadM(double course, double distance) const
{
  SGGeoc result;
  SGGeodesy::advanceRadM(*this, course, distance, result);
  return result;
}

inline
double
SGGeoc::courseRad(const SGGeoc& from, const SGGeoc& to)
{
  return SGGeodesy::courseRad(from, to);
}

inline
double
SGGeoc::courseDeg(const SGGeoc& from, const SGGeoc& to)
{
  return SGMiscd::rad2deg(courseRad(from, to));
}

inline
double
SGGeoc::distanceM(const SGGeoc& from, const SGGeoc& to)
{
  return SGGeodesy::distanceM(from, to);
}

inline
bool
SGGeoc::operator == ( const SGGeoc & other ) const
{
  return _lon == other._lon &&
         _lat == other._lat &&
         _radius == other._radius;
}

/// Output to an ostream
// 自定义的输出流操作符，用于将SGGeoc对象输出到流中
template<typename char_type, typename traits_type>
inline
std::basic_ostream<char_type, traits_type>&
operator<<(std::basic_ostream<char_type, traits_type>& s, const SGGeoc& g)
{
  return s << "lon = " << g.getLongitudeDeg()
           << ", lat = " << g.getLatitudeDeg()
           << ", radius = " << g.getRadiusM();
}

#endif
