This is an automated email from the ASF dual-hosted git repository. nightowl888 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/lucenenet.git
commit a7537bb69c6bec774d0b24d5dd1b4900dad490b0 Author: Shad Storhaug <[email protected]> AuthorDate: Thu Dec 2 23:56:11 2021 +0700 BUG: Lucene.Net.Spatial.Prefix.SpatialOpRecursivePrefixTreeTest: Ported over patch from https://github.com/apache/lucene/commit/e9906a334b8e123e93b917c3feb6e55fed0a8c57 (from 4.9.0) (fixes #559). Contains requires a non-geo instance of SpatialContext to compare corners, otherwise it may match in cases where it isn't supposed to. --- .../Prefix/SpatialOpRecursivePrefixTreeTest.cs | 89 ++++++++++++++++++---- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/src/Lucene.Net.Tests.Spatial/Prefix/SpatialOpRecursivePrefixTreeTest.cs b/src/Lucene.Net.Tests.Spatial/Prefix/SpatialOpRecursivePrefixTreeTest.cs index 41589a8..c4cb230 100644 --- a/src/Lucene.Net.Tests.Spatial/Prefix/SpatialOpRecursivePrefixTreeTest.cs +++ b/src/Lucene.Net.Tests.Spatial/Prefix/SpatialOpRecursivePrefixTreeTest.cs @@ -38,6 +38,7 @@ namespace Lucene.Net.Spatial.Prefix private SpatialPrefixTree grid; + private SpatialContext ctx2D; public override void SetUp() { @@ -51,11 +52,23 @@ namespace Lucene.Net.Spatial.Prefix SetupQuadGrid(maxLevels); else SetupGeohashGrid(maxLevels); + SetupCtx2D(ctx); //((PrefixTreeStrategy) strategy).setDistErrPct(0);//fully precise to grid Console.WriteLine("Strategy: " + strategy.toString()); } + private void SetupCtx2D(SpatialContext ctx) + { + if (!ctx.IsGeo) + ctx2D = ctx; + //A non-geo version of ctx. + FakeSpatialContextFactory ctxFactory = new FakeSpatialContextFactory(); + ctxFactory.geo = false; + ctxFactory.worldBounds = ctx.WorldBounds; + ctx2D = ctxFactory.NewSpatialContext(); + } + private void SetupQuadGrid(int maxLevels) { //non-geospatial makes this test a little easier (in gridSnap), and using boundary values 2^X raises @@ -128,7 +141,7 @@ namespace Lucene.Net.Spatial.Prefix public virtual void TestContainsPairOverlap() { SetupQuadGrid(3); - adoc("0", new ShapePair(ctx.MakeRectangle(0, 33, -128, 128), ctx.MakeRectangle(33, 128, -128, 128), true, ctx)); + adoc("0", new ShapePair(ctx.MakeRectangle(0, 33, -128, 128), ctx.MakeRectangle(33, 128, -128, 128), true, ctx, ctx2D)); Commit(); Query query = strategy.MakeQuery(new SpatialArgs(SpatialOperation.Contains, ctx.MakeRectangle(0, 128, -16, 128))); @@ -141,7 +154,7 @@ namespace Lucene.Net.Spatial.Prefix { SetupQuadGrid(7); //one shape comprised of two parts, quite separated apart - adoc("0", new ShapePair(ctx.MakeRectangle(0, 10, -120, -100), ctx.MakeRectangle(220, 240, 110, 125), false, ctx)); + adoc("0", new ShapePair(ctx.MakeRectangle(0, 10, -120, -100), ctx.MakeRectangle(220, 240, 110, 125), false, ctx, ctx2D)); Commit(); //query surrounds only the second part of the indexed shape Query query = strategy.MakeQuery(new SpatialArgs(SpatialOperation.IsWithin, @@ -175,6 +188,17 @@ namespace Lucene.Net.Spatial.Prefix ), 1).numFound == 1);//match } + [Test] + public void TestShapePair() + { + ctx = SpatialContext.GEO; + SetupCtx2D(ctx); + + IShape leftShape = new ShapePair(ctx.MakeRectangle(-74, -56, -8, 1), ctx.MakeRectangle(-180, 134, -90, 90), true, ctx, ctx2D); + IShape queryShape = ctx.MakeRectangle(-180, 180, -90, 90); + assertEquals(SpatialRelation.WITHIN, leftShape.Relate(queryShape)); + } + //Override so we can index parts of a pair separately, resulting in the detailLevel // being independent for each shape vs the whole thing protected override Document newDoc(String id, IShape shape) @@ -380,7 +404,7 @@ namespace Lucene.Net.Spatial.Prefix { IRectangle shape1 = randomRectangle(); IRectangle shape2 = randomRectangle(); - return new ShapePair(shape1, shape2, biasContains, ctx); + return new ShapePair(shape1, shape2, biasContains, ctx, ctx2D); } private void fail(String label, String id, IDictionary<String, IShape> indexedShapes, IDictionary<String, IShape> indexedShapesGS, IShape queryShape) @@ -402,7 +426,7 @@ namespace Lucene.Net.Spatial.Prefix if (snapMe is ShapePair) { ShapePair me = (ShapePair)snapMe; - return new ShapePair(gridSnap(me.shape1), gridSnap(me.shape2), me.biasContainsThenWithin, ctx); + return new ShapePair(gridSnap(me.shape1), gridSnap(me.shape2), me.biasContainsThenWithin, ctx, ctx2D); } if (snapMe is IPoint) { @@ -434,26 +458,53 @@ namespace Lucene.Net.Spatial.Prefix { private readonly SpatialContext ctx; - internal IShape shape1, shape2; + internal readonly IShape shape1, shape2; + internal readonly IShape shape1_2D, shape2_2D;//not geo (bit of a hack) internal bool biasContainsThenWithin;//a hack + private readonly SpatialContext ctx2D; - public ShapePair(IShape shape1, IShape shape2, bool containsThenWithin, SpatialContext ctx) + public ShapePair(IShape shape1, IShape shape2, bool containsThenWithin, SpatialContext ctx, SpatialContext ctx2D) : base(new JCG.List<IShape> { shape1, shape2 }, ctx) { this.ctx = ctx; + this.ctx2D = ctx2D; this.shape1 = shape1; this.shape2 = shape2; + this.shape1_2D = ToNonGeo(shape1); + this.shape2_2D = ToNonGeo(shape2); biasContainsThenWithin = containsThenWithin; } + private IShape ToNonGeo(IShape shape) + { + if (!ctx.IsGeo) + return shape;//already non-geo + if (shape is IRectangle) { + Rectangle rect = (Rectangle)shape; + if (rect.CrossesDateLine) + { + return new ShapePair( + ctx2D.MakeRectangle(rect.MinX, 180, rect.MinY, rect.MaxY), + ctx2D.MakeRectangle(-180, rect.MaxX, rect.MinY, rect.MaxY), + biasContainsThenWithin, ctx, ctx2D); + } + else + { + return ctx2D.MakeRectangle(rect.MinX, rect.MaxX, rect.MinY, rect.MaxY); + } + } + //no need to do others; this addresses the -180/+180 ambiguity corner test problem + return shape; + } + public override SpatialRelation Relate(IShape other) { - SpatialRelation r = relateApprox(other); - if (r == SpatialRelation.CONTAINS) - return r; + SpatialRelation r = RelateApprox(other); if (r == SpatialRelation.DISJOINT) return r; + if (r == SpatialRelation.CONTAINS) + return r; if (r == SpatialRelation.WITHIN && !biasContainsThenWithin) return r; @@ -462,17 +513,27 @@ namespace Lucene.Net.Spatial.Prefix bool pairTouches = shape1.Relate(shape2).Intersects(); if (!pairTouches) return r; + // LUCENENET: From commit: https://github.com/apache/lucene/commit/e9906a334b8e123e93b917c3feb6e55fed0a8c57 //test all 4 corners + // Note: awkwardly, we use a non-geo context for this because in geo, -180 & +180 are the same place, which means + // that "other" might wrap the world horizontally and yet all its corners could be in shape1 (or shape2) even + // though shape1 is only adjacent to the dateline. I couldn't think of a better way to handle this. IRectangle oRect = (IRectangle)other; - if (Relate(ctx.MakePoint(oRect.MinX, oRect.MinY)) == SpatialRelation.CONTAINS - && Relate(ctx.MakePoint(oRect.MinX, oRect.MaxY)) == SpatialRelation.CONTAINS - && Relate(ctx.MakePoint(oRect.MaxX, oRect.MinY)) == SpatialRelation.CONTAINS - && Relate(ctx.MakePoint(oRect.MaxX, oRect.MaxY)) == SpatialRelation.CONTAINS) + if (CornerContainsNonGeo(oRect.MinX, oRect.MinY) + && CornerContainsNonGeo(oRect.MinX, oRect.MaxY) + && CornerContainsNonGeo(oRect.MaxX, oRect.MinY) + && CornerContainsNonGeo(oRect.MaxX, oRect.MaxY)) return SpatialRelation.CONTAINS; return r; } - private SpatialRelation relateApprox(IShape other) + private bool CornerContainsNonGeo(double x, double y) + { + IShape pt = ctx2D.MakePoint(x, y); + return shape1_2D.Relate(pt).Intersects() || shape2_2D.Relate(pt).Intersects(); + } + + private SpatialRelation RelateApprox(IShape other) { if (biasContainsThenWithin) {
