/*
  This file is part of CDO. CDO is a collection of Operators to
  manipulate and analyse Climate model Data.

  Copyright (C) 2003-2020 Uwe Schulzweida, <uwe.schulzweida AT mpimet.mpg.de>
  See COPYING file for copying and redistribution conditions.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_PROJ_H
#include "proj.h"
#endif

#include <stdio.h>
#include <stdarg.h> /* va_list */

#include <vector>

#include <cdi.h>
#include "cdo_options.h"
#include "grid_proj.h"
#include "cdo_output.h"
#include "compare.h"

static void
set_xyvals(const double val, const size_t nvals, double *xvals, double *yvals)
{
  for (size_t i = 0; i < nvals; i++)
    {
      xvals[i] = val;
      yvals[i] = val;
    }
}

static void
check_xyvals(const size_t nvals, double *xvals, double *yvals)
{
  for (size_t i = 0; i < nvals; i++)
    {
      if (xvals[i] < -9000. || xvals[i] > 9000.) xvals[i] = -9999.;
      if (yvals[i] < -9000. || yvals[i] > 9000.) yvals[i] = -9999.;
    }
}

#ifdef HAVE_LIBPROJ
static std::string
gen_param(const char *fmt, ...)
{
  va_list args;
  char str[256];

  va_start(args, fmt);

  vsprintf(str, fmt, args);

  va_end(args);

  std::string res(str);
  return res;
}

static void
proj_fwd_xyvals(PJ *proj, size_t nvals, double *xvals, double *yvals)
{
  PJ_COORD p;
  for (size_t i = 0; i < nvals; i++)
    {
      p.uv.u = proj_torad(xvals[i]);
      p.uv.v = proj_torad(yvals[i]);
      p = proj_trans(proj, PJ_FWD, p);
      xvals[i] = p.uv.u;
      yvals[i] = p.uv.v;
    }
}

static void
proj_inv_xyvals(PJ *proj, size_t nvals, double *xvals, double *yvals)
{
  PJ_COORD p;
  for (size_t i = 0; i < nvals; i++)
    {
      p.uv.u = xvals[i];
      p.uv.v = yvals[i];
      p = proj_trans(proj, PJ_INV, p);
      xvals[i] = proj_todeg(p.uv.u);
      yvals[i] = proj_todeg(p.uv.v);
    }
}

static int
do_proj_fwd(const char *params, size_t nvals, double *xvals, double *yvals)
{
  if (Options::cdoVerbose) cdoPrint("Proj fwd: %s", params);

  auto proj = proj_create(PJ_DEFAULT_CTX, params);
  const int status = proj_errno(proj);

  if (status == 0)
    {
      proj_fwd_xyvals(proj, nvals, xvals, yvals);
      proj_destroy(proj);
    }

  return status;
}

static int
do_proj_inv(const char *params, size_t nvals, double *xvals, double *yvals)
{
  if (Options::cdoVerbose) cdoPrint("Proj inv: %s", params);

  auto proj = proj_create(PJ_DEFAULT_CTX, params);
  const auto status = proj_errno(proj);
  if (status == 0)
    {
      proj_inv_xyvals(proj, nvals, xvals, yvals);
      proj_destroy(proj);
    }

  return status;
}

#endif

static inline bool
checkValIsMiss(const char *projection, const char *name, const double val, const double missval)
{
  if (IS_EQUAL(val, missval))
    {
      cdoWarning("%s mapping parameter %s missing!", projection, name);
      return true;
    }

  return false;
}

static inline void
checkRangeWarning(const char *projection, const char *name)
{
  cdoWarning("%s mapping parameter %s out of bounds!", projection, name);
}

static inline void
checkLonRange(const char *projection, const char *name, const double lon)
{
  if (lon < -360 || lon > 360) checkRangeWarning(projection, name);
}

static inline void
checkLatRange(const char *projection, const char *name, const double lat)
{
  if (lat < -90 || lat > 90) checkRangeWarning(projection, name);
}

static inline void
checkRange(const char *projection, const char *name, const double val, const double missval, const double rmin, const double rmax)
{
  if (IS_NOT_EQUAL(val, missval) && (val < rmin || val > rmax)) checkRangeWarning(projection, name);
}

