예제 #1
0
  /**
   * This method is used to set the latitude/longitude (assumed to be of the
   * correct LatitudeType, LongitudeDirection, and LongitudeDomain. The Set
   * forces an attempted calculation of the projection X/Y values. This may or
   * may not be successful and a status is returned as such.
   *
   * @param lat Latitude value to project
   *
   * @param lon Longitude value to project
   *
   * @return bool
   */
  bool PointPerspective::SetGround(const double lat, const double lon) {
    // Convert longitude to radians & clean up
    m_longitude = lon;
    double lonRadians = lon * PI / 180.0;
    if (m_longitudeDirection == PositiveWest) lonRadians *= -1.0;

    // Now convert latitude to radians & clean up ... it must be planetographic
    m_latitude = lat;
    double latRadians = lat;
    if (IsPlanetocentric()) latRadians = ToPlanetographic(latRadians);
    latRadians *= PI / 180.0;

    // Compute helper variables
    double deltaLon = (lonRadians - m_centerLongitude);
    double sinphi = sin(latRadians);
    double cosphi = cos(latRadians);
    double coslon = cos(deltaLon);

    // Lat/Lon cannot be projected
    double g =  m_sinph0 * sinphi + m_cosph0 * cosphi * coslon;
    if (g < (1.0 / m_P)) {
      m_good = false;
      return m_good;
    }

    // Compute the coordinates
    double ksp = (m_P - 1.0) / (m_P - g);
    double x = m_equatorialRadius * ksp * cosphi * sin(deltaLon);
    double y = m_equatorialRadius * ksp *
               (m_cosph0 * sinphi - m_sinph0 * cosphi * coslon);
    SetComputedXY(x, y);
    m_good = true;
    return m_good;
  }
예제 #2
0
  /**
  * This method is used to set the projection x/y. The Set forces an attempted
  * calculation of the corresponding latitude/longitude position. This may or
  * may not be successful and a status is returned as such.
  *
  * @param x X coordinate of the projection in units that are the same as the
  *          radii in the label
  *
  * @param y Y coordinate of the projection in units that are the same as the
  *          radii in the label
  *
  * @return bool
  */
  bool LunarAzimuthalEqualArea::SetCoordinate(const double x,
      const double y) {
    // Save the coordinate
    SetXY(x, y);

    double RP = sqrt((x * x) + (y * y));

    double lat, lon;
    if (y == 0.0 && x == 0.0) {
      lat = 0.0;
      lon = 0.0;
      return true;
    }

    double radius = m_equatorialRadius;

    double D = atan2(y, x);
    double test = RP / radius;
    if (abs(test) > 1.0) {
      return false;
    }

    double EPSILON = 0.0000000001;
    double PFAC = (HALFPI + m_maxLibration) / HALFPI;
    double E = PFAC * asin(RP / radius);

    lat = HALFPI - (acos(sin(D) * sin(E)));

    if (abs(HALFPI - abs(lat)) <= EPSILON) {
      lon = 0.0;
    }
    else {
      test = sin(E) * cos(D) / sin(HALFPI - lat);
      if (test > 1.0) test = 1.0;
      else if (test < -1.0) test = -1.0;

      lon = asin(test);
    }

    if (E >= HALFPI) {
      if (lon <= 0.0) lon = -PI - lon;
      else lon = PI - lon;
    }

    // Convert to degrees
    m_latitude = lat * 180.0 / Isis::PI;
    m_longitude = lon * 180.0 / Isis::PI;

    // Cleanup the latitude
    if (IsPlanetocentric())
      m_latitude = ToPlanetocentric(m_latitude);

    m_good = true;
    return m_good;
  }
