/* * 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 } }