static inline void
checkUpperRange(const char *projection, const char *name, const double val, const double missval, const double rmax)
{
  if (IS_NOT_EQUAL(val, missval) && (val > rmax)) checkRangeWarning(projection, name);
}

static void
verify_lcc_parameter(double lon_0, double lat_0, double lat_1, double lat_2, double a, double rf, double x_0, double y_0)
{
  const auto grid_missval = cdiInqGridMissval();
  const char *projection = "lambert_conformal_conic";

  checkUpperRange(projection, "earth_radius", a, grid_missval, 1.e10);
  checkUpperRange(projection, "inverse_flattening", rf, grid_missval, 400);

  checkLonRange(projection, "longitude_of_central_meridian", lon_0);

  checkLatRange(projection, "latitude_of_central_meridian", lat_0);
  checkLatRange(projection, "standard_parallel", lat_1);
  checkLatRange(projection, "standard_parallel", lat_2);

  checkRange(projection, "false_easting", x_0, grid_missval, -1.e20, 1.e20);
  checkRange(projection, "false_northing", y_0, grid_missval, -1.e20, 1.e20);
}

int
proj_lonlat_to_lcc(double missval, double lon_0, double lat_0, double lat_1, double lat_2, double a, double rf, size_t nvals,
                   double *xvals, double *yvals)
{
#ifdef HAVE_LIBPROJ
  std::string params = "";

  params += gen_param("+proj=lcc ");
  if (IS_NOT_EQUAL(a, missval) && a > 0) params += gen_param("+a=%g ", a);
  if (IS_NOT_EQUAL(rf, missval) && rf > 0) params += gen_param("+rf=%g ", rf);
  params += gen_param("+lon_0=%g ", lon_0);
  params += gen_param("+lat_0=%g ", lat_0);
  params += gen_param("+lat_1=%g ", lat_1);
  params += gen_param("+lat_2=%g ", lat_2);
  params += gen_param("+units=m ");
  //  params += gen_param("+no_defs ");

  const int status = do_proj_fwd(params.c_str(), nvals, xvals, yvals);
#else
  const int status = 1;
#endif

  if (status == 1) set_xyvals(missval, nvals, xvals, yvals);

  return status;
}

static void
lonlat_to_lcc(double missval, double lon_0, double lat_0, double lat_1, double lat_2, double a, double rf, size_t nvals,
              double *xvals, double *yvals)
{
  const int status = proj_lonlat_to_lcc(missval, lon_0, lat_0, lat_1, lat_2, a, rf, nvals, xvals, yvals);
#ifdef HAVE_LIBPROJ
  if (status) cdoAbort("proj library error: %s", proj_errno_string(status));
#else
  if (status) cdoAbort("proj library support not compiled in!");
#endif
}

int
cdo_lonlat_to_lcc(int gridID, size_t nvals, double *xvals, double *yvals)
{
  const auto grid_missval = cdiInqGridMissval();
  double lon_0, lat_0, lat_1, lat_2, a, rf, xval_0, yval_0, x_0, y_0;
  gridInqParamLCC(gridID, grid_missval, &lon_0, &lat_0, &lat_1, &lat_2, &a, &rf, &xval_0, &yval_0, &x_0, &y_0);

  lonlat_to_lcc(grid_missval, lon_0, lat_0, lat_1, lat_2, a, rf, nvals, xvals, yvals);

  return 0;
}

int
proj_lcc_to_lonlat(double missval, double lon_0, double lat_0, double lat_1, double lat_2, double a, double rf, double x_0,
                   double y_0, size_t nvals, double *xvals, double *yvals)
{
  const auto grid_missval = cdiInqGridMissval();
#ifdef HAVE_LIBPROJ
  std::string params = "";

  params += gen_param("+proj=lcc ");
  if (IS_NOT_EQUAL(a, grid_missval) && a > 0) params += gen_param("+a=%g ", a);
  if (IS_NOT_EQUAL(rf, grid_missval) && rf > 0) params += gen_param("+rf=%g ", rf);
  params += gen_param("+lon_0=%g ", lon_0);
  params += gen_param("+lat_0=%g ", lat_0);
  params += gen_param("+lat_1=%g ", lat_1);
  params += gen_param("+lat_2=%g ", lat_2);
  if (IS_NOT_EQUAL(x_0, grid_missval)) params += gen_param("+x_0=%g ", x_0);
  if (IS_NOT_EQUAL(y_0, grid_missval)) params += gen_param("+y_0=%g ", y_0);

  const int status = do_proj_inv(params.c_str(), nvals, xvals, yvals);
#else
  const int status = 1;
#endif

  if (status == 1) set_xyvals(missval, nvals, xvals, yvals);

  return status;
}