예제 #3
0
  /**
   * Constructs an PointPerspective object
   *
   * @param label This argument must be a Label containing the proper mapping
   *              information as indicated in the Projection class. Additionally,
   *              the point perspective projection requires the center longitude
   *              to be defined in the keyword CenterLongitude.
   *
   * @param allowDefaults If set to false the constructor expects that a keyword
   *                      of CenterLongitude will be in the label. Otherwise it
   *                      will attempt to compute the center longitude using the
   *                      middle of the longitude range specified in the labels.
   *                      Defaults to false.
   *
   * @throws IException::Io
   */
  PointPerspective::PointPerspective(Pvl &label, bool allowDefaults) :
    TProjection::TProjection(label) {
    try {
      // Try to read the mapping group
      PvlGroup &mapGroup = label.findGroup("Mapping", Pvl::Traverse);

      // Compute and write the default center longitude if allowed and
      // necessary
      if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
        double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
        mapGroup += PvlKeyword("CenterLongitude", toString(lon));
      }

      // Compute and write the default center latitude if allowed and
      // necessary
      if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
        double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
        mapGroup += PvlKeyword("CenterLatitude", toString(lat));
      }

      // Get the center longitude  & latitude
      m_centerLongitude = mapGroup["CenterLongitude"];
      m_centerLatitude = mapGroup["CenterLatitude"];
      if (IsPlanetocentric()) {
        m_centerLatitude = ToPlanetographic(m_centerLatitude);
      }

      // convert to radians, adjust for longitude direction
      m_centerLongitude *= PI / 180.0;
      m_centerLatitude *= PI / 180.0;
      if (m_longitudeDirection == PositiveWest) m_centerLongitude *= -1.0;

      // Calculate sine & cosine of center latitude
      m_sinph0 = sin(m_centerLatitude);
      m_cosph0 = cos(m_centerLatitude);

      // Get the distance above planet center (the point of perspective from
      // the center of planet), and calculate P
      m_distance = mapGroup["Distance"];
      m_distance *= 1000.;
      m_P = 1.0 + (m_distance / m_equatorialRadius);

    }
    catch(IException &e) {
      QString message = "Invalid label group [Mapping]";
      throw IException(e, IException::Io, message, _FILEINFO_);
    }
  }
예제 #4
0
  /**
  * This method is used to set the latitude/longitude (assumed to be of the
  * correct LatitudeType, LongitudeDirection, and LongitudeDomain. The Set
  * forces an attempted calculation of the projection X/Y values. This may or
  * may not be successful and a status is returned as such.
  *
  * @param lat Latitude value to project
  *
  * @param lon Longitude value to project
  *
  * @return bool
  */
  bool LunarAzimuthalEqualArea::SetGround(const double lat,
                                          const double lon) {
    // Convert longitude to radians
    m_longitude = lon;
    double lonRadians = lon * Isis::PI / 180.0;
    if (m_longitudeDirection == PositiveWest)
      lonRadians *= -1.0;

    // Now convert latitude to radians... it must be planetographic
    m_latitude = lat;
    double latRadians = lat;
    if (IsPlanetocentric())
      latRadians = ToPlanetographic(latRadians);
    latRadians *= Isis::PI / 180.0;

    double x, y;
    if (lonRadians == 0.0 && latRadians == 0.0) {
      x = 0.0;
      y = 0.0;
      SetComputedXY(x, y);
      m_good = true;
      return true;
    }

    double E = acos(cos(latRadians) * cos(lonRadians));
    double test = (sin(lonRadians) * cos(latRadians)) / sin(E);

    if (test > 1.0) test = 1.0;
    else if (test < -1.0) test = -1.0;

    double D = HALFPI - asin(test);
    if (latRadians < 0.0)
      D = -D;

    double radius = m_equatorialRadius;
    double PFAC = (HALFPI + m_maxLibration) / HALFPI;
    double RP = radius * sin(E / PFAC);

    x = RP * cos(D);
    y = RP * sin(D);

    SetComputedXY(x, y);
    m_good = true;
    return true;

  } // of SetGround
