use of org.locationtech.spatial4j.context.SpatialContext in project lucene-solr by apache.
the class TestTermQueryPrefixGridStrategy method testNGramPrefixGridLosAngeles.
@Test
public void testNGramPrefixGridLosAngeles() throws IOException {
SpatialContext ctx = SpatialContext.GEO;
TermQueryPrefixTreeStrategy prefixGridStrategy = new TermQueryPrefixTreeStrategy(new QuadPrefixTree(ctx), "geo");
Shape point = ctx.makePoint(-118.243680, 34.052230);
Document losAngeles = new Document();
losAngeles.add(new StringField("name", "Los Angeles", Field.Store.YES));
for (Field field : prefixGridStrategy.createIndexableFields(point)) {
losAngeles.add(field);
}
//just for diagnostics
losAngeles.add(new StoredField(prefixGridStrategy.getFieldName(), point.toString()));
addDocumentsAndCommit(Arrays.asList(losAngeles));
// This won't work with simple spatial context...
SpatialArgsParser spatialArgsParser = new SpatialArgsParser();
// TODO... use a non polygon query
// SpatialArgs spatialArgs = spatialArgsParser.parse(
// "Intersects(POLYGON((-127.00390625 39.8125,-112.765625 39.98828125,-111.53515625 31.375,-125.94921875 30.14453125,-127.00390625 39.8125)))",
// new SimpleSpatialContext());
// Query query = prefixGridStrategy.makeQuery(spatialArgs, fieldInfo);
// SearchResults searchResults = executeQuery(query, 1);
// assertEquals(1, searchResults.numFound);
}
use of org.locationtech.spatial4j.context.SpatialContext in project lucene-solr by apache.
the class TestTestFramework method testQueries.
@Test
public void testQueries() throws IOException {
String name = StrategyTestCase.QTEST_Cities_Intersects_BBox;
InputStream in = getClass().getClassLoader().getResourceAsStream(name);
SpatialContext ctx = SpatialContext.GEO;
Iterator<SpatialTestQuery> iter = SpatialTestQuery.getTestQueries(new SpatialArgsParser(), ctx, name, //closes the InputStream
in);
List<SpatialTestQuery> tests = new ArrayList<>();
while (iter.hasNext()) {
tests.add(iter.next());
}
Assert.assertEquals(3, tests.size());
SpatialTestQuery sf = tests.get(0);
// assert
assertEquals(1, sf.ids.size());
Assert.assertTrue(sf.ids.get(0).equals("G5391959"));
Assert.assertTrue(sf.args.getShape() instanceof Rectangle);
assertEquals(SpatialOperation.Intersects, sf.args.getOperation());
}
use of org.locationtech.spatial4j.context.SpatialContext in project lucene-solr by apache.
the class DistanceStrategyTest method parameters.
@ParametersFactory(argumentFormatting = "strategy=%s")
public static Iterable<Object[]> parameters() {
List<Object[]> ctorArgs = new ArrayList<>();
SpatialContext ctx = SpatialContext.GEO;
SpatialPrefixTree grid;
SpatialStrategy strategy;
grid = new QuadPrefixTree(ctx, 25);
strategy = new RecursivePrefixTreeStrategy(grid, "recursive_quad");
ctorArgs.add(new Object[] { strategy.getFieldName(), strategy });
grid = new GeohashPrefixTree(ctx, 12);
strategy = new TermQueryPrefixTreeStrategy(grid, "termquery_geohash");
ctorArgs.add(new Object[] { strategy.getFieldName(), strategy });
grid = new PackedQuadPrefixTree(ctx, 25);
strategy = new RecursivePrefixTreeStrategy(grid, "recursive_packedquad");
ctorArgs.add(new Object[] { strategy.getFieldName(), strategy });
strategy = PointVectorStrategy.newInstance(ctx, "pointvector");
ctorArgs.add(new Object[] { strategy.getFieldName(), strategy });
// Can't test this without un-inverting since PVS legacy config didn't have docValues.
// However, note that Solr's tests use UninvertingReader and thus test this.
// strategy = PointVectorStrategy.newLegacyInstance(ctx, "pointvector_legacy");
// ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
strategy = BBoxStrategy.newInstance(ctx, "bbox");
ctorArgs.add(new Object[] { strategy.getFieldName(), strategy });
strategy = new SerializedDVStrategy(ctx, "serialized");
ctorArgs.add(new Object[] { strategy.getFieldName(), strategy });
return ctorArgs;
}
use of org.locationtech.spatial4j.context.SpatialContext in project lucene-solr by apache.
the class HeatmapFacetCounter method calcFacets.
/**
* Calculates spatial 2D facets (aggregated counts) in a grid, sometimes called a heatmap.
* Facet computation is implemented by navigating the underlying indexed terms efficiently. If you don't know exactly
* what facetLevel to go to for a given input box but you have some sense of how many cells there should be relative
* to the size of the shape, then consider using the logic that {@link org.apache.lucene.spatial.prefix.PrefixTreeStrategy}
* uses when approximating what level to go to when indexing a shape given a distErrPct.
*
* @param context the IndexReader's context
* @param topAcceptDocs a Bits to limit counted docs. If null, live docs are counted.
* @param inputShape the shape to gather grid squares for; typically a {@link Rectangle}.
* The <em>actual</em> heatmap area will usually be larger since the cells on the edge that overlap
* are returned. We always return a rectangle of integers even if the inputShape isn't a rectangle
* -- the non-intersecting cells will all be 0.
* If null is given, the entire world is assumed.
* @param facetLevel the target depth (detail) of cells.
* @param maxCells the maximum number of cells to return. If the cells exceed this count, an
*/
public static Heatmap calcFacets(PrefixTreeStrategy strategy, IndexReaderContext context, Bits topAcceptDocs, Shape inputShape, final int facetLevel, int maxCells) throws IOException {
if (maxCells > (MAX_ROWS_OR_COLUMNS * MAX_ROWS_OR_COLUMNS)) {
throw new IllegalArgumentException("maxCells (" + maxCells + ") should be <= " + MAX_ROWS_OR_COLUMNS);
}
if (inputShape == null) {
inputShape = strategy.getSpatialContext().getWorldBounds();
}
final Rectangle inputRect = inputShape.getBoundingBox();
//First get the rect of the cell at the bottom-left at depth facetLevel
final SpatialPrefixTree grid = strategy.getGrid();
final SpatialContext ctx = grid.getSpatialContext();
final Point cornerPt = ctx.makePoint(inputRect.getMinX(), inputRect.getMinY());
final CellIterator cellIterator = grid.getTreeCellIterator(cornerPt, facetLevel);
Cell cornerCell = null;
while (cellIterator.hasNext()) {
cornerCell = cellIterator.next();
}
assert cornerCell != null && cornerCell.getLevel() == facetLevel : "Cell not at target level: " + cornerCell;
final Rectangle cornerRect = (Rectangle) cornerCell.getShape();
assert cornerRect.hasArea();
//Now calculate the number of columns and rows necessary to cover the inputRect
//note: we might change this below...
double heatMinX = cornerRect.getMinX();
final double cellWidth = cornerRect.getWidth();
final Rectangle worldRect = ctx.getWorldBounds();
final int columns = calcRowsOrCols(cellWidth, heatMinX, inputRect.getWidth(), inputRect.getMinX(), worldRect.getWidth());
final double heatMinY = cornerRect.getMinY();
final double cellHeight = cornerRect.getHeight();
final int rows = calcRowsOrCols(cellHeight, heatMinY, inputRect.getHeight(), inputRect.getMinY(), worldRect.getHeight());
assert rows > 0 && columns > 0;
if (columns > MAX_ROWS_OR_COLUMNS || rows > MAX_ROWS_OR_COLUMNS || columns * rows > maxCells) {
throw new IllegalArgumentException("Too many cells (" + columns + " x " + rows + ") for level " + facetLevel + " shape " + inputRect);
}
//Create resulting heatmap bounding rectangle & Heatmap object.
final double halfCellWidth = cellWidth / 2.0;
// if X world-wraps, use world bounds' range
if (columns * cellWidth + halfCellWidth > worldRect.getWidth()) {
heatMinX = worldRect.getMinX();
}
double heatMaxX = heatMinX + columns * cellWidth;
if (Math.abs(heatMaxX - worldRect.getMaxX()) < halfCellWidth) {
//numeric conditioning issue
heatMaxX = worldRect.getMaxX();
} else if (heatMaxX > worldRect.getMaxX()) {
//wraps dateline (won't happen if !geo)
heatMaxX = heatMaxX - worldRect.getMaxX() + worldRect.getMinX();
}
final double halfCellHeight = cellHeight / 2.0;
double heatMaxY = heatMinY + rows * cellHeight;
if (Math.abs(heatMaxY - worldRect.getMaxY()) < halfCellHeight) {
//numeric conditioning issue
heatMaxY = worldRect.getMaxY();
}
final Heatmap heatmap = new Heatmap(columns, rows, ctx.makeRectangle(heatMinX, heatMaxX, heatMinY, heatMaxY));
if (topAcceptDocs instanceof Bits.MatchNoBits) {
// short-circuit
return heatmap;
}
//All ancestor cell counts (of facetLevel) will be captured during facet visiting and applied later. If the data is
// just points then there won't be any ancestors.
//Facet count of ancestors covering all of the heatmap:
// single-element array so it can be accumulated in the inner class
int[] allCellsAncestorCount = new int[1];
//All other ancestors:
Map<Rectangle, Integer> ancestors = new HashMap<>();
//Now lets count some facets!
PrefixTreeFacetCounter.compute(strategy, context, topAcceptDocs, inputShape, facetLevel, new PrefixTreeFacetCounter.FacetVisitor() {
@Override
public void visit(Cell cell, int count) {
final double heatMinX = heatmap.region.getMinX();
final Rectangle rect = (Rectangle) cell.getShape();
if (cell.getLevel() == facetLevel) {
//heatmap level; count it directly
//convert to col & row
int column;
if (rect.getMinX() >= heatMinX) {
column = (int) Math.round((rect.getMinX() - heatMinX) / cellWidth);
} else {
// due to dateline wrap
column = (int) Math.round((rect.getMinX() + 360 - heatMinX) / cellWidth);
}
int row = (int) Math.round((rect.getMinY() - heatMinY) / cellHeight);
// allows adjacent cells to overlap on the seam), so we need to skip them
if (column < 0 || column >= heatmap.columns || row < 0 || row >= heatmap.rows) {
return;
}
// increment
heatmap.counts[column * heatmap.rows + row] += count;
} else if (rect.relate(heatmap.region) == SpatialRelation.CONTAINS) {
//containing ancestor
allCellsAncestorCount[0] += count;
} else {
// ancestor
// note: not particularly efficient (possible put twice, and Integer wrapper); oh well
Integer existingCount = ancestors.put(rect, count);
if (existingCount != null) {
ancestors.put(rect, count + existingCount);
}
}
}
});
// Apply allCellsAncestorCount
if (allCellsAncestorCount[0] > 0) {
for (int i = 0; i < heatmap.counts.length; i++) {
heatmap.counts[i] += allCellsAncestorCount[0];
}
}
// Apply ancestors
// note: This approach isn't optimized for a ton of ancestor cells. We'll potentially increment the same cells
// multiple times in separate passes if any ancestors overlap. IF this poses a problem, we could optimize it
// with additional complication by keeping track of intervals in a sorted tree structure (possible TreeMap/Set)
// and iterate them cleverly such that we just make one pass at this stage.
//output of intersectInterval
int[] pair = new int[2];
for (Map.Entry<Rectangle, Integer> entry : ancestors.entrySet()) {
// from a cell (thus doesn't cross DL)
Rectangle rect = entry.getKey();
final int count = entry.getValue();
//note: we approach this in a way that eliminates int overflow/underflow (think huge cell, tiny heatmap)
intersectInterval(heatMinY, heatMaxY, cellHeight, rows, rect.getMinY(), rect.getMaxY(), pair);
final int startRow = pair[0];
final int endRow = pair[1];
if (!heatmap.region.getCrossesDateLine()) {
intersectInterval(heatMinX, heatMaxX, cellWidth, columns, rect.getMinX(), rect.getMaxX(), pair);
final int startCol = pair[0];
final int endCol = pair[1];
incrementRange(heatmap, startCol, endCol, startRow, endRow, count);
} else {
// note: the cell rect might intersect 2 disjoint parts of the heatmap, so we do the left & right separately
final int leftColumns = (int) Math.round((180 - heatMinX) / cellWidth);
final int rightColumns = heatmap.columns - leftColumns;
//left half of dateline:
if (rect.getMaxX() > heatMinX) {
intersectInterval(heatMinX, 180, cellWidth, leftColumns, rect.getMinX(), rect.getMaxX(), pair);
final int startCol = pair[0];
final int endCol = pair[1];
incrementRange(heatmap, startCol, endCol, startRow, endRow, count);
}
//right half of dateline
if (rect.getMinX() < heatMaxX) {
intersectInterval(-180, heatMaxX, cellWidth, rightColumns, rect.getMinX(), rect.getMaxX(), pair);
final int startCol = pair[0] + leftColumns;
final int endCol = pair[1] + leftColumns;
incrementRange(heatmap, startCol, endCol, startRow, endRow, count);
}
}
}
return heatmap;
}
use of org.locationtech.spatial4j.context.SpatialContext in project lucene-solr by apache.
the class SpatialDocMaker method makeSpatialStrategy.
/**
* Builds a SpatialStrategy from configuration options.
*/
protected SpatialStrategy makeSpatialStrategy(final Config config) {
//A Map view of Config that prefixes keys with "spatial."
Map<String, String> configMap = new AbstractMap<String, String>() {
@Override
public Set<Entry<String, String>> entrySet() {
throw new UnsupportedOperationException();
}
@Override
public String get(Object key) {
return config.get("spatial." + key, null);
}
};
SpatialContext ctx = SpatialContextFactory.makeSpatialContext(configMap, null);
return makeSpatialStrategy(config, configMap, ctx);
}
Aggregations