All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opensearch.common.geo.GeoShapeDocValue Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * The OpenSearch Contributors require contributions made to
 * this file be licensed under the Apache-2.0 license or a
 * compatible open source license.
 */

package org.opensearch.common.geo;

import org.apache.lucene.document.Field;
import org.apache.lucene.document.LatLonShape;
import org.apache.lucene.document.LatLonShapeDocValues;
import org.apache.lucene.document.LatLonShapeDocValuesField;
import org.apache.lucene.geo.LatLonGeometry;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.PointValues;
import org.apache.lucene.util.BytesRef;
import org.opensearch.geometry.Geometry;
import org.opensearch.geometry.GeometryVisitor;
import org.opensearch.geometry.Rectangle;
import org.opensearch.index.mapper.GeoShapeIndexer;

import java.io.IOException;
import java.util.List;

/**
 * This class is an OpenSearch Internal representation of lucene {@link LatLonShapeDocValuesField} for GeoShape.
 *
 * @opensearch.internal
 */
public class GeoShapeDocValue extends ShapeDocValue {
    private static final String FIELD_NAME = "missingField";
    private final LatLonShapeDocValues shapeDocValues;

    public GeoShapeDocValue(final String fieldName, final BytesRef bytesRef) {
        this(LatLonShape.createDocValueField(fieldName, bytesRef));
    }

    public GeoShapeDocValue(final LatLonShapeDocValuesField shapeDocValuesField) {
        centroid = new Centroid(shapeDocValuesField.getCentroid().getLat(), shapeDocValuesField.getCentroid().getLon());
        highestDimensionType = ShapeType.fromShapeFieldType(shapeDocValuesField.getHighestDimensionType());
        boundingRectangle = new BoundingRectangle(
            shapeDocValuesField.getBoundingBox().maxLon,
            shapeDocValuesField.getBoundingBox().maxLat,
            shapeDocValuesField.getBoundingBox().minLon,
            shapeDocValuesField.getBoundingBox().minLat
        );
        this.shapeDocValues = LatLonShape.createLatLonShapeDocValues(shapeDocValuesField.binaryValue());
    }

    /**
     * This function takes a {@link Geometry} and creates the {@link GeoShapeDocValue}. The function uses the
     * {@link GeoShapeIndexer} to first convert the {@link Geometry} to {@link IndexableField}s and then convert it
     * to the DocValue. This is very expensive function and should not be used on the Geometry Objects which are
     * already converted to {@link IndexableField}s as it does the Tessellation internally which is already done on the
     * {@link IndexableField}s.
     *
     * @param geometry {@link Geometry}
     * @return {@link GeoShapeDocValue}
     */
    public static GeoShapeDocValue createGeometryDocValue(final Geometry geometry) {
        // Setting the orientation to CCW, which will make the holes to CW. This is the default value which we will
        // be using. This is in conjunction with what we take as a default value for geoshape in WKT format.
        final GeoShapeIndexer shapeIndex = new GeoShapeIndexer(true, FIELD_NAME);
        final List indexableFields = shapeIndex.indexShape(null, shapeIndex.prepareForIndexing(geometry));
        Field[] fieldsArray = new Field[indexableFields.size()];
        fieldsArray = indexableFields.toArray(fieldsArray);
        final LatLonShapeDocValuesField latLonShapeDocValuesField = LatLonShape.createDocValueField(FIELD_NAME, fieldsArray);
        return new GeoShapeDocValue(latLonShapeDocValuesField);
    }

    public Centroid getCentroid() {
        return (Centroid) centroid;
    }

    public BoundingRectangle getBoundingRectangle() {
        return (BoundingRectangle) boundingRectangle;
    }

    @Override
    public String toString() {
        return "BoundingRectangle(" + boundingRectangle + "), Centroid(" + centroid + "), HighestDimension(" + highestDimensionType + ")";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        GeoShapeDocValue object = (GeoShapeDocValue) o;
        boolean isEqual = true;
        if (boundingRectangle != null) {
            isEqual = boundingRectangle.equals(object.getBoundingRectangle());
        }
        if (centroid != null) {
            isEqual = isEqual && centroid.equals(object.getCentroid());
        }
        if (highestDimensionType != null) {
            isEqual = isEqual && highestDimensionType == object.getHighestDimensionType();
        }
        return isEqual;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = boundingRectangle != null ? boundingRectangle.hashCode() : 0L;
        result = Long.hashCode(temp);
        temp = centroid != null ? centroid.hashCode() : 0L;
        result = 31 * result + Long.hashCode(temp);
        temp = highestDimensionType != null ? highestDimensionType.hashCode() : 0L;

        result = 31 * result + Long.hashCode(temp);
        return result;
    }

    /**
     * An extension for {@link ShapeDocValue.Centroid} which make easy to read the values when centroid is on the
     * EarthSurface
     */
    public static final class Centroid extends ShapeDocValue.Centroid {

        Centroid(final double lat, final double lon) {
            super(lat, lon);
        }

        public double getLatitude() {
            return getY();
        }

        public double getLongitude() {
            return getX();
        }

        @Override
        public String toString() {
            return getY() + ", " + getX();
        }

    }

    /**
     * An extension for {@link ShapeDocValue.BoundingRectangle} which make easy to read the values when BB is on the
     * EarthSurface
     */
    public static final class BoundingRectangle extends ShapeDocValue.BoundingRectangle {

        BoundingRectangle(final double maxLon, final double maxLat, final double minLon, final double minLat) {
            super(maxLon, maxLat, minLon, minLat);
        }

        public double getMaxLongitude() {
            return getMaxX();
        }

        public double getMaxLatitude() {
            return getMaxY();
        }

        public double getMinLatitude() {
            return getMinY();
        }

        public double getMinLongitude() {
            return getMinX();
        }

        @Override
        public String toString() {
            return "maxLatitude: "
                + getMaxY()
                + ", minLatitude: "
                + getMinY()
                + ", maxLongitude: "
                + getMaxX()
                + ", minLongitude: "
                + getMinX();

        }
    }

    /**
     * Checks if the input {@link Rectangle} is intersecting with the shape represented as {@link GeoShapeDocValue}.
     * We could have used the {@link GeometryVisitor} here and added the functionality to check the intersection with
     * other {@link Geometry} also, but that will be an overkill for now, if required we can easily create a
     * {@link GeometryVisitor} to check the intersection with this Shape represented as {@link GeoShapeDocValue}.
     * @return boolean
     */
    public boolean isIntersectingRectangle(final Rectangle rectangle) {
        final org.apache.lucene.geo.Rectangle luceneRectangle = GeoShapeUtils.toLuceneRectangle(rectangle);
        try {
            final PointValues.Relation relation = shapeDocValues.relate(LatLonGeometry.create(luceneRectangle));
            return relation != PointValues.Relation.CELL_OUTSIDE_QUERY;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy