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

org.apache.lucene.spatial3d.PointInShapeIntersectVisitor Maven / Gradle / Ivy

There is a newer version: 10.1.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.lucene.spatial3d;

import java.io.IOException;
import org.apache.lucene.index.PointValues.IntersectVisitor;
import org.apache.lucene.index.PointValues.Relation;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.spatial3d.geom.GeoArea;
import org.apache.lucene.spatial3d.geom.GeoAreaFactory;
import org.apache.lucene.spatial3d.geom.GeoShape;
import org.apache.lucene.spatial3d.geom.PlanetModel.DocValueEncoder;
import org.apache.lucene.spatial3d.geom.XYZBounds;
import org.apache.lucene.util.DocIdSetBuilder;
import org.apache.lucene.util.NumericUtils;

class PointInShapeIntersectVisitor implements IntersectVisitor {
  private final DocIdSetBuilder hits;
  private final GeoShape shape;
  private final double minimumX;
  private final double maximumX;
  private final double minimumY;
  private final double maximumY;
  private final double minimumZ;
  private final double maximumZ;
  private DocIdSetBuilder.BulkAdder adder;

  public PointInShapeIntersectVisitor(DocIdSetBuilder hits, GeoShape shape, XYZBounds bounds) {
    this.hits = hits;
    this.shape = shape;
    DocValueEncoder docValueEncoder = shape.getPlanetModel().getDocValueEncoder();
    this.minimumX = docValueEncoder.roundDownX(bounds.getMinimumX());
    this.maximumX = docValueEncoder.roundUpX(bounds.getMaximumX());
    this.minimumY = docValueEncoder.roundDownY(bounds.getMinimumY());
    this.maximumY = docValueEncoder.roundUpY(bounds.getMaximumY());
    this.minimumZ = docValueEncoder.roundDownZ(bounds.getMinimumZ());
    this.maximumZ = docValueEncoder.roundUpZ(bounds.getMaximumZ());
  }

  @Override
  public void grow(int count) {
    adder = hits.grow(count);
  }

  @Override
  public void visit(int docID) {
    adder.add(docID);
  }

  @Override
  public void visit(DocIdSetIterator iterator) throws IOException {
    adder.add(iterator);
  }

  @Override
  public void visit(int docID, byte[] packedValue) {
    assert packedValue.length == 12;
    double x = Geo3DPoint.decodeDimension(packedValue, 0, shape.getPlanetModel());
    double y = Geo3DPoint.decodeDimension(packedValue, Integer.BYTES, shape.getPlanetModel());
    double z = Geo3DPoint.decodeDimension(packedValue, 2 * Integer.BYTES, shape.getPlanetModel());
    if (x >= minimumX
        && x <= maximumX
        && y >= minimumY
        && y <= maximumY
        && z >= minimumZ
        && z <= maximumZ) {
      if (shape.isWithin(x, y, z)) {
        adder.add(docID);
      }
    }
  }

  @Override
  public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
    // Because the dimensional format operates in quantized (64 bit -> 32 bit) space, and the cell
    // bounds here are inclusive, we need to extend the bounds to the largest un-quantized values
    // that could quantize into these bounds.  The encoding (Geo3DUtil.encodeValue) does
    // a Math.round from double to long, so e.g. 1.4 -> 1, and -1.4 -> -1:
    double xMin =
        Geo3DUtil.decodeValueFloor(
            NumericUtils.sortableBytesToInt(minPackedValue, 0), shape.getPlanetModel());
    double xMax =
        Geo3DUtil.decodeValueCeil(
            NumericUtils.sortableBytesToInt(maxPackedValue, 0), shape.getPlanetModel());
    double yMin =
        Geo3DUtil.decodeValueFloor(
            NumericUtils.sortableBytesToInt(minPackedValue, 1 * Integer.BYTES),
            shape.getPlanetModel());
    double yMax =
        Geo3DUtil.decodeValueCeil(
            NumericUtils.sortableBytesToInt(maxPackedValue, 1 * Integer.BYTES),
            shape.getPlanetModel());
    double zMin =
        Geo3DUtil.decodeValueFloor(
            NumericUtils.sortableBytesToInt(minPackedValue, 2 * Integer.BYTES),
            shape.getPlanetModel());
    double zMax =
        Geo3DUtil.decodeValueCeil(
            NumericUtils.sortableBytesToInt(maxPackedValue, 2 * Integer.BYTES),
            shape.getPlanetModel());

    // System.out.println("  compare: x=" + cellXMin + "-" + cellXMax + " y=" + cellYMin + "-" +
    // cellYMax + " z=" + cellZMin + "-" + cellZMax);
    assert xMin <= xMax;
    assert yMin <= yMax;
    assert zMin <= zMax;

    // First, check bounds.  If the shape is entirely contained, return CELL_CROSSES_QUERY.
    if (minimumX >= xMin
        && maximumX <= xMax
        && minimumY >= yMin
        && maximumY <= yMax
        && minimumZ >= zMin
        && maximumZ <= zMax) {
      return Relation.CELL_CROSSES_QUERY;
    }

    // Quick test failed so do slower one...
    GeoArea xyzSolid =
        GeoAreaFactory.makeGeoArea(shape.getPlanetModel(), xMin, xMax, yMin, yMax, zMin, zMax);

    switch (xyzSolid.getRelationship(shape)) {
      case GeoArea.CONTAINS:
        // Shape fully contains the cell
        // System.out.println("    inside");
        return Relation.CELL_INSIDE_QUERY;
      case GeoArea.OVERLAPS:
        // They do overlap but neither contains the other:
        // System.out.println("    crosses1");
        return Relation.CELL_CROSSES_QUERY;
      case GeoArea.WITHIN:
        // Cell fully contains the shape:
        // System.out.println("    crosses2");
        // return Relation.SHAPE_INSIDE_CELL;
        return Relation.CELL_CROSSES_QUERY;
      case GeoArea.DISJOINT:
        // They do not overlap at all
        // System.out.println("    outside");
        return Relation.CELL_OUTSIDE_QUERY;
      default:
        assert false;
        return Relation.CELL_CROSSES_QUERY;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy