class GeoReference extends SimpleReference {

    private final String geoTree;

    @Nullable
    private final String precision;

    @Nullable
    private final Integer treeLevels;

    @Nullable
    private final Double distanceErrorPct;

    public GeoReference(ReferenceIdent ident,
                        DataType<?> type,
                        ColumnPolicy columnPolicy,
                        IndexType indexType,
                        boolean nullable,
                        int position,
                        long oid,
                        boolean isDropped,
                        Symbol defaultExpression,
                        String geoTree,
                        String precision,
                        Integer treeLevels,
                        Double distanceErrorPct) {
        super(ident,
            RowGranularity.DOC, // Only primitive types columns can be used in PARTITIONED BY clause
            type,
            columnPolicy,
            indexType,
            nullable,
            false, //Geo shapes don't have doc values
            position,
            oid,
            isDropped,
            defaultExpression
        );
        this.geoTree = Objects.requireNonNullElse(geoTree, TREE_GEOHASH);
        if (TREE_BKD.equals(this.geoTree) && (precision != null || treeLevels != null || distanceErrorPct != null)) {
            throw new IllegalArgumentException(
                "The parameters precision, tree_levels, and distance_error_pct are not applicable to BKD tree indexes."
            );
        }
        this.precision = precision;
        this.treeLevels = treeLevels;
        this.distanceErrorPct = distanceErrorPct;
    }

    @Override
    public SymbolType symbolType() {
        return SymbolType.GEO_REFERENCE;
    }

    public String geoTree() {
        return geoTree;
    }

    @Nullable
    public String precision() {
        return precision;
    }

    @Nullable
    public Integer treeLevels() {
        return treeLevels;
    }

    @Nullable
    public Double distanceErrorPct() {
        return distanceErrorPct;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        if (!super.equals(o)) {
            return false;
        }
        GeoReference that = (GeoReference) o;
        return java.util.Objects.equals(geoTree, that.geoTree) &&
               java.util.Objects.equals(precision, that.precision) &&
               java.util.Objects.equals(treeLevels, that.treeLevels) &&
               java.util.Objects.equals(distanceErrorPct, that.distanceErrorPct);
    }

    @Override
    public int hashCode() {
        return java.util.Objects.hash(super.hashCode(), geoTree, precision, treeLevels, distanceErrorPct);
    }

    public GeoReference(StreamInput in) throws IOException {
        super(in);
        geoTree = in.readString();
        precision = in.readOptionalString();
        treeLevels = in.readBoolean() ? null : in.readVInt();
        distanceErrorPct = in.readBoolean() ? null : in.readDouble();
    }


    @Override
    public void writeTo(StreamOutput out) throws IOException {
        super.writeTo(out);
        out.writeString(geoTree);
        out.writeOptionalString(precision);
        out.writeBoolean(treeLevels == null);
        if (treeLevels != null) {
            out.writeVInt(treeLevels);
        }
        out.writeBoolean(distanceErrorPct == null);
        if (distanceErrorPct != null) {
            out.writeDouble(distanceErrorPct);
        }
    }

    @Override
    public Reference withReferenceIdent(ReferenceIdent newIdent) {
        return new GeoReference(
            newIdent,
            type,
            columnPolicy,
            indexType,
            nullable,
            position,
            oid,
            isDropped,
            defaultExpression,
            geoTree,
            precision,
            treeLevels,
            distanceErrorPct
        );
    }

    @Override
    public Reference withOidAndPosition(LongSupplier acquireOid, IntSupplier acquirePosition) {
        long newOid = oid == COLUMN_OID_UNASSIGNED ? acquireOid.getAsLong() : oid;
        int newPosition = position < 0 ? acquirePosition.getAsInt() : position;
        if (newOid == oid && newPosition == position) {
            return this;
        }
        return new GeoReference(
            ident,
            type,
            columnPolicy,
            indexType,
            nullable,
            newPosition,
            newOid,
            isDropped,
            defaultExpression,
            geoTree,
            precision,
            treeLevels,
            distanceErrorPct
        );
    }

    @Override
    public GeoReference withValueType(DataType<?> newType) {
        return new GeoReference(
            ident,
            newType,
            columnPolicy,
            indexType,
            nullable,
            position,
            oid,
            isDropped,
            defaultExpression,
            geoTree,
            precision,
            treeLevels,
            distanceErrorPct
        );
    }

    @Override
    public Map<String, Object> toMapping(int position) {<FILL_FUNCTION_BODY>}
}

        Map<String, Object> mapping = super.toMapping(position);
        Maps.putNonNull(mapping, "tree", geoTree);
        Maps.putNonNull(mapping, "precision", precision);
        Maps.putNonNull(mapping, "tree_levels", treeLevels);
        if (distanceErrorPct != null) {
            mapping.put("distance_error_pct", distanceErrorPct.floatValue());
        }
        return mapping;
    
<methods>public void <init>(org.elasticsearch.common.io.stream.StreamInput) throws java.io.IOException,public void <init>(io.crate.metadata.ReferenceIdent, io.crate.metadata.RowGranularity, DataType<?>, int, io.crate.expression.symbol.Symbol) ,public void <init>(io.crate.metadata.ReferenceIdent, io.crate.metadata.RowGranularity, DataType<?>, io.crate.sql.tree.ColumnPolicy, io.crate.metadata.IndexType, boolean, boolean, int, long, boolean, io.crate.expression.symbol.Symbol) ,public R accept(SymbolVisitor<C,R>, C) ,public io.crate.metadata.ColumnIdent column() ,public io.crate.sql.tree.ColumnPolicy columnPolicy() ,public io.crate.expression.symbol.Symbol defaultExpression() ,public boolean equals(java.lang.Object) ,public io.crate.metadata.RowGranularity granularity() ,public boolean hasDocValues() ,public int hashCode() ,public io.crate.metadata.ReferenceIdent ident() ,public io.crate.metadata.IndexType indexType() ,public boolean isDropped() ,public boolean isGenerated() ,public boolean isNullable() ,public long oid() ,public int position() ,public long ramBytesUsed() ,public io.crate.expression.symbol.SymbolType symbolType() ,public Map<java.lang.String,java.lang.Object> toMapping(int) ,public java.lang.String toString() ,public java.lang.String toString(io.crate.expression.symbol.format.Style) ,public DataType<?> valueType() ,public io.crate.metadata.Reference withDropped(boolean) ,public io.crate.metadata.Reference withOidAndPosition(java.util.function.LongSupplier, java.util.function.IntSupplier) ,public io.crate.metadata.Reference withReferenceIdent(io.crate.metadata.ReferenceIdent) ,public io.crate.metadata.Reference withValueType(DataType<?>) ,public void writeTo(org.elasticsearch.common.io.stream.StreamOutput) throws java.io.IOException<variables>private static final long SHALLOW_SIZE,protected final non-sealed io.crate.sql.tree.ColumnPolicy columnPolicy,protected final non-sealed io.crate.expression.symbol.Symbol defaultExpression,protected final non-sealed io.crate.metadata.RowGranularity granularity,protected final non-sealed boolean hasDocValues,protected final non-sealed io.crate.metadata.ReferenceIdent ident,protected final non-sealed io.crate.metadata.IndexType indexType,protected boolean isDropped,protected final non-sealed boolean nullable,protected final non-sealed long oid,protected final non-sealed int position,protected DataType<?> type