/**
   * Normalize the geo {@code Point} for the given coordinates to lie within their respective
   * normalized ranges.
   *
   * <p>You can control which coordinate gets normalized with the two flags.
   *
   * <p>Note: A shift of 180&deg; is applied in the longitude if necessary, in order to normalize
   * properly the latitude. If normalizing latitude but not longitude, it is assumed that the
   * longitude is in the form x+k*360, with x in ]-180;180], and k is meaningful to the application.
   * Therefore x will be adjusted while keeping k preserved.
   *
   * @param point The point to normalize in-place.
   * @param normLat Whether to normalize latitude or leave it as is.
   * @param normLon Whether to normalize longitude.
   */
  public static void normalizePoint(GeoPoint point, boolean normLat, boolean normLon) {
    double lat = point.lat();
    double lon = point.lon();

    normLat = normLat && (lat > 90 || lat <= -90);
    normLon = normLon && (lon > 180 || lon <= -180);

    if (normLat) {
      lat = centeredModulus(lat, 360);
      boolean shift = true;
      if (lat < -90) {
        lat = -180 - lat;
      } else if (lat > 90) {
        lat = 180 - lat;
      } else {
        // No need to shift the longitude, and the latitude is normalized
        shift = false;
      }
      if (shift) {
        if (normLon) {
          lon += 180;
        } else {
          // Longitude won't be normalized,
          // keep it in the form x+k*360 (with x in ]-180;180])
          // by only changing x, assuming k is meaningful for the user application.
          lon += normalizeLon(lon) > 0 ? -180 : 180;
        }
      }
    }
    if (normLon) {
      lon = centeredModulus(lon, 360);
    }
    point.reset(lat, lon);
  }
 private void assertGeoPointQuery(GeoDistanceRangeQueryBuilder queryBuilder, Query query)
     throws IOException {
   assertThat(query, instanceOf(XGeoPointDistanceRangeQuery.class));
   XGeoPointDistanceRangeQuery geoQuery = (XGeoPointDistanceRangeQuery) query;
   assertThat(geoQuery.getField(), equalTo(queryBuilder.fieldName()));
   if (queryBuilder.point() != null) {
     GeoPoint expectedPoint = new GeoPoint(queryBuilder.point());
     GeoUtils.normalizePoint(expectedPoint);
     assertThat(geoQuery.getCenterLat(), equalTo(expectedPoint.lat()));
     assertThat(geoQuery.getCenterLon(), equalTo(expectedPoint.lon()));
   }
   if (queryBuilder.from() != null && queryBuilder.from() instanceof Number) {
     double fromValue = ((Number) queryBuilder.from()).doubleValue();
     if (queryBuilder.unit() != null) {
       fromValue = queryBuilder.unit().toMeters(fromValue);
     }
     assertThat(geoQuery.getMinRadiusMeters(), closeTo(fromValue, 1E-5));
   }
   if (queryBuilder.to() != null && queryBuilder.to() instanceof Number) {
     double toValue = ((Number) queryBuilder.to()).doubleValue();
     if (queryBuilder.unit() != null) {
       toValue = queryBuilder.unit().toMeters(toValue);
     }
     assertThat(geoQuery.getMaxRadiusMeters(), closeTo(toValue, 1E-5));
   }
 }
  private void parse(ParseContext context, GeoPoint point, String geohash) throws IOException {
    if (fieldType().ignoreMalformed == false) {
      if (point.lat() > 90.0 || point.lat() < -90.0) {
        throw new IllegalArgumentException(
            "illegal latitude value [" + point.lat() + "] for " + name());
      }
      if (point.lon() > 180.0 || point.lon() < -180) {
        throw new IllegalArgumentException(
            "illegal longitude value [" + point.lon() + "] for " + name());
      }
    }

    if (fieldType().coerce) {
      // by setting coerce to false we are assuming all geopoints are already in a valid coordinate
      // system
      // thus this extra step can be skipped
      // LUCENE WATCH: This will be folded back into Lucene's GeoPointField
      GeoUtils.normalizePoint(point, true, true);
    }

    if (fieldType().indexOptions() != IndexOptions.NONE || fieldType().stored()) {
      Field field =
          new Field(
              fieldType().names().indexName(),
              Double.toString(point.lat()) + ',' + Double.toString(point.lon()),
              fieldType());
      context.doc().add(field);
    }
    if (fieldType().isGeohashEnabled()) {
      if (geohash == null) {
        geohash = GeoHashUtils.encode(point.lat(), point.lon());
      }
      addGeohashField(context, geohash);
    }
    if (fieldType().isLatLonEnabled()) {
      latMapper.parse(context.createExternalValueContext(point.lat()));
      lonMapper.parse(context.createExternalValueContext(point.lon()));
    }
    if (fieldType().hasDocValues()) {
      CustomGeoPointDocValuesField field =
          (CustomGeoPointDocValuesField) context.doc().getByKey(fieldType().names().indexName());
      if (field == null) {
        field =
            new CustomGeoPointDocValuesField(
                fieldType().names().indexName(), point.lat(), point.lon());
        context.doc().addWithKey(fieldType().names().indexName(), field);
      } else {
        field.add(point.lat(), point.lon());
      }
    }
    multiFields.parse(this, context);
  }
 @Override
 protected boolean matchDoc(int doc) {
   values.setDocument(doc);
   final int length = values.count();
   for (int i = 0; i < length; i++) {
     GeoPoint point = values.valueAt(i);
     if (pointInPolygon(points, point.lat(), point.lon())) {
       return true;
     }
   }
   return false;
 }
 private void assertLegacyQuery(GeoDistanceRangeQueryBuilder queryBuilder, Query query)
     throws IOException {
   assertThat(query, instanceOf(GeoDistanceRangeQuery.class));
   GeoDistanceRangeQuery geoQuery = (GeoDistanceRangeQuery) query;
   assertThat(geoQuery.fieldName(), equalTo(queryBuilder.fieldName()));
   if (queryBuilder.point() != null) {
     GeoPoint expectedPoint = new GeoPoint(queryBuilder.point());
     if (GeoValidationMethod.isCoerce(queryBuilder.getValidationMethod())) {
       GeoUtils.normalizePoint(expectedPoint, true, true);
     }
     assertThat(geoQuery.lat(), equalTo(expectedPoint.lat()));
     assertThat(geoQuery.lon(), equalTo(expectedPoint.lon()));
   }
   assertThat(geoQuery.geoDistance(), equalTo(queryBuilder.geoDistance()));
   if (queryBuilder.from() != null && queryBuilder.from() instanceof Number) {
     double fromValue = ((Number) queryBuilder.from()).doubleValue();
     if (queryBuilder.unit() != null) {
       fromValue = queryBuilder.unit().toMeters(fromValue);
     }
     if (queryBuilder.geoDistance() != null) {
       fromValue = queryBuilder.geoDistance().normalize(fromValue, DistanceUnit.DEFAULT);
     }
     double fromSlop = Math.abs(fromValue) / 1000;
     if (queryBuilder.includeLower() == false) {
       fromSlop =
           NumericUtils.sortableLongToDouble(
                   (NumericUtils.doubleToSortableLong(Math.abs(fromValue)) + 1L))
               / 1000.0;
     }
     assertThat(geoQuery.minInclusiveDistance(), closeTo(fromValue, fromSlop));
   }
   if (queryBuilder.to() != null && queryBuilder.to() instanceof Number) {
     double toValue = ((Number) queryBuilder.to()).doubleValue();
     if (queryBuilder.unit() != null) {
       toValue = queryBuilder.unit().toMeters(toValue);
     }
     if (queryBuilder.geoDistance() != null) {
       toValue = queryBuilder.geoDistance().normalize(toValue, DistanceUnit.DEFAULT);
     }
     double toSlop = Math.abs(toValue) / 1000;
     if (queryBuilder.includeUpper() == false) {
       toSlop =
           NumericUtils.sortableLongToDouble(
                   (NumericUtils.doubleToSortableLong(Math.abs(toValue)) - 1L))
               / 1000.0;
     }
     assertThat(geoQuery.maxInclusiveDistance(), closeTo(toValue, toSlop));
   }
 }
  @Override
  protected GeoDistanceRangeQueryBuilder doCreateTestQueryBuilder() {
    GeoDistanceRangeQueryBuilder builder;
    GeoPoint randomPoint = RandomGeoGenerator.randomPointIn(random(), -180.0, -89.9, 180.0, 89.9);
    if (randomBoolean()) {
      builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME, randomPoint.geohash());
    } else {
      if (randomBoolean()) {
        builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME, randomPoint);
      } else {
        builder =
            new GeoDistanceRangeQueryBuilder(
                GEO_POINT_FIELD_NAME, randomPoint.lat(), randomPoint.lon());
      }
    }
    GeoPoint point = builder.point();
    final double maxRadius = GeoUtils.maxRadialDistanceMeters(point.lat(), point.lon());
    final int fromValueMeters = randomInt((int) (maxRadius * 0.5));
    final int toValueMeters = randomIntBetween(fromValueMeters + 1, (int) maxRadius);
    DistanceUnit fromToUnits = randomFrom(DistanceUnit.values());
    final String fromToUnitsStr = fromToUnits.toString();
    final double fromValue =
        DistanceUnit.convert(fromValueMeters, DistanceUnit.DEFAULT, fromToUnits);
    final double toValue = DistanceUnit.convert(toValueMeters, DistanceUnit.DEFAULT, fromToUnits);

    if (randomBoolean()) {
      int branch = randomInt(2);
      fromToUnits = DistanceUnit.DEFAULT;
      switch (branch) {
        case 0:
          builder.from(fromValueMeters);
          break;
        case 1:
          builder.to(toValueMeters);
          break;
        case 2:
          builder.from(fromValueMeters);
          builder.to(toValueMeters);
          break;
      }
    } else {
      int branch = randomInt(2);
      switch (branch) {
        case 0:
          builder.from(fromValue + fromToUnitsStr);
          break;
        case 1:
          builder.to(toValue + fromToUnitsStr);
          break;
        case 2:
          builder.from(fromValue + fromToUnitsStr);
          builder.to(toValue + fromToUnitsStr);
          break;
      }
    }
    if (randomBoolean()) {
      builder.includeLower(randomBoolean());
    }
    if (randomBoolean()) {
      builder.includeUpper(randomBoolean());
    }
    if (randomBoolean()) {
      builder.geoDistance(randomFrom(GeoDistance.values()));
    }
    builder.unit(fromToUnits);
    if (randomBoolean()) {
      builder.setValidationMethod(randomFrom(GeoValidationMethod.values()));
    }

    if (randomBoolean()) {
      builder.ignoreUnmapped(randomBoolean());
    }
    return builder;
  }