예제 #5
0
 /**
  * This method is used to set the projection x/y. The Set forces an attempted
  * calculation of the corresponding latitude/longitude position. This may or
  * may not be successful and a status is returned as such.
  *
  * @param x X coordinate of the projection in units that are the same as the
  *          radii in the label
  *
  * @param y Y coordinate of the projection in units that are the same as the
  *          radii in the label
  *
  * @return bool
  */
  bool TransverseMercator::SetCoordinate(const double x, const double y) {
    // Save the coordinate
    SetXY(x,y);

    // Declare & Initialize variables
    double f,g,h,temp,con,phi,dphi,sinphi,cosphi,tanphi;
    double c,cs,t,ts,n,rp,d,ds;
    const double epsilon = 1.0e-10;

    // Sphere Conversion
    if (p_sph) {
      f = exp(GetX() / (p_equatorialRadius * p_scalefactor));
      g = 0.5 * (f - 1.0 / f);
      temp = p_centerLatitude + GetY() / (p_equatorialRadius * p_scalefactor);
      h = cos(temp);
      con = sqrt((1.0 - h * h) / (1.0 + g * g));
      if (con > 1.0) con = 1.0;
      if (con < -1.0) con = -1.0;
      p_latitude = asin(con);
      if (temp < 0.0 ) p_latitude = -p_latitude;
      p_longitude = p_centerLongitude;
      if (g != 0.0 || h != 0.0) {
        p_longitude = atan2(g,h) + p_centerLongitude;
      }
    }

    // Ellipsoid Conversion
    else if (!p_sph) {
      con = (p_ml0 + GetY() / p_scalefactor) / p_equatorialRadius;
      phi = con;
      for (int i = 1; i < 7; i++) {
        dphi = ((con + p_e1 * sin(2.0 * phi) - p_e2 * sin(4.0 * phi)
                 + p_e3 * sin(6.0 * phi)) / p_e0) - phi;
        phi += dphi;
        if (fabs(dphi) <= epsilon) break;
      }

      // Didn't converge
      if (fabs(dphi) > epsilon) {
        p_good = false;
        return p_good;
      }
      if (fabs(phi) >= Isis::HALFPI) {
        if (GetY() >= 0.0) p_latitude = fabs(Isis::HALFPI);
        if (GetY() < 0.0) p_latitude = - fabs(Isis::HALFPI);
        p_longitude = p_centerLongitude;
      }
      else {
        sinphi = sin(phi);
        cosphi = cos(phi);
        tanphi = tan(phi);
        c = p_esp * cosphi * cosphi;
        cs = c * c;
        t = tanphi * tanphi;
        ts = t * t;
        con = 1.0 - p_eccsq * sinphi * sinphi;
        n = p_equatorialRadius / sqrt(con);
        rp = n * (1.0 - p_eccsq) / con;
        d = GetX() / (n * p_scalefactor);
        ds = d * d;
        p_latitude = phi - (n * tanphi * ds / rp) * (0.5 - ds /
                      24.0 * (5.0 + 3.0 * t + 10.0 * c - 4.0 * cs - 9.0 *
                      p_esp - ds / 30.0 * (61.0 + 90.0 * t + 298.0 * c +
                      45.0 * ts - 252.0 * p_esp - 3.0 * cs)));


        // Latitude cannot be greater than + or - halfpi radians (or 90 degrees)
        if (fabs(p_latitude) > Isis::HALFPI) {
          p_good = false;
          return p_good;
        }
        p_longitude = p_centerLongitude + (d * (1.0 - ds / 6.0 *
                      (1.0 + 2.0 * t + c - ds / 20.0 * (5.0 - 2.0 * c +
                      28.0 * t - 3.0 * cs + 8.0 * p_esp + 24.0 * ts))) / cosphi);
      }
    }

    // Convert to Degrees
    p_latitude *= 180.0 / Isis::PI;
    p_longitude *= 180.0 / Isis::PI;

    // Cleanup the longitude
    if (p_longitudeDirection == PositiveWest) p_longitude *= -1.0;
  // These need to be done for circular type projections
    p_longitude = To360Domain (p_longitude);
    if (p_longitudeDomain == 180) p_longitude = To180Domain(p_longitude);

    // Cleanup the latitude
    if (IsPlanetocentric()) p_latitude = ToPlanetocentric(p_latitude);

    p_good = true;
    return p_good;
  }
