public void testMakeRect(SpatialContext ctx) { this.ctx = ctx; //test rectangle constructor Assert.Equal(new RectangleImpl(1, 3, 2, 4), new RectangleImpl(new PointImpl(1, 2), new PointImpl(3, 4))); //test ctx.makeRect Assert.Equal(ctx.MakeRect(1, 3, 2, 4), ctx.MakeRect(ctx.MakePoint(1, 2), ctx.MakePoint(3, 4))); }
/// <summary> /// Computes the distance given a shape and the {@code distErrPct}. The /// algorithm is the fraction of the distance from the center of the query /// shape to its furthest bounding box corner. /// </summary> /// <param name="shape">Mandatory.</param> /// <param name="distErrPct">0 to 0.5</param> /// <param name="ctx">Mandatory</param> /// <returns>A distance (in degrees).</returns> public static double CalcDistanceFromErrPct(Shape shape, double distErrPct, SpatialContext ctx) { if (distErrPct < 0 || distErrPct > 0.5) { throw new ArgumentException("distErrPct " + distErrPct + " must be between [0 to 0.5]", "distErrPct"); } if (distErrPct == 0 || shape is Point) { return 0; } Rectangle bbox = shape.GetBoundingBox(); //The diagonal distance should be the same computed from any opposite corner, // and this is the longest distance that might be occurring within the shape. double diagonalDist = ctx.GetDistCalc().Distance( ctx.MakePoint(bbox.GetMinX(), bbox.GetMinY()), bbox.GetMaxX(), bbox.GetMaxY()); return diagonalDist*0.5*distErrPct; }
private readonly GeoCircle inverseCircle; //when distance reaches > 1/2 way around the world, cache the inverse. #endregion Fields #region Constructors public GeoCircle(Point p, double dist, SpatialContext ctx) : base(p, dist, ctx) { Debug.Assert(ctx.IsGeo()); //In the direction of latitude (N,S), distance is the same number of degrees. distDEG = ctx.GetDistCalc().DistanceToDegrees(distRadius); if (distDEG > 90) { //--spans more than half the globe Debug.Assert(enclosingBox.GetWidth() == 360); double backDistDEG = 180 - distDEG; if (backDistDEG > 0) { double backDistance = ctx.GetDistCalc().DegreesToDistance(backDistDEG); //shrink inverseCircle as small as possible to avoid accidental overlap backDistance -= Ulp(backDistance); Point backPoint = ctx.MakePoint(GetCenter().GetX() + 180, GetCenter().GetY() + 180); inverseCircle = new GeoCircle(backPoint, backDistance, ctx); } else inverseCircle = null;//whole globe horizAxisY = GetCenter().GetY();//although probably not used } else { inverseCircle = null; double _horizAxisY = ctx.GetDistCalc().CalcBoxByDistFromPt_yHorizAxisDEG(GetCenter(), dist, ctx); //some rare numeric conditioning cases can cause this to be barely beyond the box if (_horizAxisY > enclosingBox.GetMaxY()) { horizAxisY = enclosingBox.GetMaxY(); } else if (_horizAxisY < enclosingBox.GetMinY()) { horizAxisY = enclosingBox.GetMinY(); } else { horizAxisY = _horizAxisY; } //Debug.Assert(enclosingBox.Relate_yRange(horizAxis, horizAxis, ctx).Intersects()); } }
public override Point PointOnBearing(Point @from, double dist, double bearingDEG, SpatialContext ctx) { if (dist == 0) return from; double bearingRAD = DistanceUtils.ToRadians(bearingDEG); double x = Math.Sin(bearingRAD) * dist; double y = Math.Cos(bearingRAD) * dist; return ctx.MakePoint(from.GetX() + x, from.GetY() + y); }
public void TestSimplePoint(SpatialContext ctx) { base.ctx = ctx; Point pt = ctx.MakePoint(0, 0); String msg = pt.ToString(); //test equals & hashcode Point pt2 = ctx.MakePoint(0, 0); Assert.Equal(/*msg,*/ pt, pt2); Assert.Equal(/*msg,*/ pt.GetHashCode(), pt2.GetHashCode()); Assert.False(pt.HasArea(), msg); Assert.Equal(/*msg,*/ pt.GetCenter(), pt); Rectangle bbox = pt.GetBoundingBox(); Assert.False(bbox.HasArea(), msg); var center = bbox.GetCenter(); Assert.True(pt.Equals(center)); //Assert.Equal(/*msg,*/ pt, center); AssertRelation(msg, SpatialRelation.CONTAINS, pt, pt2); AssertRelation(msg, SpatialRelation.DISJOINT, pt, ctx.MakePoint(0, 1)); AssertRelation(msg, SpatialRelation.DISJOINT, pt, ctx.MakePoint(1, 0)); AssertRelation(msg, SpatialRelation.DISJOINT, pt, ctx.MakePoint(1, 1)); }
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(); }
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(); }
/** * Given a start point (startLat, startLon) and a bearing on a sphere of radius <i>sphereRadius</i>, return the destination point. * * * @param startLat The starting point latitude, in radians * @param startLon The starting point longitude, in radians * @param distanceRAD The distance to travel along the bearing in radians. * @param bearingRAD The bearing, in radians. North is a 0, moving clockwise till radians(360). * @param result A preallocated array to hold the results. If null, a new one is constructed. * @return The destination point, in radians. First entry is latitude, second is longitude */ public static Point PointOnBearingRAD(double startLat, double startLon, double distanceRAD, double bearingRAD, SpatialContext ctx, Point reuse) { /* lat2 = asin(sin(lat1)*cos(d/R) + cos(lat1)*sin(d/R)*cos(θ)) lon2 = lon1 + atan2(sin(θ)*sin(d/R)*cos(lat1), cos(d/R)−sin(lat1)*sin(lat2)) */ double cosAngDist = Math.Cos(distanceRAD); double cosStartLat = Math.Cos(startLat); double sinAngDist = Math.Sin(distanceRAD); double sinStartLat = Math.Sin(startLat); double lat2 = Math.Asin(sinStartLat*cosAngDist + cosStartLat*sinAngDist*Math.Cos(bearingRAD)); double lon2 = startLon + Math.Atan2(Math.Sin(bearingRAD)*sinAngDist*cosStartLat, cosAngDist - sinStartLat*Math.Sin(lat2)); // normalize lon first if (lon2 > DEG_180_AS_RADS) { lon2 = -1.0*(DEG_180_AS_RADS - (lon2 - DEG_180_AS_RADS)); } else if (lon2 < -DEG_180_AS_RADS) { lon2 = (lon2 + DEG_180_AS_RADS) + DEG_180_AS_RADS; } // normalize lat - could flip poles if (lat2 > DEG_90_AS_RADS) { lat2 = DEG_90_AS_RADS - (lat2 - DEG_90_AS_RADS); if (lon2 < 0) { lon2 = lon2 + DEG_180_AS_RADS; } else { lon2 = lon2 - DEG_180_AS_RADS; } } else if (lat2 < -DEG_90_AS_RADS) { lat2 = -DEG_90_AS_RADS - (lat2 + DEG_90_AS_RADS); if (lon2 < 0) { lon2 = lon2 + DEG_180_AS_RADS; } else { lon2 = lon2 - DEG_180_AS_RADS; } } if (reuse == null) { return ctx.MakePoint(lon2, lat2); } else { reuse.Reset(lon2, lat2); //x y return reuse; } }
public override Point PointOnBearing(Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse) { if (distDEG == 0) { if (reuse == null) return from; reuse.Reset(from.GetX(), from.GetY()); return reuse; } double bearingRAD = DistanceUtils.ToRadians(bearingDEG); double x = from.GetX() + Math.Sin(bearingRAD)*distDEG; double y = from.GetY() + Math.Cos(bearingRAD)*distDEG; if (reuse == null) { return ctx.MakePoint(x, y); } else { reuse.Reset(x, y); return reuse; } }
public override Point PointOnBearing(Point @from, double dist, double bearingDEG, SpatialContext ctx) { //TODO avoid unnecessary double[] intermediate object if (dist == 0) return from; double[] latLon = DistanceUtils.PointOnBearingRAD( DistanceUtils.ToRadians(from.GetY()), DistanceUtils.ToRadians(from.GetX()), DistanceUtils.Dist2Radians(dist, ctx.GetUnits().EarthRadius()), DistanceUtils.ToRadians(bearingDEG), null); return ctx.MakePoint(DistanceUtils.ToDegrees(latLon[1]), DistanceUtils.ToDegrees(latLon[0])); }
public void TestGeoRectangle(SpatialContext ctx) { base.ctx = ctx; //First test some relateXRange // opposite +/- 180 Assert.Equal(SpatialRelation.INTERSECTS, ctx.MakeRect(170, 180, 0, 0).RelateXRange(-180, -170, ctx)); Assert.Equal(SpatialRelation.INTERSECTS, ctx.MakeRect(-90, -45, 0, 0).RelateXRange(-45, -135, ctx)); Assert.Equal(SpatialRelation.CONTAINS, ctx.GetWorldBounds().RelateXRange(-90, -135, ctx)); //point on edge at dateline using opposite +/- 180 Assert.Equal(SpatialRelation.CONTAINS, ctx.MakeRect(170, 180, 0, 0).Relate(ctx.MakePoint(-180, 0), ctx)); //test 180 becomes -180 for non-zero width rectangle Assert.Equal(ctx.MakeRect(-180, -170, 0, 0), ctx.MakeRect(180, -170, 0, 0)); Assert.Equal(ctx.MakeRect(170, 180, 0, 0), ctx.MakeRect(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) { 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 } } //Test geo rectangle intersections TestRectIntersect(); }
/** * Decodes the given geohash into a latitude and longitude * * @param geohash Geohash to deocde * @return Array with the latitude at index 0, and longitude at index 1 */ public static Point Decode(String geohash, SpatialContext ctx) { Rectangle rect = DecodeBoundary(geohash, ctx); double latitude = (rect.GetMinY() + rect.GetMaxY()) / 2D; double longitude = (rect.GetMinX() + rect.GetMaxX()) / 2D; return ctx.MakePoint(longitude, latitude); }
public SpatialRelation Relate(Point pt, SpatialContext ctx) { //TODO if not jtsPoint, test against bbox to avoid JTS if disjoint var jtsPoint = (NtsPoint)(pt is NtsPoint ? pt : ctx.MakePoint(pt.GetX(), pt.GetY())); return geom.Disjoint(jtsPoint.GetGeom()) ? SpatialRelation.DISJOINT : SpatialRelation.CONTAINS; }