/*
 * 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.document;

import java.util.Arrays;

import org.apache.lucene.document.ShapeField.QueryRelation;
import org.apache.lucene.geo.GeoEncodingUtils;
import org.apache.lucene.geo.Line;
import org.apache.lucene.geo.Line2D;
import org.apache.lucene.index.PointValues.Relation;
import org.apache.lucene.util.NumericUtils;

Finds all previously indexed geo shapes that intersect the specified arbitrary Line.

Note:

  • QueryRelation.WITHIN queries are not yet supported
  • Dateline crossing is not yet supported

todo:

  • Add distance support for buffered queries

The field must be indexed using LatLonShape.createIndexableFields added per document. @lucene.experimental

/** * Finds all previously indexed geo shapes that intersect the specified arbitrary {@code Line}. * <p> * Note: * <ul> * <li>{@code QueryRelation.WITHIN} queries are not yet supported</li> * <li>Dateline crossing is not yet supported</li> * </ul> * <p> * todo: * <ul> * <li>Add distance support for buffered queries</li> * </ul> * <p>The field must be indexed using * {@link org.apache.lucene.document.LatLonShape#createIndexableFields} added per document. * * @lucene.experimental **/
final class LatLonShapeLineQuery extends ShapeQuery { final Line[] lines; final private Line2D line2D; public LatLonShapeLineQuery(String field, QueryRelation queryRelation, Line... lines) { super(field, queryRelation); /** line queries do not support within relations, only intersects and disjoint */ if (queryRelation == QueryRelation.WITHIN) { throw new IllegalArgumentException("LatLonShapeLineQuery does not support " + QueryRelation.WITHIN + " queries"); } if (lines == null) { throw new IllegalArgumentException("lines must not be null"); } if (lines.length == 0) { throw new IllegalArgumentException("lines must not be empty"); } for (int i = 0; i < lines.length; ++i) { if (lines[i] == null) { throw new IllegalArgumentException("line[" + i + "] must not be null"); } else if (lines[i].minLon > lines[i].maxLon) { throw new IllegalArgumentException("LatLonShapeLineQuery does not currently support querying across dateline."); } } this.lines = lines.clone(); this.line2D = Line2D.create(lines); } @Override protected Relation relateRangeBBoxToQuery(int minXOffset, int minYOffset, byte[] minTriangle, int maxXOffset, int maxYOffset, byte[] maxTriangle) { double minLat = GeoEncodingUtils.decodeLatitude(NumericUtils.sortableBytesToInt(minTriangle, minYOffset)); double minLon = GeoEncodingUtils.decodeLongitude(NumericUtils.sortableBytesToInt(minTriangle, minXOffset)); double maxLat = GeoEncodingUtils.decodeLatitude(NumericUtils.sortableBytesToInt(maxTriangle, maxYOffset)); double maxLon = GeoEncodingUtils.decodeLongitude(NumericUtils.sortableBytesToInt(maxTriangle, maxXOffset)); // check internal node against query return line2D.relate(minLat, maxLat, minLon, maxLon); } @Override protected boolean queryMatches(byte[] t, int[] scratchTriangle, QueryRelation queryRelation) { ShapeField.decodeTriangle(t, scratchTriangle); double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle[0]); double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle[1]); double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle[2]); double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle[3]); double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle[4]); double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle[5]); if (queryRelation == QueryRelation.WITHIN) { return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY; } // INTERSECTS return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) != Relation.CELL_OUTSIDE_QUERY; } @Override public String toString(String field) { final StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(':'); if (this.field.equals(field) == false) { sb.append(" field="); sb.append(this.field); sb.append(':'); } sb.append("Line(").append(lines[0].toGeoJSON()).append(')'); return sb.toString(); } @Override protected boolean equalsTo(Object o) { return super.equalsTo(o) && Arrays.equals(lines, ((LatLonShapeLineQuery)o).lines); } @Override public int hashCode() { int hash = super.hashCode(); hash = 31 * hash + Arrays.hashCode(lines); return hash; } }