public static class Defaults {
    public static final String TREE = Names.TREE_GEOHASH;
    public static final String STRATEGY = SpatialStrategy.RECURSIVE.getStrategyName();
    public static final boolean POINTS_ONLY = false;
    public static final int GEOHASH_LEVELS = GeoUtils.geoHashLevelsForPrecision("50m");
    public static final int QUADTREE_LEVELS = GeoUtils.quadTreeLevelsForPrecision("50m");
    public static final double LEGACY_DISTANCE_ERROR_PCT = 0.025d;
    public static final Orientation ORIENTATION = Orientation.RIGHT;
    public static final Explicit<Boolean> COERCE = new Explicit<>(false, false);

    public static final MappedFieldType FIELD_TYPE = new GeoShapeFieldType();

    static {
      // setting name here is a hack so freeze can be called...instead all these options should be
      // moved to the default ctor for GeoShapeFieldType, and defaultFieldType() should be removed
      // from mappers...
      FIELD_TYPE.setNames(new MappedFieldType.Names("DoesNotExist"));
      FIELD_TYPE.setIndexOptions(IndexOptions.DOCS);
      FIELD_TYPE.setTokenized(false);
      FIELD_TYPE.setStored(false);
      FIELD_TYPE.setStoreTermVectors(false);
      FIELD_TYPE.setOmitNorms(true);
      FIELD_TYPE.freeze();
    }
  }
 private static int getLevels(
     int treeLevels, double precisionInMeters, int defaultLevels, boolean geoHash) {
   if (treeLevels > 0 || precisionInMeters >= 0) {
     return Math.max(
         treeLevels,
         precisionInMeters >= 0
             ? (geoHash
                 ? GeoUtils.geoHashLevelsForPrecision(precisionInMeters)
                 : GeoUtils.quadTreeLevelsForPrecision(precisionInMeters))
             : 0);
   }
   return defaultLevels;
 }
  @Override
  public Mapper parse(ParseContext context) throws IOException {
    ContentPath.Type origPathType = context.path().pathType();
    context.path().pathType(pathType);
    context.path().add(simpleName());

    GeoPoint sparse = context.parseExternalValue(GeoPoint.class);

    if (sparse != null) {
      parse(context, sparse, null);
    } else {
      sparse = new GeoPoint();
      XContentParser.Token token = context.parser().currentToken();
      if (token == XContentParser.Token.START_ARRAY) {
        token = context.parser().nextToken();
        if (token == XContentParser.Token.START_ARRAY) {
          // its an array of array of lon/lat [ [1.2, 1.3], [1.4, 1.5] ]
          while (token != XContentParser.Token.END_ARRAY) {
            parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
            token = context.parser().nextToken();
          }
        } else {
          // its an array of other possible values
          if (token == XContentParser.Token.VALUE_NUMBER) {
            double lon = context.parser().doubleValue();
            token = context.parser().nextToken();
            double lat = context.parser().doubleValue();
            while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) ;
            parse(context, sparse.reset(lat, lon), null);
          } else {
            while (token != XContentParser.Token.END_ARRAY) {
              if (token == XContentParser.Token.VALUE_STRING) {
                parsePointFromString(context, sparse, context.parser().text());
              } else {
                parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
              }
              token = context.parser().nextToken();
            }
          }
        }
      } else if (token == XContentParser.Token.VALUE_STRING) {
        parsePointFromString(context, sparse, context.parser().text());
      } else if (token != XContentParser.Token.VALUE_NULL) {
        parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
      }
    }

    context.path().remove();
    context.path().pathType(origPathType);
    return null;
  }
 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);
  }
 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));
   }
 }
 private AbstractDistanceScoreFunction parseGeoVariable(
     XContentParser parser,
     QueryShardContext context,
     BaseGeoPointFieldMapper.GeoPointFieldType fieldType,
     MultiValueMode mode)
     throws IOException {
   XContentParser.Token token;
   String parameterName = null;
   GeoPoint origin = new GeoPoint();
   String scaleString = null;
   String offsetString = "0km";
   double decay = 0.5;
   while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
     if (token == XContentParser.Token.FIELD_NAME) {
       parameterName = parser.currentName();
     } else if (DecayFunctionBuilder.SCALE.equals(parameterName)) {
       scaleString = parser.text();
     } else if (DecayFunctionBuilder.ORIGIN.equals(parameterName)) {
       origin = GeoUtils.parseGeoPoint(parser);
     } else if (DecayFunctionBuilder.DECAY.equals(parameterName)) {
       decay = parser.doubleValue();
     } else if (DecayFunctionBuilder.OFFSET.equals(parameterName)) {
       offsetString = parser.text();
     } else {
       throw new ElasticsearchParseException("parameter [{}] not supported!", parameterName);
     }
   }
   if (origin == null || scaleString == null) {
     throw new ElasticsearchParseException(
         "[{}] and [{}] must be set for geo fields.",
         DecayFunctionBuilder.ORIGIN,
         DecayFunctionBuilder.SCALE);
   }
   double scale = DistanceUnit.DEFAULT.parse(scaleString, DistanceUnit.DEFAULT);
   double offset = DistanceUnit.DEFAULT.parse(offsetString, DistanceUnit.DEFAULT);
   IndexGeoPointFieldData indexFieldData = context.getForField(fieldType);
   return new GeoFieldDataScoreFunction(
       origin, scale, decay, offset, getDecayFunction(), indexFieldData, mode);
 }
  @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;
  }
    @Override
    public Builder fromXContent(QueryParseContext parseContext) throws IOException {
      XContentParser parser = parseContext.parser();

      String fieldName = null;
      String geohash = null;
      Integer levels = null;
      Boolean neighbors = null;
      String queryName = null;
      Float boost = null;

      XContentParser.Token token;
      if ((token = parser.currentToken()) != Token.START_OBJECT) {
        throw new ElasticsearchParseException(
            "failed to parse [{}] query. expected an object but found [{}] instead", NAME, token);
      }

      while ((token = parser.nextToken()) != Token.END_OBJECT) {
        if (token == Token.FIELD_NAME) {
          String field = parser.currentName();

          if (parseContext.isDeprecatedSetting(field)) {
            // skip
          } else if (parseContext.parseFieldMatcher().match(field, PRECISION_FIELD)) {
            token = parser.nextToken();
            if (token == Token.VALUE_NUMBER) {
              levels = parser.intValue();
            } else if (token == Token.VALUE_STRING) {
              double meters =
                  DistanceUnit.parse(parser.text(), DistanceUnit.DEFAULT, DistanceUnit.METERS);
              levels = GeoUtils.geoHashLevelsForPrecision(meters);
            }
          } else if (parseContext.parseFieldMatcher().match(field, NEIGHBORS_FIELD)) {
            parser.nextToken();
            neighbors = parser.booleanValue();
          } else if (parseContext
              .parseFieldMatcher()
              .match(field, AbstractQueryBuilder.NAME_FIELD)) {
            parser.nextToken();
            queryName = parser.text();
          } else if (parseContext
              .parseFieldMatcher()
              .match(field, AbstractQueryBuilder.BOOST_FIELD)) {
            parser.nextToken();
            boost = parser.floatValue();
          } else {
            if (fieldName == null) {
              fieldName = field;
              token = parser.nextToken();
              if (token == Token.VALUE_STRING) {
                // A string indicates either a geohash or a
                // lat/lon
                // string
                String location = parser.text();
                if (location.indexOf(",") > 0) {
                  geohash = GeoUtils.parseGeoPoint(parser).geohash();
                } else {
                  geohash = location;
                }
              } else {
                geohash = GeoUtils.parseGeoPoint(parser).geohash();
              }
            } else {
              throw new ParsingException(
                  parser.getTokenLocation(),
                  "["
                      + NAME
                      + "] field name already set to ["
                      + fieldName
                      + "] but found ["
                      + field
                      + "]");
            }
          }
        } else {
          throw new ElasticsearchParseException(
              "failed to parse [{}] query. unexpected token [{}]", NAME, token);
        }
      }
      Builder builder = new Builder(fieldName, geohash);
      if (levels != null) {
        builder.precision(levels);
      }
      if (neighbors != null) {
        builder.neighbors(neighbors);
      }
      if (queryName != null) {
        builder.queryName(queryName);
      }
      if (boost != null) {
        builder.boost(boost);
      }
      return builder;
    }
 public Builder precision(String precision) {
   double meters = DistanceUnit.parse(precision, DistanceUnit.DEFAULT, DistanceUnit.METERS);
   return precision(GeoUtils.geoHashLevelsForPrecision(meters));
 }
 @Override
 public Mapper.Builder<?, ?> parse(
     String name, Map<String, Object> node, ParserContext parserContext)
     throws MapperParsingException {
   Builder builder = geoPointField(name);
   final boolean indexCreatedBeforeV2_0 =
       parserContext.indexVersionCreated().before(Version.V_2_0_0);
   parseField(builder, name, node, parserContext);
   for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator();
       iterator.hasNext(); ) {
     Map.Entry<String, Object> entry = iterator.next();
     String fieldName = Strings.toUnderscoreCase(entry.getKey());
     Object fieldNode = entry.getValue();
     if (fieldName.equals("path")
         && parserContext.indexVersionCreated().before(Version.V_2_0_0_beta1)) {
       builder.multiFieldPathType(parsePathType(name, fieldNode.toString()));
       iterator.remove();
     } else if (fieldName.equals("lat_lon")) {
       builder.enableLatLon(XContentMapValues.nodeBooleanValue(fieldNode));
       iterator.remove();
     } else if (fieldName.equals("geohash")) {
       builder.enableGeoHash(XContentMapValues.nodeBooleanValue(fieldNode));
       iterator.remove();
     } else if (fieldName.equals("geohash_prefix")) {
       builder.geohashPrefix(XContentMapValues.nodeBooleanValue(fieldNode));
       if (XContentMapValues.nodeBooleanValue(fieldNode)) {
         builder.enableGeoHash(true);
       }
       iterator.remove();
     } else if (fieldName.equals("precision_step")) {
       builder.precisionStep(XContentMapValues.nodeIntegerValue(fieldNode));
       iterator.remove();
     } else if (fieldName.equals("geohash_precision")) {
       if (fieldNode instanceof Integer) {
         builder.geoHashPrecision(XContentMapValues.nodeIntegerValue(fieldNode));
       } else {
         builder.geoHashPrecision(GeoUtils.geoHashLevelsForPrecision(fieldNode.toString()));
       }
       iterator.remove();
     } else if (fieldName.equals(Names.IGNORE_MALFORMED)) {
       if (builder.fieldType().coerce == false) {
         builder.fieldType().ignoreMalformed = XContentMapValues.nodeBooleanValue(fieldNode);
       }
       iterator.remove();
     } else if (indexCreatedBeforeV2_0 && fieldName.equals("validate")) {
       if (builder.fieldType().ignoreMalformed == false) {
         builder.fieldType().ignoreMalformed = !XContentMapValues.nodeBooleanValue(fieldNode);
       }
       iterator.remove();
     } else if (indexCreatedBeforeV2_0 && fieldName.equals("validate_lon")) {
       if (builder.fieldType().ignoreMalformed() == false) {
         builder.fieldType().ignoreMalformed = !XContentMapValues.nodeBooleanValue(fieldNode);
       }
       iterator.remove();
     } else if (indexCreatedBeforeV2_0 && fieldName.equals("validate_lat")) {
       if (builder.fieldType().ignoreMalformed == false) {
         builder.fieldType().ignoreMalformed = !XContentMapValues.nodeBooleanValue(fieldNode);
       }
       iterator.remove();
     } else if (fieldName.equals(Names.COERCE)) {
       builder.fieldType().coerce = XContentMapValues.nodeBooleanValue(fieldNode);
       if (builder.fieldType().coerce == true) {
         builder.fieldType().ignoreMalformed = true;
       }
       iterator.remove();
     } else if (indexCreatedBeforeV2_0 && fieldName.equals("normalize")) {
       builder.fieldType().coerce = XContentMapValues.nodeBooleanValue(fieldNode);
       iterator.remove();
     } else if (indexCreatedBeforeV2_0 && fieldName.equals("normalize_lat")) {
       builder.fieldType().coerce = XContentMapValues.nodeBooleanValue(fieldNode);
       iterator.remove();
     } else if (indexCreatedBeforeV2_0 && fieldName.equals("normalize_lon")) {
       if (builder.fieldType().coerce == false) {
         builder.fieldType().coerce = XContentMapValues.nodeBooleanValue(fieldNode);
       }
       iterator.remove();
     } else if (parseMultiField(builder, name, parserContext, fieldName, fieldNode)) {
       iterator.remove();
     }
   }
   return builder;
 }