/**
   * Called after bounding box is intersected.
   *
   * @param bboxSect INTERSECTS or CONTAINS from enclosingBox's intersection
   * @return DISJOINT, CONTAINS, or INTERSECTS (not WITHIN)
   */
  @Override
  protected SpatialRelation relateRectanglePhase2(Rectangle r, SpatialRelation bboxSect) {

    // Rectangle wraps around the world longitudinally creating a solid band; there are no corners
    // to test intersection
    if (r.getWidth() == 360) {
      return SpatialRelation.INTERSECTS;
    }

    if (inverseCircle != null) {
      return inverseCircle.relate(r).inverse();
    }

    // if a pole is wrapped, we have a separate algorithm
    if (enclosingBox.getWidth() == 360) {
      return relateRectangleCircleWrapsPole(r, ctx);
    }

    // This is an optimization path for when there are no dateline or pole issues.
    if (!enclosingBox.getCrossesDateLine() && !r.getCrossesDateLine()) {
      return super.relateRectanglePhase2(r, bboxSect);
    }

    // do quick check to see if all corners are within this circle for CONTAINS
    int cornersIntersect = numCornersIntersect(r);
    if (cornersIntersect == 4) {
      // ensure r's x axis is within c's.  If it doesn't, r sneaks around the globe to touch the
      // other side (intersect).
      SpatialRelation xIntersect = r.relateXRange(enclosingBox.getMinX(), enclosingBox.getMaxX());
      if (xIntersect == SpatialRelation.WITHIN) return SpatialRelation.CONTAINS;
      return SpatialRelation.INTERSECTS;
    }

    // INTERSECT or DISJOINT ?
    if (cornersIntersect > 0) return SpatialRelation.INTERSECTS;

    // Now we check if one of the axis of the circle intersect with r.  If so we have
    // intersection.

    /* x axis intersects  */
    if (r.relateYRange(getYAxis(), getYAxis()).intersects() // at y vertical
        && r.relateXRange(enclosingBox.getMinX(), enclosingBox.getMaxX()).intersects())
      return SpatialRelation.INTERSECTS;

    /* y axis intersects */
    if (r.relateXRange(getXAxis(), getXAxis()).intersects()) { // at x horizontal
      double yTop = getCenter().getY() + radiusDEG;
      assert yTop <= 90;
      double yBot = getCenter().getY() - radiusDEG;
      assert yBot >= -90;
      if (r.relateYRange(yBot, yTop).intersects()) // back bottom
      return SpatialRelation.INTERSECTS;
    }

    return SpatialRelation.DISJOINT;
  }
  private SpatialRelation relateRectangleCircleWrapsPole(Rectangle r, SpatialContext ctx) {
    // This method handles the case where the circle wraps ONE pole, but not both.  For both,
    // there is the inverseCircle case handled before now.  The only exception is for the case where
    // the circle covers the entire globe, and we'll check that first.
    if (radiusDEG == 180) // whole globe
    return SpatialRelation.CONTAINS;

    // Check if r is within the pole wrap region:
    double yTop = getCenter().getY() + radiusDEG;
    if (yTop > 90) {
      double yTopOverlap = yTop - 90;
      assert yTopOverlap <= 90;
      if (r.getMinY() >= 90 - yTopOverlap) return SpatialRelation.CONTAINS;
    } else {
      double yBot = point.getY() - radiusDEG;
      if (yBot < -90) {
        double yBotOverlap = -90 - yBot;
        assert yBotOverlap <= 90;
        if (r.getMaxY() <= -90 + yBotOverlap) return SpatialRelation.CONTAINS;
      } else {
        // This point is probably not reachable ??
        assert yTop == 90 || yBot == -90; // we simply touch a pole
        // continue
      }
    }

    // If there are no corners to check intersection because r wraps completely...
    if (r.getWidth() == 360) return SpatialRelation.INTERSECTS;

    // Check corners:
    int cornersIntersect = numCornersIntersect(r);
    // (It might be possible to reduce contains() calls within nCI() to exactly two, but this
    // intersection
    //  code is complicated enough as it is.)
    double frontX = getCenter().getX();
    if (cornersIntersect == 4) { // all
      double backX = frontX <= 0 ? frontX + 180 : frontX - 180;
      if (r.relateXRange(backX, backX).intersects()) return SpatialRelation.INTERSECTS;
      else return SpatialRelation.CONTAINS;
    } else if (cornersIntersect == 0) { // none
      if (r.relateXRange(frontX, frontX).intersects()) return SpatialRelation.INTERSECTS;
      else return SpatialRelation.DISJOINT;
    } else // partial
    return SpatialRelation.INTERSECTS;
  }