blob: ae2fe83d20e720462a514f334bafca89136d4e45 [file] [log] [blame]
/*
* 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.spatial.prefix.tree;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.Shape;
import org.apache.lucene.util.BytesRef;
/**
* A spatial Prefix Tree, or Trie, which decomposes shapes into prefixed strings
* at variable lengths corresponding to variable precision. Each string
* corresponds to a rectangular spatial region. This approach is
* also referred to "Grids", "Tiles", and "Spatial Tiers".
* <p>
* Implementations of this class should be thread-safe and immutable once
* initialized.
*
* @lucene.experimental
*/
public abstract class SpatialPrefixTree {
protected final int maxLevels;
protected final SpatialContext ctx;
public SpatialPrefixTree(SpatialContext ctx, int maxLevels) {
assert maxLevels > 0;
this.ctx = ctx;
this.maxLevels = maxLevels;
}
public SpatialContext getSpatialContext() {
return ctx;
}
public int getMaxLevels() {
return maxLevels;
}
@Override
public String toString() {
return getClass().getSimpleName() + "(maxLevels:" + maxLevels + ",ctx:" + ctx + ")";
}
/**
* Returns the level of the largest grid in which its longest side is less
* than or equal to the provided distance (in degrees). Consequently {@code
* dist} acts as an error epsilon declaring the amount of detail needed in the
* grid, such that you can get a grid with just the right amount of
* precision.
*
* @param dist {@code >= 0}
* @return level [1 to maxLevels]
*/
public abstract int getLevelForDistance(double dist);
/**
* Given a cell having the specified level, returns the distance from opposite
* corners. Since this might vary depending on where the cell is, this method
* may over-estimate.
*
* @param level [1 to maxLevels]
* @return {@code > 0}
*/
public abstract double getDistanceForLevel(int level);
/**
* Returns the level 0 cell which encompasses all spatial data. Equivalent to {@link #readCell(BytesRef,Cell)}
* with no bytes.
*/
public abstract Cell getWorldCell(); //another possible name: getTopCell
/**
* This creates a new Cell (or re-using {@code scratch} if provided), initialized to the state as read
* by the bytes.
* Warning: An implementation may refer to the same byte array (no copy). If {@link Cell#setLeaf()} is
* subsequently called, it would then modify these bytes.
*/
public abstract Cell readCell(BytesRef term, Cell scratch);
/**
* Gets the intersecting cells for the specified shape, without exceeding
* detail level. If a cell is within the query shape then it's marked as a
* leaf and none of its children are added. For cells at detailLevel, they are marked as
* leaves too, unless it's a point.
* <p>
* IMPORTANT: Cells returned from the iterator can be re-used for cells at the same level. So you can't simply
* iterate to subsequent cells and still refer to the former cell nor the bytes returned from the former cell, unless
* you know the former cell is a parent.
*
* @param shape the shape; possibly null but the caller should liberally call
* {@code remove()} if so.
* @param detailLevel the maximum detail level to get cells for
* @return the matching cells
*/
public CellIterator getTreeCellIterator(Shape shape, int detailLevel) {
if (detailLevel > maxLevels) {
throw new IllegalArgumentException("detailLevel > maxLevels");
}
return new TreeCellIterator(shape, detailLevel, getWorldCell());
}
}