static void
lcc_to_lonlat(double missval, double lon_0, double lat_0, double lat_1, double lat_2, double a, double rf, double x_0, double y_0,
              size_t nvals, double *xvals, double *yvals)
{
  const int status = proj_lcc_to_lonlat(missval, lon_0, lat_0, lat_1, lat_2, a, rf, x_0, y_0, nvals, xvals, yvals);
#ifdef HAVE_LIBPROJ
  if (status) cdoAbort("proj library error: %s", proj_errno_string(status));
#else
  if (status == 1) cdoAbort("proj library support not compiled in!");
#endif
}

int
cdo_lcc_to_lonlat(int gridID, size_t nvals, double *xvals, double *yvals)
{
  const auto grid_missval = cdiInqGridMissval();
  const char *projection = "lambert_conformal_conic";

  double lon_0, lat_0, lat_1, lat_2, a, rf, xval_0, yval_0, x_0, y_0;
  gridInqParamLCC(gridID, grid_missval, &lon_0, &lat_0, &lat_1, &lat_2, &a, &rf, &xval_0, &yval_0, &x_0, &y_0);

  int status = 0;
  if (!status && checkValIsMiss(projection, "longitude_of_central_meridian", lon_0, grid_missval)) status = 1;
  if (!status && checkValIsMiss(projection, "latitude_of_central_meridian", lat_0, grid_missval)) status = 1;
  if (!status && checkValIsMiss(projection, "standard_parallel", lat_1, grid_missval)) status = 1;

  if (!status && IS_EQUAL(x_0, grid_missval) && IS_EQUAL(y_0, grid_missval) && IS_NOT_EQUAL(xval_0, grid_missval)
      && IS_NOT_EQUAL(yval_0, grid_missval))
    {
#ifdef HAVE_LIBPROJ
      x_0 = xval_0;
      y_0 = yval_0;
      lonlat_to_lcc(grid_missval, lon_0, lat_0, lat_1, lat_2, a, rf, 1, &x_0, &y_0);
      x_0 = -x_0;
      y_0 = -y_0;
#else
      status = 1;
      cdoWarning("%s mapping parameter %s missing!", projection, "false_easting and false_northing");
      cdoAbort("proj library support not compiled in!");
#endif
    }

  if (status) cdoAbort("%s mapping parameter missing!", projection);

  verify_lcc_parameter(lon_0, lat_0, lat_1, lat_2, a, rf, x_0, y_0);

  lcc_to_lonlat(grid_missval, lon_0, lat_0, lat_1, lat_2, a, rf, x_0, y_0, nvals, xvals, yvals);

  return 0;
}

static void
verify_stere_parameter(double lon_0, double lat_ts, double lat_0, double a, double x_0, double y_0)
{
  const auto grid_missval = cdiInqGridMissval();
  const char *const projection = "polar_stereographic";

  checkUpperRange(projection, "earth_radius", a, grid_missval, 1.e10);

  checkLonRange(projection, "straight_vertical_longitude_from_pole", lon_0);

  checkLatRange(projection, "latitude_of_projection_origin", lat_0);
  checkLatRange(projection, "standard_parallel", lat_ts);

  checkRange(projection, "false_easting", x_0, grid_missval, -1.e20, 1.e20);
  checkRange(projection, "false_northing", y_0, grid_missval, -1.e20, 1.e20);
}

int
proj_lonlat_to_stere(double missval, double lon_0, double lat_ts, double lat_0, double a, size_t nvals, double *xvals,
                     double *yvals)
{
#ifdef HAVE_LIBPROJ
  std::string params;

  params += gen_param("+proj=stere ");
  if (IS_NOT_EQUAL(a, missval) && a > 0) params += gen_param("+a=%g ", a);
  params += gen_param("+lon_0=%g ", lon_0);
  params += gen_param("+lat_ts=%g ", lat_ts);
  params += gen_param("+lat_0=%g ", lat_0);
  params += gen_param("+units=m ");
  //  params += gen_param("+no_defs ");

  const int status = do_proj_fwd(params.c_str(), nvals, xvals, yvals);
#else
  const int status = 1;
#endif

  if (status == 1) set_xyvals(missval, nvals, xvals, yvals);

  return status;
}

static void
lonlat_to_stere(double missval, double lon_0, double lat_ts, double lat_0, double a, size_t nvals, double *xvals, double *yvals)
{
  const int status = proj_lonlat_to_stere(missval, lon_0, lat_ts, lat_0, a, nvals, xvals, yvals);
#ifdef HAVE_LIBPROJ
  if (status) cdoAbort("proj library error: %s", proj_errno_string(status));
#else
  if (status == 1) cdoAbort("proj library support not compiled in!");
#endif
}

int
cdo_lonlat_to_stere(int gridID, size_t nvals, double *xvals, double *yvals)
{
  const auto grid_missval = cdiInqGridMissval();
  double lon_0, lat_ts, lat_0, a, xval_0, yval_0, x_0, y_0;
  gridInqParamSTERE(gridID, grid_missval, &lon_0, &lat_ts, &lat_0, &a, &xval_0, &yval_0, &x_0, &y_0);

  lonlat_to_stere(grid_missval, lon_0, lat_ts, lat_0, a, nvals, xvals, yvals);

  return 0;
}

int
proj_stere_to_lonlat(double missval, double lon_0, double lat_ts, double lat_0, double a, double x_0, double y_0, size_t nvals,
                     double *xvals, double *yvals)
{
#ifdef HAVE_LIBPROJ
  const auto grid_missval = cdiInqGridMissval();
  std::string params;

  params += gen_param("+proj=stere ");
  if (IS_NOT_EQUAL(a, grid_missval) && a > 0) params += gen_param("+a=%g ", a);
  params += gen_param("+lon_0=%g ", lon_0);
  params += gen_param("+lat_ts=%g ", lat_ts);
  params += gen_param("+lat_0=%g ", lat_0);
  if (IS_NOT_EQUAL(x_0, grid_missval)) params += gen_param("+x_0=%g ", x_0);
  if (IS_NOT_EQUAL(y_0, grid_missval)) params += gen_param("+y_0=%g ", y_0);

  const int status = do_proj_inv(params.c_str(), nvals, xvals, yvals);
#else
  const int status = 1;
#endif

  if (status == 1) set_xyvals(missval, nvals, xvals, yvals);

  return status;
}

static void
stere_to_lonlat(double missval, double lon_0, double lat_ts, double lat_0, double a, double x_0, double y_0, size_t nvals,
                double *xvals, double *yvals)
{
  const int status = proj_stere_to_lonlat(missval, lon_0, lat_ts, lat_0, a, x_0, y_0, nvals, xvals, yvals);
#ifdef HAVE_LIBPROJ
  if (status) cdoAbort("proj library error: %s", proj_errno_string(status));
#else
  if (status == 1) cdoAbort("proj library support not compiled in!");
#endif
}

int
cdo_stere_to_lonlat(int gridID, size_t nvals, double *xvals, double *yvals)
{
  const auto grid_missval = cdiInqGridMissval();
  const char *projection = "polar_stereographic";

  double lon_0, lat_ts, lat_0, a, xval_0, yval_0, x_0, y_0;
  gridInqParamSTERE(gridID, grid_missval, &lon_0, &lat_ts, &lat_0, &a, &xval_0, &yval_0, &x_0, &y_0);

  int status = 0;
  if (!status && checkValIsMiss(projection, "straight_vertical_longitude_from_pole", lon_0, grid_missval)) status = 1;
  if (!status && checkValIsMiss(projection, "latitude_of_projection_origin", lat_0, grid_missval)) status = 1;
  if (!status && checkValIsMiss(projection, "standard_parallel", lat_ts, grid_missval)) status = 1;

  if (!status && IS_EQUAL(x_0, grid_missval) && IS_EQUAL(y_0, grid_missval) && IS_NOT_EQUAL(xval_0, grid_missval)
      && IS_NOT_EQUAL(yval_0, grid_missval))
    {
#ifdef HAVE_LIBPROJ
      x_0 = xval_0;
      y_0 = yval_0;
      lonlat_to_stere(grid_missval, lon_0, lat_ts, lat_0, a, 1, &x_0, &y_0);
      x_0 = -x_0;
      y_0 = -y_0;
#else
      status = 1;
      cdoWarning("%s mapping parameter %s missing!", projection, "false_easting and false_northing");
      cdoAbort("proj library support not compiled in!");
#endif
    }

  if (status) cdoAbort("%s mapping parameter missing!", projection);

  verify_stere_parameter(lon_0, lat_ts, lat_0, a, x_0, y_0);

  stere_to_lonlat(grid_missval, lon_0, lat_ts, lat_0, a, x_0, y_0, nvals, xvals, yvals);

  return 0;
}

void
grid_def_param_sinu(int gridID)
{
  const char *const projection = "sinusoidal";
  cdiDefKeyString(gridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_NAME, projection);
  const char *const gmapvarname = "Sinusoidal";
  cdiDefKeyString(gridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_VARNAME, gmapvarname);

  cdiDefAttTxt(gridID, CDI_GLOBAL, "grid_mapping_name", (int) strlen(projection), projection);
}

void
grid_def_param_laea(int gridID, double a, double lon_0, double lat_0)
{
  const char *const projection = "lambert_azimuthal_equal_area";
  cdiDefKeyString(gridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_NAME, projection);
  const char *const gmapvarname = "Lambert_AEA";
  cdiDefKeyString(gridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_VARNAME, gmapvarname);

  cdiDefAttTxt(gridID, CDI_GLOBAL, "grid_mapping_name", (int) strlen(projection), projection);

  cdiDefAttFlt(gridID, CDI_GLOBAL, "earth_radius", CDI_DATATYPE_FLT64, 1, &a);
  cdiDefAttFlt(gridID, CDI_GLOBAL, "longitude_of_projection_origin", CDI_DATATYPE_FLT64, 1, &lon_0);
  cdiDefAttFlt(gridID, CDI_GLOBAL, "latitude_of_projection_origin", CDI_DATATYPE_FLT64, 1, &lat_0);
}

void
cdo_sinu_to_lonlat(size_t nvals, double *xvals, double *yvals)
{
#ifdef HAVE_LIBPROJ
  std::string params;

  params += gen_param("+proj=sinu ");
  params += gen_param("+ellps=WGS84 ");

  const int status = do_proj_inv(params.c_str(), nvals, xvals, yvals);
  if (status) cdoAbort("proj library error: %s", proj_errno_string(status));

  check_xyvals(nvals, xvals, yvals);
#else
  cdoAbort("proj library support not compiled in!");
#endif
}

#ifdef HAVE_LIBPROJ
static bool
cdiInqAttConvertedToFloat(int gridID, int atttype, const char *attname, int attlen, double *attflt)
{
  bool status = true;

  if (atttype == CDI_DATATYPE_INT32)
    {
      std::vector<int> attint(attlen);
      cdiInqAttInt(gridID, CDI_GLOBAL, attname, attlen, &attint[0]);
      for (int i = 0; i < attlen; ++i) attflt[i] = (double) attint[i];
    }
  else if (atttype == CDI_DATATYPE_FLT32 || atttype == CDI_DATATYPE_FLT64)
    {
      cdiInqAttFlt(gridID, CDI_GLOBAL, attname, attlen, attflt);
    }
  else
    {
      status = false;
    }

  return status;
}