예제 #6
0
 /**
  * This method is used to set the latitude/longitude (assumed to be of the
  * correct LatitudeType, LongitudeDirection, and LongitudeDomain. The Set
  * forces an attempted calculation of the projection X/Y values. This may or
  * may not be successful and a status is returned as such.
  *
  * @param lat Latitude value to project
  *
  * @param lon Longitude value to project
  *
  * @return bool
  */
  bool TransverseMercator::SetGround(const double lat,const double lon) {
    // Get longitude & fix direction
    p_longitude = lon;
    if (p_longitudeDirection == PositiveWest) p_longitude *= -1.0;

    double cLonDeg = p_centerLongitude * 180.0 / Isis::PI;
    double deltaLon = p_longitude - cLonDeg;
    while (deltaLon < -360.0) deltaLon += 360.0;
    while (deltaLon > 360.0) deltaLon -= 360.0;
    double deltaLonRads = deltaLon * Isis::PI / 180.0;

    // Now convert latitude to radians & clean up ... it must be planetographic
    p_latitude = lat;
    double latRadians = p_latitude * Isis::PI / 180.0;
    if (IsPlanetocentric()) {
      latRadians = ToPlanetographic(p_latitude) * Isis::PI / 180.0;
    }

    double ml = p_equatorialRadius * (p_e0 * latRadians - p_e1 * sin(2.0 * latRadians)
                 + p_e2 * sin(4.0 * latRadians) - p_e3 * sin(6.0 * latRadians));

    // Declare variables
    const double epsilon = 1.0e-10;

    // Sphere Conversion
    double x,y;
    if (p_sph) {
       double cosphi = cos(latRadians);
       double b = cosphi * sin(deltaLonRads);

       // Point projects to infinity
       if (fabs(fabs(b) - 1.0) <= epsilon) {
         p_good = false;
         return p_good;
       }
       x = 0.5 * p_equatorialRadius * p_scalefactor * log((1.0 + b) / (1.0 - b));

       // If arcosine argument is too close to 1, con=0.0 because arcosine(1)=0
       double con = cosphi * cos(deltaLonRads) / sqrt(1.0 - b * b);
       if (fabs(con) > 1.0) {
         con = 0.0;
       }
       else {
         con = acos(con);
       }
       if(p_latitude < 0.0) con = -con;
       y = p_equatorialRadius * p_scalefactor * (con - p_centerLatitude);
    }

    // Ellipsoid Conversion
    else {
      if(fabs(Isis::HALFPI - fabs(latRadians)) < epsilon) {
        x = 0.0;
        y = p_scalefactor * (ml - p_ml0);
      }
      else {
        double sinphi = sin(latRadians);
        double cosphi = cos(latRadians);
        double al = cosphi * deltaLonRads;
        double als = al * al;
        double c = p_esp * cosphi * cosphi;
        double tq = tan(latRadians);
        double t = tq * tq;
        double n = p_equatorialRadius / sqrt (1.0 - p_eccsq * sinphi * sinphi);
        x = p_scalefactor * n * al * (1.0 + als / 6.0 * (1.0 - t + c + als
                  / 20.0 * (5.0 - 18.0 * t + t * t + 72.0 * c - 58.0 * p_esp)));
        y = p_scalefactor *(ml - p_ml0 + n * tq * (als * (0.5 + als / 24.0 *
                  (5.0 - t + 9.0 * c + 4.0 * c * c + als / 30.0 *
                  (61.0 - 58.0 * t + t * t + 600.0 * c - 330.0 * p_esp)))));
      }
    }

    SetComputedXY(x,y);
    p_good = true;
    return p_good;
  }
예제 #7
0
  /**
   * Constructs a TransverseMercator object
   *
   * @param label This argument must be a Label containing the proper mapping
   *              information as indicated in the Projection class. Additionally,
   *              the transversemercator projection requires the center longitude
   *              to be defined in the keyword CenterLongitude, and the scale
   *              factor to be defined in the keyword ScaleFactor.
   *
   * @param allowDefaults If set to false the constructor expects that a keyword
   *                      of CenterLongitude and ScaleFactor will be in the label.
   *                      Otherwise it will attempt to compute the center
   *                      longitude using the middle of the longitude range
   *                      specified in the label, and the scale factor will
   *                      default to 1.0. Defaults to false.
   *
   * @throws IException
   */
  TransverseMercator::TransverseMercator(Pvl &label, bool allowDefaults) :
      TProjection::TProjection(label) {
    try {
      // Try to read the mapping group
      PvlGroup &mapGroup = label.findGroup("Mapping", Pvl::Traverse);

      // Compute and write the default center longitude if allowed and
      // necessary
      if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLongitude"))) {
        double lon = (m_minimumLongitude + m_maximumLongitude) / 2.0;
        mapGroup += PvlKeyword("CenterLongitude", toString(lon));
      }

      // Compute and write the default center latitude if allowed and
      // necessary
      if ((allowDefaults) && (!mapGroup.hasKeyword("CenterLatitude"))) {
        double lat = (m_minimumLatitude + m_maximumLatitude) / 2.0;
        mapGroup += PvlKeyword("CenterLatitude", toString(lat));
      }

      // Get the center longitude  & latitude
      m_centerLongitude = mapGroup["CenterLongitude"];
      m_centerLatitude = mapGroup["CenterLatitude"];

      // make sure the center latitude value is valid
      if (fabs(m_centerLatitude) >= 90.0) {
        string msg = "Invalid Center Latitude Value. Must be between -90 and 90";
        throw IException(IException::Io, msg, _FILEINFO_);
      }

      // make sure the center longitude value is valid
      if (fabs(m_centerLongitude) > 360.0) {
        string msg = "Invalid Center Longitude Value. Must be between -360 and 360";
        throw IException(IException::Io, msg, _FILEINFO_);
      }

      // convert latitude to planetographic if it is planetocentric
      if (IsPlanetocentric()) {
        m_centerLatitude = ToPlanetographic(m_centerLatitude);
      }

      // convert to radians and adjust for longitude direction
      if (m_longitudeDirection == PositiveWest) m_centerLongitude *= -1.0;
      m_centerLatitude *= PI / 180.0;
      m_centerLongitude *= PI / 180.0;

      // Compute other necessary variables. See Snyder, page 61
      m_eccsq = Eccentricity() * Eccentricity();
      m_esp = m_eccsq;
      m_e0 = 1.0 - 0.25 * m_eccsq * (1.0 + m_eccsq / 16.0 * (3.0 + 1.25 * m_eccsq));
      m_e1 = 0.375 * m_eccsq * (1.0 + 0.25 * m_eccsq * (1.0 + 0.468750 * m_eccsq));
      m_e2 = 0.058593750 * m_eccsq * m_eccsq * (1.0 + 0.750 * m_eccsq);
      m_e3 = m_eccsq * m_eccsq * m_eccsq * (35.0 / 3072.0);
      m_ml0 = m_equatorialRadius * (m_e0 * m_centerLatitude - m_e1 * sin(2.0 * m_centerLatitude) +
                                    m_e2 * sin(4.0 * m_centerLatitude) - m_e3 * sin(6.0 * m_centerLatitude));

      // Set flag for sphere or ellipsiod
      m_sph = true; // Sphere
      if (Eccentricity() >= .00001) {
        m_sph = false; // Ellipsoid
        m_esp = m_eccsq / (1.0 - m_eccsq);
      }

      // Get the scale factor
      if ((allowDefaults) && (!mapGroup.hasKeyword("ScaleFactor"))) {
        mapGroup += PvlKeyword("ScaleFactor", toString(1.0));
      }
      m_scalefactor = mapGroup["ScaleFactor"];
    }
    catch(IException &e) {
      string message = "Invalid label group [Mapping]";
      throw IException(e, IException::Io, message, _FILEINFO_);
    }
  }
