| package org.apache.lucene.spatial.bbox; |
| |
| /* |
| * 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. |
| */ |
| |
| import com.spatial4j.core.context.SpatialContext; |
| import com.spatial4j.core.shape.Point; |
| import com.spatial4j.core.shape.Rectangle; |
| import com.spatial4j.core.shape.Shape; |
| import org.apache.lucene.document.DoubleField; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.document.FieldType; |
| import org.apache.lucene.document.StringField; |
| import org.apache.lucene.index.Term; |
| import org.apache.lucene.queries.function.FunctionQuery; |
| import org.apache.lucene.queries.function.ValueSource; |
| import org.apache.lucene.search.BooleanClause; |
| import org.apache.lucene.search.BooleanQuery; |
| import org.apache.lucene.search.ConstantScoreQuery; |
| import org.apache.lucene.search.Filter; |
| import org.apache.lucene.search.NumericRangeQuery; |
| import org.apache.lucene.search.Query; |
| import org.apache.lucene.search.QueryWrapperFilter; |
| import org.apache.lucene.search.TermQuery; |
| import org.apache.lucene.spatial.SpatialStrategy; |
| import org.apache.lucene.spatial.query.SpatialArgs; |
| import org.apache.lucene.spatial.query.SpatialOperation; |
| import org.apache.lucene.spatial.query.UnsupportedSpatialOperation; |
| |
| |
| /** |
| * A SpatialStrategy for indexing and searching Rectangles by storing its |
| * coordinates in numeric fields. It supports all {@link SpatialOperation}s and |
| * has a custom overlap relevancy. It is based on GeoPortal's <a |
| * href="http://geoportal.svn.sourceforge.net/svnroot/geoportal/Geoportal/trunk/src/com/esri/gpt/catalog/lucene/SpatialClauseAdapter.java">SpatialClauseAdapter</a>. |
| * |
| * <h4>Characteristics:</h4> |
| * <ul> |
| * <li>Only indexes Rectangles; just one per field value.</li> |
| * <li>Can query only by a Rectangle.</li> |
| * <li>Supports all {@link SpatialOperation}s.</li> |
| * <li>Uses the FieldCache for any sorting / relevancy.</li> |
| * </ul> |
| * |
| * <h4>Implementation:</h4> |
| * This uses 4 double fields for minX, maxX, minY, maxY |
| * and a boolean to mark a dateline cross. Depending on the particular {@link |
| * SpatialOperation}s, there is a variety of {@link NumericRangeQuery}s to be |
| * done. |
| * The {@link #makeBBoxAreaSimilarityValueSource(com.spatial4j.core.shape.Rectangle)} |
| * works by calculating the query bbox overlap percentage against the indexed |
| * shape overlap percentage. The indexed shape's coordinates are retrieved from |
| * the {@link org.apache.lucene.search.FieldCache}. |
| * |
| * @lucene.experimental |
| */ |
| public class BBoxStrategy extends SpatialStrategy { |
| |
| public static final String SUFFIX_MINX = "__minX"; |
| public static final String SUFFIX_MAXX = "__maxX"; |
| public static final String SUFFIX_MINY = "__minY"; |
| public static final String SUFFIX_MAXY = "__maxY"; |
| public static final String SUFFIX_XDL = "__xdl"; |
| |
| /* |
| * The Bounding Box gets stored as four fields for x/y min/max and a flag |
| * that says if the box crosses the dateline (xdl). |
| */ |
| public final String field_bbox; |
| public final String field_minX; |
| public final String field_minY; |
| public final String field_maxX; |
| public final String field_maxY; |
| public final String field_xdl; // crosses dateline |
| |
| public double queryPower = 1.0; |
| public double targetPower = 1.0f; |
| public int precisionStep = 8; // same as solr default |
| |
| public BBoxStrategy(SpatialContext ctx, String fieldNamePrefix) { |
| super(ctx, fieldNamePrefix); |
| field_bbox = fieldNamePrefix; |
| field_minX = fieldNamePrefix + SUFFIX_MINX; |
| field_maxX = fieldNamePrefix + SUFFIX_MAXX; |
| field_minY = fieldNamePrefix + SUFFIX_MINY; |
| field_maxY = fieldNamePrefix + SUFFIX_MAXY; |
| field_xdl = fieldNamePrefix + SUFFIX_XDL; |
| } |
| |
| public void setPrecisionStep( int p ) { |
| precisionStep = p; |
| if (precisionStep<=0 || precisionStep>=64) |
| precisionStep=Integer.MAX_VALUE; |
| } |
| |
| //--------------------------------- |
| // Indexing |
| //--------------------------------- |
| |
| @Override |
| public Field[] createIndexableFields(Shape shape) { |
| if (shape instanceof Rectangle) |
| return createIndexableFields((Rectangle)shape); |
| throw new UnsupportedOperationException("Can only index Rectangle, not " + shape); |
| } |
| |
| public Field[] createIndexableFields(Rectangle bbox) { |
| FieldType doubleFieldType = new FieldType(DoubleField.TYPE_NOT_STORED); |
| doubleFieldType.setNumericPrecisionStep(precisionStep); |
| Field[] fields = new Field[5]; |
| fields[0] = new DoubleField(field_minX, bbox.getMinX(), doubleFieldType); |
| fields[1] = new DoubleField(field_maxX, bbox.getMaxX(), doubleFieldType); |
| fields[2] = new DoubleField(field_minY, bbox.getMinY(), doubleFieldType); |
| fields[3] = new DoubleField(field_maxY, bbox.getMaxY(), doubleFieldType); |
| fields[4] = new Field( field_xdl, bbox.getCrossesDateLine()?"T":"F", StringField.TYPE_NOT_STORED); |
| return fields; |
| } |
| |
| //--------------------------------- |
| // Query Builder |
| //--------------------------------- |
| |
| @Override |
| public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) { |
| return new BBoxSimilarityValueSource( |
| this, new DistanceSimilarity(this.getSpatialContext(), queryPoint, multiplier)); |
| } |
| |
| public ValueSource makeBBoxAreaSimilarityValueSource(Rectangle queryBox) { |
| return new BBoxSimilarityValueSource( |
| this, new AreaSimilarity(queryBox, queryPower, targetPower)); |
| } |
| |
| @Override |
| public Filter makeFilter(SpatialArgs args) { |
| return new QueryWrapperFilter(makeSpatialQuery(args)); |
| } |
| |
| @Override |
| public ConstantScoreQuery makeQuery(SpatialArgs args) { |
| return new ConstantScoreQuery(makeSpatialQuery(args)); |
| } |
| |
| public Query makeQueryWithValueSource(SpatialArgs args, ValueSource valueSource) { |
| BooleanQuery bq = new BooleanQuery(); |
| Query spatial = makeSpatialQuery(args); |
| bq.add(new ConstantScoreQuery(spatial), BooleanClause.Occur.MUST); |
| |
| // This part does the scoring |
| Query spatialRankingQuery = new FunctionQuery(valueSource); |
| bq.add(spatialRankingQuery, BooleanClause.Occur.MUST); |
| return bq; |
| } |
| |
| |
| private Query makeSpatialQuery(SpatialArgs args) { |
| Shape shape = args.getShape(); |
| if (!(shape instanceof Rectangle)) |
| throw new UnsupportedOperationException("Can only query by Rectangle, not " + shape); |
| |
| Rectangle bbox = (Rectangle) shape; |
| Query spatial = null; |
| |
| // Useful for understanding Relations: |
| // http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm |
| SpatialOperation op = args.getOperation(); |
| if( op == SpatialOperation.BBoxIntersects ) spatial = makeIntersects(bbox); |
| else if( op == SpatialOperation.BBoxWithin ) spatial = makeWithin(bbox); |
| else if( op == SpatialOperation.Contains ) spatial = makeContains(bbox); |
| else if( op == SpatialOperation.Intersects ) spatial = makeIntersects(bbox); |
| else if( op == SpatialOperation.IsEqualTo ) spatial = makeEquals(bbox); |
| else if( op == SpatialOperation.IsDisjointTo ) spatial = makeDisjoint(bbox); |
| else if( op == SpatialOperation.IsWithin ) spatial = makeWithin(bbox); |
| else if( op == SpatialOperation.Overlaps ) spatial = makeIntersects(bbox); |
| else { |
| throw new UnsupportedSpatialOperation(op); |
| } |
| return spatial; |
| } |
| |
| |
| //------------------------------------------------------------------------------- |
| // |
| //------------------------------------------------------------------------------- |
| |
| /** |
| * Constructs a query to retrieve documents that fully contain the input envelope. |
| * |
| * @return the spatial query |
| */ |
| Query makeContains(Rectangle bbox) { |
| |
| // general case |
| // docMinX <= queryExtent.getMinX() AND docMinY <= queryExtent.getMinY() AND docMaxX >= queryExtent.getMaxX() AND docMaxY >= queryExtent.getMaxY() |
| |
| // Y conditions |
| // docMinY <= queryExtent.getMinY() AND docMaxY >= queryExtent.getMaxY() |
| Query qMinY = NumericRangeQuery.newDoubleRange(field_minY, precisionStep, null, bbox.getMinY(), false, true); |
| Query qMaxY = NumericRangeQuery.newDoubleRange(field_maxY, precisionStep, bbox.getMaxY(), null, true, false); |
| Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.MUST); |
| |
| // X conditions |
| Query xConditions = null; |
| |
| // queries that do not cross the date line |
| if (!bbox.getCrossesDateLine()) { |
| |
| // X Conditions for documents that do not cross the date line, |
| // documents that contain the min X and max X of the query envelope, |
| // docMinX <= queryExtent.getMinX() AND docMaxX >= queryExtent.getMaxX() |
| Query qMinX = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, null, bbox.getMinX(), false, true); |
| Query qMaxX = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, bbox.getMaxX(), null, true, false); |
| Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.MUST); |
| Query qNonXDL = this.makeXDL(false, qMinMax); |
| |
| // X Conditions for documents that cross the date line, |
| // the left portion of the document contains the min X of the query |
| // OR the right portion of the document contains the max X of the query, |
| // docMinXLeft <= queryExtent.getMinX() OR docMaxXRight >= queryExtent.getMaxX() |
| Query qXDLLeft = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, null, bbox.getMinX(), false, true); |
| Query qXDLRight = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, bbox.getMaxX(), null, true, false); |
| Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.SHOULD); |
| Query qXDL = this.makeXDL(true, qXDLLeftRight); |
| |
| // apply the non-XDL and XDL conditions |
| xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD); |
| |
| // queries that cross the date line |
| } else { |
| |
| // No need to search for documents that do not cross the date line |
| |
| // X Conditions for documents that cross the date line, |
| // the left portion of the document contains the min X of the query |
| // AND the right portion of the document contains the max X of the query, |
| // docMinXLeft <= queryExtent.getMinX() AND docMaxXRight >= queryExtent.getMaxX() |
| Query qXDLLeft = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, null, bbox.getMinX(), false, true); |
| Query qXDLRight = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, bbox.getMaxX(), null, true, false); |
| Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.MUST); |
| |
| xConditions = this.makeXDL(true, qXDLLeftRight); |
| } |
| |
| // both X and Y conditions must occur |
| return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.MUST); |
| } |
| |
| /** |
| * Constructs a query to retrieve documents that are disjoint to the input envelope. |
| * |
| * @return the spatial query |
| */ |
| Query makeDisjoint(Rectangle bbox) { |
| |
| // general case |
| // docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX() OR docMinY > queryExtent.getMaxY() OR docMaxY < queryExtent.getMinY() |
| |
| // Y conditions |
| // docMinY > queryExtent.getMaxY() OR docMaxY < queryExtent.getMinY() |
| Query qMinY = NumericRangeQuery.newDoubleRange(field_minY, precisionStep, bbox.getMaxY(), null, false, false); |
| Query qMaxY = NumericRangeQuery.newDoubleRange(field_maxY, precisionStep, null, bbox.getMinY(), false, false); |
| Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.SHOULD); |
| |
| // X conditions |
| Query xConditions = null; |
| |
| // queries that do not cross the date line |
| if (!bbox.getCrossesDateLine()) { |
| |
| // X Conditions for documents that do not cross the date line, |
| // docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX() |
| Query qMinX = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, bbox.getMaxX(), null, false, false); |
| Query qMaxX = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, bbox.getMinX(), false, false); |
| Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.SHOULD); |
| Query qNonXDL = this.makeXDL(false, qMinMax); |
| |
| // X Conditions for documents that cross the date line, |
| // both the left and right portions of the document must be disjoint to the query |
| // (docMinXLeft > queryExtent.getMaxX() OR docMaxXLeft < queryExtent.getMinX()) AND |
| // (docMinXRight > queryExtent.getMaxX() OR docMaxXRight < queryExtent.getMinX()) |
| // where: docMaxXLeft = 180.0, docMinXRight = -180.0 |
| // (docMaxXLeft < queryExtent.getMinX()) equates to (180.0 < queryExtent.getMinX()) and is ignored |
| // (docMinXRight > queryExtent.getMaxX()) equates to (-180.0 > queryExtent.getMaxX()) and is ignored |
| Query qMinXLeft = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, bbox.getMaxX(), null, false, false); |
| Query qMaxXRight = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, bbox.getMinX(), false, false); |
| Query qLeftRight = this.makeQuery(new Query[]{qMinXLeft, qMaxXRight}, BooleanClause.Occur.MUST); |
| Query qXDL = this.makeXDL(true, qLeftRight); |
| |
| // apply the non-XDL and XDL conditions |
| xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD); |
| |
| // queries that cross the date line |
| } else { |
| |
| // X Conditions for documents that do not cross the date line, |
| // the document must be disjoint to both the left and right query portions |
| // (docMinX > queryExtent.getMaxX()Left OR docMaxX < queryExtent.getMinX()) AND (docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX()Left) |
| // where: queryExtent.getMaxX()Left = 180.0, queryExtent.getMinX()Left = -180.0 |
| Query qMinXLeft = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, 180.0, null, false, false); |
| Query qMaxXLeft = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, bbox.getMinX(), false, false); |
| Query qMinXRight = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, bbox.getMaxX(), null, false, false); |
| Query qMaxXRight = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, -180.0, false, false); |
| Query qLeft = this.makeQuery(new Query[]{qMinXLeft, qMaxXLeft}, BooleanClause.Occur.SHOULD); |
| Query qRight = this.makeQuery(new Query[]{qMinXRight, qMaxXRight}, BooleanClause.Occur.SHOULD); |
| Query qLeftRight = this.makeQuery(new Query[]{qLeft, qRight}, BooleanClause.Occur.MUST); |
| |
| // No need to search for documents that do not cross the date line |
| |
| xConditions = this.makeXDL(false, qLeftRight); |
| } |
| |
| // either X or Y conditions should occur |
| return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.SHOULD); |
| } |
| |
| /** |
| * Constructs a query to retrieve documents that equal the input envelope. |
| * |
| * @return the spatial query |
| */ |
| Query makeEquals(Rectangle bbox) { |
| |
| // docMinX = queryExtent.getMinX() AND docMinY = queryExtent.getMinY() AND docMaxX = queryExtent.getMaxX() AND docMaxY = queryExtent.getMaxY() |
| Query qMinX = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, bbox.getMinX(), bbox.getMinX(), true, true); |
| Query qMinY = NumericRangeQuery.newDoubleRange(field_minY, precisionStep, bbox.getMinY(), bbox.getMinY(), true, true); |
| Query qMaxX = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, bbox.getMaxX(), bbox.getMaxX(), true, true); |
| Query qMaxY = NumericRangeQuery.newDoubleRange(field_maxY, precisionStep, bbox.getMaxY(), bbox.getMaxY(), true, true); |
| BooleanQuery bq = new BooleanQuery(); |
| bq.add(qMinX, BooleanClause.Occur.MUST); |
| bq.add(qMinY, BooleanClause.Occur.MUST); |
| bq.add(qMaxX, BooleanClause.Occur.MUST); |
| bq.add(qMaxY, BooleanClause.Occur.MUST); |
| return bq; |
| } |
| |
| /** |
| * Constructs a query to retrieve documents that intersect the input envelope. |
| * |
| * @return the spatial query |
| */ |
| Query makeIntersects(Rectangle bbox) { |
| |
| // the original intersects query does not work for envelopes that cross the date line, |
| // switch to a NOT Disjoint query |
| |
| // MUST_NOT causes a problem when it's the only clause type within a BooleanQuery, |
| // to get round it we add all documents as a SHOULD |
| |
| // there must be an envelope, it must not be disjoint |
| Query qDisjoint = makeDisjoint(bbox); |
| Query qIsNonXDL = this.makeXDL(false); |
| Query qIsXDL = this.makeXDL(true); |
| Query qHasEnv = this.makeQuery(new Query[]{qIsNonXDL, qIsXDL}, BooleanClause.Occur.SHOULD); |
| BooleanQuery qNotDisjoint = new BooleanQuery(); |
| qNotDisjoint.add(qHasEnv, BooleanClause.Occur.MUST); |
| qNotDisjoint.add(qDisjoint, BooleanClause.Occur.MUST_NOT); |
| |
| //Query qDisjoint = makeDisjoint(); |
| //BooleanQuery qNotDisjoint = new BooleanQuery(); |
| //qNotDisjoint.add(new MatchAllDocsQuery(),BooleanClause.Occur.SHOULD); |
| //qNotDisjoint.add(qDisjoint,BooleanClause.Occur.MUST_NOT); |
| return qNotDisjoint; |
| } |
| |
| /** |
| * Makes a boolean query based upon a collection of queries and a logical operator. |
| * |
| * @param queries the query collection |
| * @param occur the logical operator |
| * @return the query |
| */ |
| BooleanQuery makeQuery(Query[] queries, BooleanClause.Occur occur) { |
| BooleanQuery bq = new BooleanQuery(); |
| for (Query query : queries) { |
| bq.add(query, occur); |
| } |
| return bq; |
| } |
| |
| /** |
| * Constructs a query to retrieve documents are fully within the input envelope. |
| * |
| * @return the spatial query |
| */ |
| Query makeWithin(Rectangle bbox) { |
| |
| // general case |
| // docMinX >= queryExtent.getMinX() AND docMinY >= queryExtent.getMinY() AND docMaxX <= queryExtent.getMaxX() AND docMaxY <= queryExtent.getMaxY() |
| |
| // Y conditions |
| // docMinY >= queryExtent.getMinY() AND docMaxY <= queryExtent.getMaxY() |
| Query qMinY = NumericRangeQuery.newDoubleRange(field_minY, precisionStep, bbox.getMinY(), null, true, false); |
| Query qMaxY = NumericRangeQuery.newDoubleRange(field_maxY, precisionStep, null, bbox.getMaxY(), false, true); |
| Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.MUST); |
| |
| // X conditions |
| Query xConditions = null; |
| |
| // X Conditions for documents that cross the date line, |
| // the left portion of the document must be within the left portion of the query, |
| // AND the right portion of the document must be within the right portion of the query |
| // docMinXLeft >= queryExtent.getMinX() AND docMaxXLeft <= 180.0 |
| // AND docMinXRight >= -180.0 AND docMaxXRight <= queryExtent.getMaxX() |
| Query qXDLLeft = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, bbox.getMinX(), null, true, false); |
| Query qXDLRight = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, bbox.getMaxX(), false, true); |
| Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.MUST); |
| Query qXDL = this.makeXDL(true, qXDLLeftRight); |
| |
| // queries that do not cross the date line |
| if (!bbox.getCrossesDateLine()) { |
| |
| // X Conditions for documents that do not cross the date line, |
| // docMinX >= queryExtent.getMinX() AND docMaxX <= queryExtent.getMaxX() |
| Query qMinX = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, bbox.getMinX(), null, true, false); |
| Query qMaxX = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, bbox.getMaxX(), false, true); |
| Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.MUST); |
| Query qNonXDL = this.makeXDL(false, qMinMax); |
| |
| // apply the non-XDL or XDL X conditions |
| if ((bbox.getMinX() <= -180.0) && bbox.getMaxX() >= 180.0) { |
| xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD); |
| } else { |
| xConditions = qNonXDL; |
| } |
| |
| // queries that cross the date line |
| } else { |
| |
| // X Conditions for documents that do not cross the date line |
| |
| // the document should be within the left portion of the query |
| // docMinX >= queryExtent.getMinX() AND docMaxX <= 180.0 |
| Query qMinXLeft = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, bbox.getMinX(), null, true, false); |
| Query qMaxXLeft = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, 180.0, false, true); |
| Query qLeft = this.makeQuery(new Query[]{qMinXLeft, qMaxXLeft}, BooleanClause.Occur.MUST); |
| |
| // the document should be within the right portion of the query |
| // docMinX >= -180.0 AND docMaxX <= queryExtent.getMaxX() |
| Query qMinXRight = NumericRangeQuery.newDoubleRange(field_minX, precisionStep, -180.0, null, true, false); |
| Query qMaxXRight = NumericRangeQuery.newDoubleRange(field_maxX, precisionStep, null, bbox.getMaxX(), false, true); |
| Query qRight = this.makeQuery(new Query[]{qMinXRight, qMaxXRight}, BooleanClause.Occur.MUST); |
| |
| // either left or right conditions should occur, |
| // apply the left and right conditions to documents that do not cross the date line |
| Query qLeftRight = this.makeQuery(new Query[]{qLeft, qRight}, BooleanClause.Occur.SHOULD); |
| Query qNonXDL = this.makeXDL(false, qLeftRight); |
| |
| // apply the non-XDL and XDL conditions |
| xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD); |
| } |
| |
| // both X and Y conditions must occur |
| return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.MUST); |
| } |
| |
| /** |
| * Constructs a query to retrieve documents that do or do not cross the date line. |
| * |
| * |
| * @param crossedDateLine <code>true</true> for documents that cross the date line |
| * @return the query |
| */ |
| Query makeXDL(boolean crossedDateLine) { |
| // The 'T' and 'F' values match solr fields |
| return new TermQuery(new Term(field_xdl, crossedDateLine ? "T" : "F")); |
| } |
| |
| /** |
| * Constructs a query to retrieve documents that do or do not cross the date line |
| * and match the supplied spatial query. |
| * |
| * @param crossedDateLine <code>true</true> for documents that cross the date line |
| * @param query the spatial query |
| * @return the query |
| */ |
| Query makeXDL(boolean crossedDateLine, Query query) { |
| BooleanQuery bq = new BooleanQuery(); |
| bq.add(this.makeXDL(crossedDateLine), BooleanClause.Occur.MUST); |
| bq.add(query, BooleanClause.Occur.MUST); |
| return bq; |
| } |
| } |
| |
| |
| |