public Coordinate projectInverse(double xyx, double xyy, Coordinate out) {
    double c;

    out.y = MapMath.asin(xyy / C_y);
    out.x = xyx / (C_x * ((c = Math.cos(out.y)) - 0.5));
    out.y = MapMath.asin((out.y + Math.sin(out.y) * (c - 1.)) / C_p);
    return out;
  }
  public Point2D.Double project(double lam, double phi, Point2D.Double xy) {
    if (spherical) {
      double coslam, cosphi, sinphi;

      sinphi = Math.sin(phi);
      cosphi = Math.cos(phi);
      coslam = Math.cos(lam);
      switch (mode) {
        case EQUATOR:
        case OBLIQUE:
          if (mode == EQUATOR) xy.y = cosphi * coslam;
          else xy.y = sinphi0 * sinphi + cosphi0 * cosphi * coslam;
          if (Math.abs(Math.abs(xy.y) - 1.) < TOL)
            if (xy.y < 0.) throw new ProjectionException();
            else xy.x = xy.y = 0.;
          else {
            xy.y = Math.acos(xy.y);
            xy.y /= Math.sin(xy.y);
            xy.x = xy.y * cosphi * Math.sin(lam);
            xy.y *= (mode == EQUATOR) ? sinphi : cosphi0 * sinphi - sinphi0 * cosphi * coslam;
          }
          break;
        case NORTH_POLE:
          phi = -phi;
          coslam = -coslam;
        case SOUTH_POLE:
          if (Math.abs(phi - MapMath.HALFPI) < EPS10) throw new ProjectionException();
          xy.x = (xy.y = (MapMath.HALFPI + phi)) * Math.sin(lam);
          xy.y *= coslam;
          break;
      }
    } else {
      double coslam, cosphi, sinphi, rho, s, H, H2, c, Az, t, ct, st, cA, sA;

      coslam = Math.cos(lam);
      cosphi = Math.cos(phi);
      sinphi = Math.sin(phi);
      switch (mode) {
        case NORTH_POLE:
          coslam = -coslam;
        case SOUTH_POLE:
          xy.x = (rho = Math.abs(Mp - MapMath.mlfn(phi, sinphi, cosphi, en))) * Math.sin(lam);
          xy.y = rho * coslam;
          break;
        case EQUATOR:
        case OBLIQUE:
          if (Math.abs(lam) < EPS10 && Math.abs(phi - projectionLatitude) < EPS10) {
            xy.x = xy.y = 0.;
            break;
          }
          t =
              Math.atan2(
                  one_es * sinphi + es * N1 * sinphi0 * Math.sqrt(1. - es * sinphi * sinphi),
                  cosphi);
          ct = Math.cos(t);
          st = Math.sin(t);
          Az = Math.atan2(Math.sin(lam) * ct, cosphi0 * st - sinphi0 * coslam * ct);
          cA = Math.cos(Az);
          sA = Math.sin(Az);
          s =
              MapMath.asin(
                  Math.abs(sA) < TOL
                      ? (cosphi0 * st - sinphi0 * coslam * ct) / cA
                      : Math.sin(lam) * ct / sA);
          H = He * cA;
          H2 = H * H;
          c =
              N1
                  * s
                  * (1.
                      + s
                          * s
                          * (-H2 * (1. - H2) / 6.
                              + s
                                  * (G * H * (1. - 2. * H2 * H2) / 8.
                                      + s
                                          * ((H2 * (4. - 7. * H2) - 3. * G * G * (1. - 7. * H2))
                                                  / 120.
                                              - s * G * H / 48.))));
          xy.x = c * sA;
          xy.y = c * cA;
          break;
      }
    }
    return xy;
  }
  public Point2D.Double projectInverse(double x, double y, Point2D.Double lp) {
    if (spherical) {
      double cosc, c_rh, sinc;

      if ((c_rh = MapMath.distance(x, y)) > Math.PI) {
        if (c_rh - EPS10 > Math.PI) throw new ProjectionException();
        c_rh = Math.PI;
      } else if (c_rh < EPS10) {
        lp.y = projectionLatitude;
        lp.x = 0.;
        return lp;
      }
      if (mode == OBLIQUE || mode == EQUATOR) {
        sinc = Math.sin(c_rh);
        cosc = Math.cos(c_rh);
        if (mode == EQUATOR) {
          lp.y = MapMath.asin(y * sinc / c_rh);
          x *= sinc;
          y = cosc * c_rh;
        } else {
          lp.y = MapMath.asin(cosc * sinphi0 + y * sinc * cosphi0 / c_rh);
          y = (cosc - sinphi0 * Math.sin(lp.y)) * c_rh;
          x *= sinc * cosphi0;
        }
        lp.x = y == 0. ? 0. : Math.atan2(x, y);
      } else if (mode == NORTH_POLE) {
        lp.y = MapMath.HALFPI - c_rh;
        lp.x = Math.atan2(x, -y);
      } else {
        lp.y = c_rh - MapMath.HALFPI;
        lp.x = Math.atan2(x, y);
      }
    } else {
      double c, Az, cosAz, A, B, D, E, F, psi, t;
      int i;

      if ((c = MapMath.distance(x, y)) < EPS10) {
        lp.y = projectionLatitude;
        lp.x = 0.;
        return (lp);
      }
      if (mode == OBLIQUE || mode == EQUATOR) {
        cosAz = Math.cos(Az = Math.atan2(x, y));
        t = cosphi0 * cosAz;
        B = es * t / one_es;
        A = -B * t;
        B *= 3. * (1. - A) * sinphi0;
        D = c / N1;
        E = D * (1. - D * D * (A * (1. + A) / 6. + B * (1. + 3. * A) * D / 24.));
        F = 1. - E * E * (A / 2. + B * E / 6.);
        psi = MapMath.asin(sinphi0 * Math.cos(E) + t * Math.sin(E));
        lp.x = MapMath.asin(Math.sin(Az) * Math.sin(E) / Math.cos(psi));
        if ((t = Math.abs(psi)) < EPS10) lp.y = 0.;
        else if (Math.abs(t - MapMath.HALFPI) < 0.) lp.y = MapMath.HALFPI;
        else lp.y = Math.atan((1. - es * F * sinphi0 / Math.sin(psi)) * Math.tan(psi) / one_es);
      } else {
        lp.y = MapMath.inv_mlfn(mode == NORTH_POLE ? Mp - c : Mp + c, es, en);
        lp.x = Math.atan2(x, mode == NORTH_POLE ? -y : y);
      }
    }
    return lp;
  }