예제 #8
0
  /**
   * This method is used to set the latitude/longitude (assumed to be of the
   * correct LatitudeType, LongitudeDirection, and LongitudeDomain. The Set
   * forces an attempted calculation of the projection X/Y values. This may or
   * may not be successful and a status is returned as such.
   *
   * @param lat Latitude value to project
   *
   * @param lon Longitude value to project
   *
   * @return bool
   */
  bool TransverseMercator::SetGround(const double lat, const double lon) {
    // Get longitude & fix direction
    m_longitude = lon;
    if (m_longitudeDirection == PositiveWest) m_longitude *= -1.0;

    double cLonDeg = m_centerLongitude * 180.0 / PI;
    double deltaLon = m_longitude - cLonDeg;
    while(deltaLon < -360.0) deltaLon += 360.0;
    while(deltaLon > 360.0) deltaLon -= 360.0;
    double deltaLonRads = deltaLon * PI / 180.0;

    // Now convert latitude to radians & clean up ... it must be planetographic
    m_latitude = lat;
    double latRadians = m_latitude * PI / 180.0;
    if (IsPlanetocentric()) {
      latRadians = ToPlanetographic(m_latitude) * PI / 180.0;
    }

    // distance along the meridian fromthe Equator to the latitude phi
    // see equation (3-21) on pages 61, 17.
    double M = m_equatorialRadius * (m_e0 * latRadians 
                                      - m_e1 * sin(2.0 * latRadians)
                                      + m_e2 * sin(4.0 * latRadians) 
                                      - m_e3 * sin(6.0 * latRadians));

    // Declare variables
    const double epsilon = 1.0e-10;

    // Sphere Conversion
    double x, y;
    if (m_sph) {
      double cosphi = cos(latRadians);
      double b = cosphi * sin(deltaLonRads);

      // Point projects to infinity
      if (fabs(fabs(b) - 1.0) <= epsilon) {
        m_good = false;
        return m_good;
      }
      x = 0.5 * m_equatorialRadius * m_scalefactor * log((1.0 + b) / (1.0 - b));

      // If arcosine argument is too close to 1, con=0.0 because arcosine(1)=0
      double con = cosphi * cos(deltaLonRads) / sqrt(1.0 - b * b);
      if (fabs(con) > 1.0) {
        con = 0.0;
      }
      else {
        con = acos(con);
      }
      if (m_latitude < 0.0) con = -con;
      y = m_equatorialRadius * m_scalefactor * (con - m_centerLatitude);
    }

    // Ellipsoid Conversion
    else {
      if (fabs(HALFPI - fabs(latRadians)) < epsilon) {
        x = 0.0;
        y = m_scalefactor * (M - m_ml0);
      }
      else {
        // Define Snyder's variables for ellipsoidal projections, page61
        double sinphi = sin(latRadians);
        double cosphi = cos(latRadians);
        double A = cosphi * deltaLonRads;        // see equation (8-15), page 61
        double Asquared = A * A;
        double C = m_esp * cosphi * cosphi;      // see equation (8-14), page 61
        double tanphi = tan(latRadians);
        double T = tanphi * tanphi;              // see equation (8-13), page 61
        double N = m_equatorialRadius / sqrt(1.0 - m_eccsq * sinphi * sinphi);
                                                 // see equation (4-20), page 61

        x = m_scalefactor * N * A 
               * (1.0 + Asquared / 6.0 * (1.0 - T + C + Asquared / 20.0 
                                 *(5.0 - 18.0*T + T*T + 72.0*C - 58.0*m_esp)));
        y = m_scalefactor 
               * (M - m_ml0 + N*tanphi*(Asquared * (0.5 + Asquared / 24.0 *
                             (5.0 - T + 9.0*C + 4.0*C*C + Asquared / 30.0
                             *(61.0 - 58.0*T + T*T + 600.0*C - 330.0*m_esp)))));
      }
    }

    SetComputedXY(x, y);
    m_good = true;
    return m_good;
  }
