public void TestDefault() { SpatialContext s = SpatialContext.GEO; SpatialContext t = Call();//default Assert.Equal(s.GetType(), t.GetType()); Assert.Equal(s.IsGeo(), t.IsGeo()); Assert.Equal(s.GetDistCalc(), t.GetDistCalc()); Assert.Equal(s.GetWorldBounds(), t.GetWorldBounds()); }
public GeohashPrefixTree(SpatialContext ctx, int maxLevels) : base(ctx, maxLevels) { Rectangle bounds = ctx.GetWorldBounds(); if (bounds.GetMinX() != -180) { throw new ArgumentException("Geohash only supports lat-lon world bounds. Got " + bounds); } int MAXP = GetMaxLevelsPossible(); if (maxLevels <= 0 || maxLevels > MAXP) { throw new ArgumentException("maxLen must be [1-" + MAXP + "] but got " + maxLevels); } }
/// <summary> /// Given a cell having the specified level, returns the distance from opposite /// corners. /// </summary> /// <remarks> /// Given a cell having the specified level, returns the distance from opposite /// corners. Since this might very depending on where the cell is, this method /// may over-estimate. /// </remarks> /// <param name="level">[1 to maxLevels]</param> /// <returns>> 0</returns> public virtual double GetDistanceForLevel(int level) { if (level < 1 || level > MaxLevels) { throw new ArgumentException("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)); }
public void testArea() { double radius = DistanceUtils.EARTH_MEAN_RADIUS_KM * DistanceUtils.KM_TO_DEG; //surface of a sphere is 4 * pi * r^2 double earthArea = 4 * Math.PI * radius * radius; Circle c = ctx.MakeCircle(random.Next(-180, 180), random.Next(-90, 90), 180); //180 means whole earth CustomAssert.EqualWithDelta(earthArea, c.GetArea(ctx), 1.0); CustomAssert.EqualWithDelta(earthArea, ctx.GetWorldBounds().GetArea(ctx), 1.0); //now check half earth Circle cHalf = ctx.MakeCircle(c.GetCenter(), 90); CustomAssert.EqualWithDelta(earthArea / 2, cHalf.GetArea(ctx), 1.0); //circle with same radius at +20 lat with one at -20 lat should have same area as well as bbox with same area Circle c2 = ctx.MakeCircle(c.GetCenter(), 30); Circle c3 = ctx.MakeCircle(c.GetCenter().GetX(), 20, 30); CustomAssert.EqualWithDelta(c2.GetArea(ctx), c3.GetArea(ctx), 0.01); Circle c3Opposite = ctx.MakeCircle(c.GetCenter().GetX(), -20, 30); CustomAssert.EqualWithDelta(c3.GetArea(ctx), c3Opposite.GetArea(ctx), 0.01); CustomAssert.EqualWithDelta(c3.GetBoundingBox().GetArea(ctx), c3Opposite.GetBoundingBox().GetArea(ctx), 0.01); //small shapes near the equator should have similar areas to euclidean rectangle Rectangle smallRect = ctx.MakeRectangle(0, 1, 0, 1); CustomAssert.EqualWithDelta(1.0, smallRect.GetArea(null), 0.0); double smallDelta = smallRect.GetArea(null) - smallRect.GetArea(ctx); Assert.True(smallDelta > 0 && smallDelta < 0.0001); Circle smallCircle = ctx.MakeCircle(0, 0, 1); smallDelta = smallCircle.GetArea(null) - smallCircle.GetArea(ctx); Assert.True(smallDelta > 0 && smallDelta < 0.0001); //bigger, but still fairly similar //c2 = ctx.makeCircle(c.getCenter(), 30); double areaRatio = c2.GetArea(null) / c2.GetArea(ctx); Assert.True(areaRatio > 1 && areaRatio < 1.1); }
public void TestGeoRectangle(SpatialContext ctx) { base.ctx = ctx; double v = 200 * (random.NextDouble() > 0.5 ? -1 : 1); Assert.Throws <InvalidShapeException>(() => ctx.MakeRectangle(v, 0, 0, 0)); Assert.Throws <InvalidShapeException>(() => ctx.MakeRectangle(0, v, 0, 0)); Assert.Throws <InvalidShapeException>(() => ctx.MakeRectangle(0, 0, v, 0)); Assert.Throws <InvalidShapeException>(() => ctx.MakeRectangle(0, 0, 0, v)); Assert.Throws <InvalidShapeException>(() => ctx.MakeRectangle(0, 0, 10, -10)); //test some relateXRange // opposite +/- 180 Assert.Equal(SpatialRelation.INTERSECTS, ctx.MakeRectangle(170, 180, 0, 0).RelateXRange(-180, -170)); Assert.Equal(SpatialRelation.INTERSECTS, ctx.MakeRectangle(-90, -45, 0, 0).RelateXRange(-45, -135)); Assert.Equal(SpatialRelation.CONTAINS, ctx.GetWorldBounds().RelateXRange(-90, -135)); //point on edge at dateline using opposite +/- 180 Assert.Equal(SpatialRelation.CONTAINS, ctx.MakeRectangle(170, 180, 0, 0).Relate(ctx.MakePoint(-180, 0))); //test 180 becomes -180 for non-zero width rectangle Assert.Equal(ctx.MakeRectangle(-180, -170, 0, 0), ctx.MakeRectangle(180, -170, 0, 0)); Assert.Equal(ctx.MakeRectangle(170, 180, 0, 0), ctx.MakeRectangle(170, -180, 0, 0)); double[] lons = new double[] { 0, 45, 160, 180, -45, -175, -180 }; //minX foreach (double lon in lons) { double[] lonWs = new double[] { 0, 20, 180, 200, 355, 360 }; //width foreach (double lonW in lonWs) { if (lonW == 360 && lon != -180) { continue; } TestRectangle(lon, lonW, 0, 0); TestRectangle(lon, lonW, -10, 10); TestRectangle(lon, lonW, 80, 10); //polar cap TestRectangle(lon, lonW, -90, 180); //full lat range } } TestShapes2D.testCircleReset(ctx); //Test geo rectangle intersections testRectIntersect(); }
public void TestCustom() { SpatialContext sc = Call("geo", "false"); Assert.True(!sc.IsGeo()); Assert.Equal(new CartesianDistCalc(), sc.GetDistCalc()); sc = Call("geo", "false", "distCalculator", "cartesian^2", "worldBounds", "-100 0 75 200");//West South East North Assert.Equal(new CartesianDistCalc(true), sc.GetDistCalc()); Assert.Equal(new RectangleImpl(-100, 75, 0, 200, sc), sc.GetWorldBounds()); sc = Call("geo", "true", "distCalculator", "lawOfCosines"); Assert.True(sc.IsGeo()); var test = new GeodesicSphereDistCalc.LawOfCosines(); Assert.Equal(test, sc.GetDistCalc()); }
public void testNodeTraverse() { Node prevN = null; Node n = trie.GetWorldNode(); Assert.AreEqual(0, n.GetLevel()); Assert.AreEqual(ctx.GetWorldBounds(), n.GetShape()); while (n.GetLevel() < trie.GetMaxLevels()) { prevN = n; var it = n.GetSubCells().GetEnumerator(); it.MoveNext(); n = it.Current; //TODO random which one? Assert.AreEqual(prevN.GetLevel() + 1, n.GetLevel()); Rectangle prevNShape = (Rectangle)prevN.GetShape(); Shape s = n.GetShape(); Rectangle sbox = s.GetBoundingBox(); Assert.IsTrue(prevNShape.GetWidth() > sbox.GetWidth()); Assert.IsTrue(prevNShape.GetHeight() > sbox.GetHeight()); } }
public QuadPrefixTree(SpatialContext ctx, int maxLevels) : base(ctx, maxLevels) { Init(ctx, ctx.GetWorldBounds(), maxLevels); }
public QuadPrefixTree(SpatialContext ctx) : base(ctx, DEFAULT_MAX_LEVELS) { Init(ctx, ctx.GetWorldBounds(), DEFAULT_MAX_LEVELS); }
/// <summary>Returns a new shape that is larger than shape by at distErr.</summary> /// <remarks>Returns a new shape that is larger than shape by at distErr.</remarks> protected internal virtual Shape BufferShape(Shape shape, double distErr) { //TODO move this generic code elsewhere? Spatial4j? if (distErr <= 0) { throw new ArgumentException("distErr must be > 0"); } SpatialContext ctx = grid.SpatialContext; if (shape is Point) { return(ctx.MakeCircle((Point)shape, distErr)); } else { if (shape is Circle) { var circle = (Circle)shape; double newDist = circle.GetRadius() + distErr; if (ctx.IsGeo() && newDist > 180) { newDist = 180; } return(ctx.MakeCircle(circle.GetCenter(), newDist)); } else { Rectangle bbox = shape.GetBoundingBox(); double newMinX = bbox.GetMinX() - distErr; double newMaxX = bbox.GetMaxX() + distErr; double newMinY = bbox.GetMinY() - distErr; double newMaxY = bbox.GetMaxY() + distErr; if (ctx.IsGeo()) { if (newMinY < -90) { newMinY = -90; } if (newMaxY > 90) { newMaxY = 90; } if (newMinY == -90 || newMaxY == 90 || bbox.GetWidth() + 2 * distErr > 360) { newMinX = -180; newMaxX = 180; } else { newMinX = DistanceUtils.NormLonDEG(newMinX); newMaxX = DistanceUtils.NormLonDEG(newMaxX); } } else { //restrict to world bounds newMinX = Math.Max(newMinX, ctx.GetWorldBounds().GetMinX()); newMaxX = Math.Min(newMaxX, ctx.GetWorldBounds().GetMaxX()); newMinY = Math.Max(newMinY, ctx.GetWorldBounds().GetMinY()); newMaxY = Math.Min(newMaxY, ctx.GetWorldBounds().GetMaxY()); } return(ctx.MakeRectangle(newMinX, newMaxX, newMinY, newMaxY)); } } }
public QuadPrefixTree(SpatialContext ctx, int maxLevels) : this(ctx, ctx.GetWorldBounds(), maxLevels) { }
public void TestGeoCircle(SpatialContext ctx) { base.ctx = ctx; Assert.Equal("Circle(Pt(x=10.0,y=20.0), d=30.0° 3335.85km)", ctx.MakeCircle(10, 20, 30).ToString()); double v = 200 * (random.NextDouble() > 0.5 ? -1 : 1); Assert.Throws <InvalidShapeException>(() => ctx.MakeCircle(v, 0, 5)); Assert.Throws <InvalidShapeException>(() => ctx.MakeCircle(0, v, 5)); Assert.Throws <InvalidShapeException>(() => ctx.MakeCircle(random.Next(-180, 180), random.Next(-90, 90), v)); //--Start with some static tests that once failed: //Bug: numeric edge at pole, fails to init ctx.MakeCircle(110, -12, 90 + 12); //Bug: horizXAxis not in enclosing rectangle, assertion ctx.MakeCircle(-44, 16, 106); ctx.MakeCircle(-36, -76, 14); ctx.MakeCircle(107, 82, 172); // TODO need to update this test to be valid //{ // //Bug in which distance was being confused as being in the same coordinate system as x,y. // double distDeltaToPole = 0.001;//1m // double distDeltaToPoleDEG = ctx.getDistCalc().distanceToDegrees(distDeltaToPole); // double dist = 1;//1km // double distDEG = ctx.getDistCalc().distanceToDegrees(dist); // Circle c = ctx.makeCircle(0, 90 - distDeltaToPoleDEG - distDEG, dist); // Rectangle cBBox = c.getBoundingBox(); // Rectangle r = ctx.makeRect(cBBox.getMaxX() * 0.99, cBBox.getMaxX() + 1, c.getCenter().getY(), c.getCenter().getY()); // assertEquals(INTERSECTS, c.getBoundingBox().relate(r, ctx)); // assertEquals("dist != xy space", INTERSECTS, c.relate(r, ctx));//once failed here //} assertEquals("edge rounding issue 2", SpatialRelation.INTERSECTS, ctx.MakeCircle(84, -40, 136).Relate(ctx.MakeRectangle(-150, -80, 34, 84))); assertEquals("edge rounding issue", SpatialRelation.CONTAINS, ctx.MakeCircle(0, 66, 156).Relate(ctx.MakePoint(0, -90))); assertEquals("nudge back circle", SpatialRelation.CONTAINS, ctx.MakeCircle(-150, -90, 122).Relate(ctx.MakeRectangle(0, -132, 32, 32))); assertEquals("wrong estimate", SpatialRelation.DISJOINT, ctx.MakeCircle(-166, 59, kmToDeg(5226.2)).Relate(ctx.MakeRectangle(36, 66, 23, 23))); assertEquals("bad CONTAINS (dateline)", SpatialRelation.INTERSECTS, ctx.MakeCircle(56, -50, kmToDeg(12231.5)).Relate(ctx.MakeRectangle(108, 26, 39, 48))); assertEquals("bad CONTAINS (backwrap2)", SpatialRelation.INTERSECTS, ctx.MakeCircle(112, -3, 91).Relate(ctx.MakeRectangle(-163, 29, -38, 10))); assertEquals("bad CONTAINS (r x-wrap)", SpatialRelation.INTERSECTS, ctx.MakeCircle(-139, 47, 80).Relate(ctx.MakeRectangle(-180, 180, -3, 12))); assertEquals("bad CONTAINS (pwrap)", SpatialRelation.INTERSECTS, ctx.MakeCircle(-139, 47, 80).Relate(ctx.MakeRectangle(-180, 179, -3, 12))); assertEquals("no-dist 1", SpatialRelation.WITHIN, ctx.MakeCircle(135, 21, 0).Relate(ctx.MakeRectangle(-103, -154, -47, 52))); assertEquals("bbox <= >= -90 bug", SpatialRelation.CONTAINS, ctx.MakeCircle(-64, -84, 124).Relate(ctx.MakeRectangle(-96, 96, -10, -10))); //The horizontal axis line of a geo circle doesn't necessarily pass through c's ctr. assertEquals("c's horiz axis doesn't pass through ctr", SpatialRelation.INTERSECTS, ctx.MakeCircle(71, -44, 40).Relate(ctx.MakeRectangle(15, 27, -62, -34))); assertEquals("pole boundary", SpatialRelation.INTERSECTS, ctx.MakeCircle(-100, -12, 102).Relate(ctx.MakeRectangle(143, 175, 4, 32))); assertEquals("full circle assert", SpatialRelation.CONTAINS, ctx.MakeCircle(-64, 32, 180).Relate(ctx.MakeRectangle(47, 47, -14, 90))); //--Now proceed with systematic testing: assertEquals(ctx.GetWorldBounds(), ctx.MakeCircle(0, 0, 180).GetBoundingBox()); //assertEquals(ctx.makeCircle(0,0,distToOpposeSide/2 - 500).getBoundingBox()); double[] theXs = new double[] { -180, -45, 90 }; foreach (double x in theXs) { double[] theYs = new double[] { -90, -45, 0, 45, 90 }; foreach (double y in theYs) { TestCircle(x, y, 0); TestCircle(x, y, kmToDeg(500)); TestCircle(x, y, 90); TestCircle(x, y, 180); } } TestCircleIntersect(); }