LUCENE-4157 Ported the Solr 3 spatial test to Lucene spatial. Did a few minor other things too.

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1356081 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David Wayne Smiley 2012-07-02 06:47:49 +00:00
parent 8356f38ad5
commit 132175856f
4 changed files with 228 additions and 74 deletions

View File

@ -25,6 +25,11 @@ import java.util.*;
/**
* A clause that compares a stored geometry to a supplied geometry.
*
* @see <a href="http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm">
* ESRI's docs on spatial relations</a>
* @see <a href="http://docs.geoserver.org/latest/en/user/filter/ecql_reference.html#spatial-predicate">
* GeoServer ECQL Spatial Predicates</a>
*
* @lucene.experimental
*/
public class SpatialOperation implements Serializable {

View File

@ -1,3 +1,5 @@
package org.apache.lucene.spatial.vector;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@ -15,11 +17,8 @@
* limitations under the License.
*/
package org.apache.lucene.spatial.vector;
import com.spatial4j.core.distance.DistanceCalculator;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.simple.PointImpl;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
@ -84,8 +83,7 @@ public class DistanceValueSource extends ValueSource {
public double doubleVal(int doc) {
// make sure it has minX and area
if (validX.get(doc) && validY.get(doc)) {
PointImpl pt = new PointImpl( ptX[doc], ptY[doc] );
return calculator.distance(from, pt);
return calculator.distance(from, ptX[doc], ptY[doc]);
}
return 0;
}

View File

@ -0,0 +1,220 @@
package org.apache.lucene.spatial;
/*
* 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.carrotsearch.randomizedtesting.annotations.ParametersFactory;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.context.simple.SimpleSpatialContext;
import com.spatial4j.core.shape.Point;
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Based off of Solr 3's SpatialFilterTest.
* @author dsmiley
*/
public class PortedSolr3Test extends StrategyTestCase {
@ParametersFactory
public static Iterable<Object[]> parameters() {
List<Object[]> ctorArgs = new ArrayList<Object[]>();
SpatialContext ctx = SimpleSpatialContext.GEO_KM;
SpatialPrefixTree grid;
SpatialStrategy strategy;
grid = new GeohashPrefixTree(ctx,12);
strategy = new RecursivePrefixTreeStrategy(grid);
ctorArgs.add(new Object[]{"recursive_geohash",strategy});
grid = new QuadPrefixTree(ctx,25);
strategy = new RecursivePrefixTreeStrategy(grid);
ctorArgs.add(new Object[]{"recursive_quad",strategy});
grid = new GeohashPrefixTree(ctx,12);
strategy = new TermQueryPrefixTreeStrategy(grid);
ctorArgs.add(new Object[]{"termquery_geohash",strategy});
return ctorArgs;
}
// private String fieldName;
public PortedSolr3Test(String fieldName, SpatialStrategy strategy) {
ctx = strategy.getSpatialContext();
this.strategy = strategy;
// this.fieldName = fieldName;
fieldInfo = new SimpleSpatialFieldInfo( fieldName );
}
private void setupDocs() throws IOException {
super.deleteAll();
adoc("1", "32.7693246, -79.9289094");
adoc("2", "33.7693246, -80.9289094");
adoc("3", "-32.7693246, 50.9289094");
adoc("4", "-50.7693246, 60.9289094");
adoc("5", "0,0");
adoc("6", "0.1,0.1");
adoc("7", "-0.1,-0.1");
adoc("8", "0,179.9");
adoc("9", "0,-179.9");
adoc("10", "89.9,50");
adoc("11", "89.9,-130");
adoc("12", "-89.9,50");
adoc("13", "-89.9,-130");
commit();
}
@Test
public void testIntersections() throws Exception {
setupDocs();
//Try some edge cases
checkHitsCircle("1,1", 175, 3, 5, 6, 7);
checkHitsCircle("0,179.8", 200, 2, 8, 9);
checkHitsCircle("89.8, 50", 200, 2, 10, 11);//this goes over the north pole
checkHitsCircle("-89.8, 50", 200, 2, 12, 13);//this goes over the south pole
//try some normal cases
checkHitsCircle("33.0,-80.0", 300, 2);
//large distance
checkHitsCircle("1,1", 5000, 3, 5, 6, 7);
//Because we are generating a box based on the west/east longitudes and the south/north latitudes, which then
//translates to a range query, which is slightly more inclusive. Thus, even though 0.0 is 15.725 kms away,
//it will be included, b/c of the box calculation.
checkHitsBBox("0.1,0.1", 15, 2, 5, 6);
//try some more
deleteAll();
adoc("14", "0,5");
adoc("15", "0,15");
//3000KM from 0,0, see http://www.movable-type.co.uk/scripts/latlong.html
adoc("16", "18.71111,19.79750");
adoc("17", "44.043900,-95.436643");
commit();
checkHitsCircle("0,0", 1000, 1, 14);
checkHitsCircle("0,0", 2000, 2, 14, 15);
checkHitsBBox("0,0", 3000, 3, 14, 15, 16);
checkHitsCircle("0,0", 3001, 3, 14, 15, 16);
checkHitsCircle("0,0", 3000.1, 3, 14, 15, 16);
//really fine grained distance and reflects some of the vagaries of how we are calculating the box
checkHitsCircle("43.517030,-96.789603", 109, 0);
// falls outside of the real distance, but inside the bounding box
checkHitsCircle("43.517030,-96.789603", 110, 0);
checkHitsBBox("43.517030,-96.789603", 110, 1, 17);
}
/**
* This test is similar to a Solr 3 spatial test.
*/
@Test
public void testDistanceOrder() throws IOException {
adoc("100","1,2");
adoc("101","4,-1");
commit();
//query closer to #100
checkHitsOrdered("Intersects(Circle(3,4 d=1000))", "101", "100");
//query closer to #101
checkHitsOrdered("Intersects(Circle(4,0 d=1000))", "100", "101");
}
private void checkHitsOrdered(String spatialQ, String... ids) {
SpatialArgs args = this.argsParser.parse(spatialQ,ctx);
Query query = strategy.makeQuery(args, fieldInfo);
SearchResults results = executeQuery(query, 100);
String[] resultIds = new String[results.numFound];
int i = 0;
for (SearchResult result : results.results) {
resultIds[i++] = result.document.get("id");
}
assertArrayEquals("order matters",ids, resultIds);
}
//---- these are similar to Solr test methods
private void adoc(String idStr, String shapeStr) throws IOException {
Shape shape = ctx.readShape(shapeStr);
addDocument(newDoc(idStr,shape));
}
@SuppressWarnings("unchecked")
private Document newDoc(String id, Shape shape) {
Document doc = new Document();
doc.add(new StringField("id", id, Field.Store.YES));
for (IndexableField f : strategy.createFields(fieldInfo, shape, true, storeShape)) {
doc.add(f);
}
return doc;
}
private void checkHitsCircle(String ptStr, double dist, int assertNumFound, int... assertIds) {
_checkHits(SpatialOperation.Intersects, ptStr, dist, assertNumFound, assertIds);
}
private void checkHitsBBox(String ptStr, double dist, int assertNumFound, int... assertIds) {
_checkHits(SpatialOperation.BBoxIntersects, ptStr, dist, assertNumFound, assertIds);
}
@SuppressWarnings("unchecked")
private void _checkHits(SpatialOperation op, String ptStr, double dist, int assertNumFound, int... assertIds) {
Point pt = (Point) ctx.readShape(ptStr);
Shape shape = ctx.makeCircle(pt,dist);
SpatialArgs args = new SpatialArgs(op,shape);
//args.setDistPrecision(0.025);
Query query;
if (random().nextBoolean()) {
query = strategy.makeQuery(args, fieldInfo);
} else {
query = new FilteredQuery(new MatchAllDocsQuery(),strategy.makeFilter(args, fieldInfo));
}
SearchResults results = executeQuery(query, 100);
assertEquals(""+shape,assertNumFound,results.numFound);
if (assertIds != null) {
Set<Integer> resultIds = new HashSet<Integer>();
for (SearchResult result : results.results) {
resultIds.add(Integer.valueOf(result.document.get("id")));
}
for (int assertId : assertIds) {
assertTrue("has " + assertId, resultIds.contains(assertId));
}
}
}
}

View File

@ -1,69 +0,0 @@
/*
* 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;
import org.apache.lucene.util.LuceneTestCase;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
/**
* This is just a quick idea for *simple* tests
*/
public class TestSpatialPrefixField extends LuceneTestCase {
@Test
public void testRawTokens() {
// Ignoring geometry for now, and focus on what tokens need to match
List<String> docA = Arrays.asList(
"AAAAAA*",
"AAAAAB+"
);
List<String> docB = Arrays.asList(
"A*",
"BB*"
);
// Assumptions:
checkQuery("AAAAA", "docA", "docB");
checkQuery("AAAAA*", "docA", "docB"); // for now * and + are essentially identical
checkQuery("AAAAA+", "docA", "docB"); // down the road, there may be a difference between 'covers' and an edge
checkQuery("AA*", "docB", "docA"); // Bigger input query
checkQuery("AAAAAAAAAAAA*", "docA", "docB"); // small
checkQuery("BC"); // nothing
checkQuery("XX"); // nothing
// match only B
checkQuery("B", "docB");
checkQuery("BBBB", "docB");
checkQuery("B*", "docB");
checkQuery("BBBB*", "docB");
}
void checkQuery(String query, String... expect) {
// TODO, check that the query returns the docs in order
}
}