예제 #9
0
  /**
   * This method is used to set the projection x/y. The Set forces an attempted
   * calculation of the corresponding latitude/longitude position. This may or
   * may not be successful and a status is returned as such.
   *
   * @param x X coordinate of the projection in units that are the same as the
   *          radii in the label
   *
   * @param y Y coordinate of the projection in units that are the same as the
   *          radii in the label
   *
   * @return bool
   */
  bool PointPerspective::SetCoordinate(const double x, const double y) {
    // Save the coordinate
    SetXY(x, y);

    // Declare instance variables and calculate rho
    double rho, rp, con, com, z, sinz, cosz;
    const double epsilon = 1.0e-10;
    rho = sqrt(GetX() * GetX() + GetY() * GetY());
    rp = rho / m_equatorialRadius;
    con = m_P - 1.0;
    com = m_P + 1.0;

    // Error calculating rho - should be less than equatorial radius
    if (rp > (sqrt(con / com))) {
      m_good = false;
      return m_good;
    }

    // Calculate the latitude and longitude
    m_longitude = m_centerLongitude;
    if (fabs(rho) <= epsilon) {
      m_latitude = m_centerLatitude;
    }
    else {
      if (rp <= epsilon) {
        sinz = 0.0;
      }
      else {
        sinz = (m_P - sqrt(1.0 - rp * rp * com / con)) / (con / rp + rp / con);
      }
      z = asin(sinz);
      sinz = sin(z);
      cosz = cos(z);
      con = cosz * m_sinph0 + GetY() * sinz * m_cosph0 / rho;
      if (con > 1.0) con = 1.0;
      if (con < -1.0) con = -1.0;
      m_latitude = asin(con);

      con = fabs(m_centerLatitude) - HALFPI;
      if (fabs(con) <= epsilon) {
        if (m_centerLatitude >= 0.0) {
          m_longitude += atan2(GetX(), -GetY());
        }
        else {
          m_longitude += atan2(-GetX(), GetY());
        }
      }
      else {
        con = cosz - m_sinph0 * sin(m_latitude);
        if ((fabs(con) >= epsilon) || (fabs(GetX()) >= epsilon)) {
          m_longitude += atan2(GetX() * sinz * m_cosph0, con * rho);
        }
      }
    }

    // Convert to degrees
    m_latitude *= 180.0 / PI;
    m_longitude *= 180.0 / PI;

    // Cleanup the longitude
    if (m_longitudeDirection == PositiveWest) m_longitude *= -1.0;
    // These need to be done for circular type projections
    m_longitude = To360Domain(m_longitude);
    if (m_longitudeDomain == 180) m_longitude = To180Domain(m_longitude);

    // Cleanup the latitude
    if (IsPlanetocentric()) m_latitude = ToPlanetocentric(m_latitude);

    m_good = true;
    return m_good;
  }