static void
grid_inq_param_laea(int gridID, double *a, double *lon_0, double *lat_0, double *x_0, double *y_0)
{
  *a = 0;
  *lon_0 = 0;
  *lat_0 = 0, *x_0 = 0, *y_0 = 0;

  const auto gridtype = gridInqType(gridID);
  if (gridtype == GRID_PROJECTION)
    {
      const char *projection = "lambert_azimuthal_equal_area";
      char gmapname[CDI_MAX_NAME];
      int length = CDI_MAX_NAME;
      cdiInqKeyString(gridID, CDI_GLOBAL, CDI_KEY_GRIDMAP_NAME, gmapname, &length);
      if (gmapname[0] && cstrIsEqual(gmapname, projection))
        {
          char attname[CDI_MAX_NAME + 1];

          int natts;
          cdiInqNatts(gridID, CDI_GLOBAL, &natts);

          for (int iatt = 0; iatt < natts; ++iatt)
            {
              int atttype, attlen;
              cdiInqAtt(gridID, CDI_GLOBAL, iatt, attname, &atttype, &attlen);
              if (attlen != 1) continue;

              double attflt;
              if (cdiInqAttConvertedToFloat(gridID, atttype, attname, attlen, &attflt))
                {
                  // clang-format off
                  if      (cstrIsEqual(attname, "earth_radius")) *a = attflt;
                  else if (cstrIsEqual(attname, "longitude_of_projection_origin")) *lon_0 = attflt;
                  else if (cstrIsEqual(attname, "latitude_of_projection_origin")) *lat_0 = attflt;
                  else if (cstrIsEqual(attname, "false_easting")) *x_0 = attflt;
                  else if (cstrIsEqual(attname, "false_northing")) *y_0 = attflt;
                  // clang-format on
                }
            }
        }
      else
        cdoWarning("%s mapping parameter missing!", projection);
    }
}
#endif

void
cdo_laea_to_lonlat(int gridID, size_t nvals, double *xvals, double *yvals)
{
#ifdef HAVE_LIBPROJ
  std::string params;

  double a, lon_0, lat_0, x_0, y_0;
  grid_inq_param_laea(gridID, &a, &lon_0, &lat_0, &x_0, &y_0);

  params += gen_param("+proj=laea ");
  if (a > 0) params += gen_param("+a=%g ", a);
  params += gen_param("+lon_0=%g ", lon_0);
  params += gen_param("+lat_0=%g ", lat_0);
  if (IS_NOT_EQUAL(x_0, 0)) params += gen_param("+x_0=%g ", x_0);
  if (IS_NOT_EQUAL(y_0, 0)) params += gen_param("+y_0=%g ", y_0);

  const int status = do_proj_inv(params.c_str(), nvals, xvals, yvals);
  if (status) cdoAbort("proj library error: %s", proj_errno_string(status));

  check_xyvals(nvals, xvals, yvals);
#else
  cdoAbort("proj library support not compiled in!");
#endif
}

void
cdo_proj_to_lonlat(char *proj_params, size_t nvals, double *xvals, double *yvals)
{
#ifdef HAVE_LIBPROJ
  const int status = do_proj_inv(proj_params, nvals, xvals, yvals);
  if (status) cdoAbort("proj library error: %s", proj_errno_string(status));

  check_xyvals(nvals, xvals, yvals);
#else
  cdoAbort("proj library support not compiled in!");
#endif
}

double
gridGetPlanetRadius(int gridID)
{
  double planetRadius = 0;

  const auto gridtype = gridInqType(gridID);
  const auto projtype = (gridtype == GRID_PROJECTION) ? gridInqProjType(gridID) : -1;
  if (projtype == CDI_PROJ_LCC)
    {
      const auto grid_missval = cdiInqGridMissval();
      double lon_0, lat_0, lat_1, lat_2, a, rf, xval_0, yval_0, x_0, y_0;
      gridInqParamLCC(gridID, grid_missval, &lon_0, &lat_0, &lat_1, &lat_2, &a, &rf, &xval_0, &yval_0, &x_0, &y_0);
      planetRadius = a;
    }
  else if (projtype == CDI_PROJ_STERE)
    {
      const auto grid_missval = cdiInqGridMissval();
      double lon_0, lat_ts, lat_0, a, xval_0, yval_0, x_0, y_0;
      gridInqParamSTERE(gridID, grid_missval, &lon_0, &lat_ts, &lat_0, &a, &xval_0, &yval_0, &x_0, &y_0);
      planetRadius = a;
    }

  return planetRadius;
}
