blob: 7b550b4decf8e6c7b915019fe65b7ccaa6cc7eed [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.
*/
using System;
using System.Collections.Generic;
using Lucene.Net.Documents;
using Lucene.Net.Search;
using Lucene.Net.Spatial;
using Lucene.Net.Spatial.Prefix;
using Lucene.Net.Spatial.Prefix.Tree;
using Lucene.Net.Spatial.Queries;
using NUnit.Framework;
using Spatial4n.Core.Context;
using Spatial4n.Core.Shapes;
namespace Lucene.Net.Contrib.Spatial.Test
{
/**
* Based off of Solr 3's SpatialFilterTest.
*/
public class PortedSolr3Test : StrategyTestCase
{
public class TestValuesProvider
{
public List<Param> dataList = new List<Param>();
public IEnumerable<Param> ParamsProvider()
{
var ctorArgs = new List<Param>();
SpatialContext ctx = SpatialContext.GEO_KM;
SpatialPrefixTree grid = new GeohashPrefixTree(ctx, 12);
SpatialStrategy strategy = new RecursivePrefixTreeStrategy(grid, "recursive_geohash");
ctorArgs.Add(new Param(strategy, "recursive_geohash"));
grid = new QuadPrefixTree(ctx, 25);
strategy = new RecursivePrefixTreeStrategy(grid, "recursive_quad");
ctorArgs.Add(new Param(strategy, "recursive_quad"));
grid = new GeohashPrefixTree(ctx, 12);
strategy = new TermQueryPrefixTreeStrategy(grid, "termquery_geohash");
ctorArgs.Add(new Param(strategy, "termquery_geohash"));
return ctorArgs;
}
}
public class Param
{
public readonly SpatialStrategy strategy;
public readonly String description;
public Param(SpatialStrategy strategy, String description)
{
this.strategy = strategy;
this.description = description;
}
public override String ToString()
{
return description;
}
}
Random random;
private void setupDocs()
{
random = NewRandom();
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, Sequential]
public void testIntersections([ValueSourceAttribute(typeof(TestValuesProvider), "ParamsProvider")] Param p)
{
this.ctx = p.strategy.GetSpatialContext();
this.strategy = p.strategy;
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, Sequential]
public void testDistanceOrder([ValueSourceAttribute(typeof(TestValuesProvider), "ParamsProvider")] Param p)
{
this.ctx = p.strategy.GetSpatialContext();
this.strategy = p.strategy;
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, params String[] ids)
{
SpatialArgs args = this.argsParser.Parse(spatialQ, ctx);
Query query = strategy.MakeQuery(args);
SearchResults results = executeQuery(query, 100);
var resultIds = new String[results.numFound];
int i = 0;
foreach (SearchResult result in results.results)
{
resultIds[i++] = result.document.Get("id");
}
Assert.AreEqual(ids, resultIds, "order matters");
}
//---- these are similar to Solr test methods
private void adoc(String idStr, String shapeStr)
{
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 Field("id", id, Field.Store.YES, Field.Index.ANALYZED));
foreach (var f in strategy.CreateIndexableFields(shape))
{
doc.Add(f);
}
if (storeShape)
doc.Add(new Field(strategy.GetFieldName(), ctx.ToString(shape), Field.Store.YES, Field.Index.NO));
return doc;
}
private void checkHitsCircle(String ptStr, double dist, int assertNumFound, params int[] assertIds)
{
_checkHits(SpatialOperation.Intersects, ptStr, dist, assertNumFound, assertIds);
}
private void checkHitsBBox(String ptStr, double dist, int assertNumFound, params int[] assertIds)
{
_checkHits(SpatialOperation.BBoxIntersects, ptStr, dist, assertNumFound, assertIds);
}
//@SuppressWarnings("unchecked")
private void _checkHits(SpatialOperation op, String ptStr, double dist, int assertNumFound, params 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.NextDouble() > 0.5)
{
query = strategy.MakeQuery(args);
}
else
{
query = new FilteredQuery(new MatchAllDocsQuery(), strategy.MakeFilter(args));
}
SearchResults results = executeQuery(query, 100);
Assert.AreEqual(assertNumFound, results.numFound, "" + shape);
if (assertIds != null)
{
var resultIds = new HashSet<int>();
foreach (SearchResult result in results.results)
{
resultIds.Add(int.Parse(result.document.Get("id")));
}
foreach (int assertId in assertIds)
{
Assert.True(resultIds.Contains(assertId), "has " + assertId);
}
}
}
}
}