blob: 1a3afcc8740e0bb8b7cb644e8aa8547926c61fd3 [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 java.util.Arrays;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
import org.apache.lucene.util.BytesRef;
/** The base for the original two SPT's: Geohash and Quad. Don't subclass this for new SPTs.
* @lucene.internal */
abstract class LegacyPrefixTree extends SpatialPrefixTree {
public LegacyPrefixTree(SpatialContext ctx, int maxLevels) {
super(ctx, maxLevels);
}
public double getDistanceForLevel(int level) {
if (level < 1 || level > getMaxLevels())
throw new IllegalArgumentException("Level must be in 1 to maxLevels range");
//TODO cache for each level
Cell cell = getCell(ctx.getWorldBounds().getCenter(), level);
Rectangle bbox = cell.getShape().getBoundingBox();
double width = bbox.getWidth();
double height = bbox.getHeight();
//Use standard cartesian hypotenuse. For geospatial, this answer is larger
// than the correct one but it's okay to over-estimate.
return Math.sqrt(width * width + height * height);
}
/**
* Returns the cell containing point {@code p} at the specified {@code level}.
*/
protected abstract Cell getCell(Point p, int level);
@Override
public Cell readCell(BytesRef term, Cell scratch) {
LegacyCell cell = (LegacyCell) scratch;
if (cell == null)
cell = (LegacyCell) getWorldCell();
cell.readCell(term);
return cell;
}
@Override
public CellIterator getTreeCellIterator(Shape shape, int detailLevel) {
if (!(shape instanceof Point))
return super.getTreeCellIterator(shape, detailLevel);
//This specialization is here because the legacy implementations don't have a fast implementation of
// cell.getSubCells(point). It's fastest here to encode the full bytes for detailLevel, and create
// subcells from the bytesRef in a loop. This avoids an O(N^2) encode, and we have O(N) instead.
Cell cell = getCell((Point) shape, detailLevel);
assert cell instanceof LegacyCell;
BytesRef fullBytes = cell.getTokenBytesNoLeaf(null);
//fill in reverse order to be sorted
Cell[] cells = new Cell[detailLevel];
for (int i = 1; i < detailLevel; i++) {
fullBytes.length = i;
Cell parentCell = readCell(fullBytes, null);
cells[i-1] = parentCell;
}
cells[detailLevel-1] = cell;
return new FilterCellIterator(Arrays.asList(cells).iterator(), null);//null filter
}
}