/******************************************************************************* * Copyright (c) 2015 MITRE * All rights reserved. This program and the accompanying materials * are made available under the terms of the Apache License, Version 2.0 which * accompanies this distribution and is available at * http://www.apache.org/licenses/LICENSE-2.0.txt ******************************************************************************/ package org.locationtech.spatial4j.shape; import org.locationtech.spatial4j.TestLog; import org.locationtech.spatial4j.context.SpatialContext; import org.locationtech.spatial4j.distance.DistanceCalculator; import org.locationtech.spatial4j.distance.DistanceUtils; import org.locationtech.spatial4j.shape.impl.PointImpl; import org.locationtech.spatial4j.shape.impl.RectangleImpl; import org.junit.Rule; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.locationtech.spatial4j.shape.SpatialRelation.CONTAINS; import static org.locationtech.spatial4j.shape.SpatialRelation.DISJOINT; /** * Some basic tests that should work with various {@link SpatialContext} * configurations. Subclasses add more. */ public abstract class AbstractTestShapes extends RandomizedShapeTest { public AbstractTestShapes(SpatialContext ctx) { super(ctx); } @Rule public final TestLog testLog = TestLog.instance; protected void testRectangle(double minX, double width, double minY, double height) { double maxX = minX + width; double maxY = minY + height; minX = normX(minX); maxX = normX(maxX); Rectangle r = ctx.makeRectangle(minX, maxX, minY, maxY); //test equals & hashcode of duplicate Rectangle r2 = ctx.makeRectangle(minX, maxX, minY, maxY); assertEquals(r,r2); assertEquals(r.hashCode(),r2.hashCode()); String msg = r.toString(); assertEquals(msg, width != 0 && height != 0, r.hasArea()); assertEquals(msg, width != 0 && height != 0, r.getArea(ctx) > 0); if (ctx.isGeo() && r.getWidth() == 360 && r.getHeight() == 180) { //whole globe double earthRadius = DistanceUtils.toDegrees(1); assertEquals(4*Math.PI * earthRadius * earthRadius, r.getArea(ctx), 1.0);//1km err } assertEqualsRatio(msg, height, r.getHeight()); assertEqualsRatio(msg, width, r.getWidth()); Point center = r.getCenter(); msg += " ctr:"+center; //System.out.println(msg); assertRelation(msg, CONTAINS, r, center); DistanceCalculator dc = ctx.getDistCalc(); double dUR = dc.distance(center, r.getMaxX(), r.getMaxY()); double dLR = dc.distance(center, r.getMaxX(), r.getMinY()); double dUL = dc.distance(center, r.getMinX(), r.getMaxY()); double dLL = dc.distance(center, r.getMinX(), r.getMinY()); assertEquals(msg,width != 0 || height != 0, dUR != 0); if (dUR != 0) assertTrue(dUR > 0 && dLL > 0); assertEqualsRatio(msg, dUR, dUL); assertEqualsRatio(msg, dLR, dLL); if (!ctx.isGeo() || center.getY() == 0) assertEqualsRatio(msg, dUR, dLL); } protected void testRectIntersect() { //This test loops past the dateline for some variables but the makeNormRect() // method ensures the rect is valid. final double INCR = 45; final double Y = 20; for(double left = -180; left <= 180; left += INCR) { for(double right = left; right - left <= 360; right += INCR) { Rectangle r = makeNormRect(left, right, -Y, Y); //test contains (which also tests within) for(double left2 = left; left2 <= right; left2 += INCR) { for(double right2 = left2; right2 <= right; right2 += INCR) { Rectangle r2 = makeNormRect(left2, right2, -Y, Y); assertRelation(null, SpatialRelation.CONTAINS, r, r2); //test point contains assertRelation(null, SpatialRelation.CONTAINS, r, r2.getCenter()); } } //test disjoint for(double left2 = right+INCR; left2 - left < 360; left2 += INCR) { //test point disjoint assertRelation(null, SpatialRelation.DISJOINT, r, ctx.makePoint( normX(left2), randomIntBetween(-90, 90))); for(double right2 = left2; right2 - left < 360; right2 += INCR) { Rectangle r2 = makeNormRect(left2, right2, -Y, Y); assertRelation(null, SpatialRelation.DISJOINT, r, r2); } } //test intersect for(double left2 = left+INCR; left2 <= right; left2 += INCR) { for(double right2 = right+INCR; right2 - left < 360; right2 += INCR) { Rectangle r2 = makeNormRect(left2, right2, -Y, Y); assertRelation(null, SpatialRelation.INTERSECTS, r, r2); } } } } } protected void testCircle(double x, double y, double dist) { Circle c = ctx.makeCircle(x, y, dist); String msg = c.toString(); final Circle c2 = ctx.makeCircle(ctx.makePoint(x, y), dist); assertEquals(c, c2); assertEquals(c.hashCode(),c2.hashCode()); assertEquals(msg, dist > 0, c.hasArea()); double area = c.getArea(ctx); assertTrue(msg, c.hasArea() == (area > 0.0)); final Rectangle bbox = c.getBoundingBox(); assertEquals(msg, dist > 0, bbox.getArea(ctx) > 0); assertTrue(msg, area <= bbox.getArea(ctx)); if (!ctx.isGeo()) { //if not geo then units of dist == units of x,y assertEqualsRatio(msg, bbox.getHeight(), dist * 2); assertEqualsRatio(msg, bbox.getWidth(), dist * 2); } assertRelation(msg, CONTAINS, c, c.getCenter()); assertRelation(msg, CONTAINS, bbox, c); } protected void testCircleIntersect() { new RectIntersectionTestHelper<Circle>(ctx) { @Override protected Circle generateRandomShape(Point nearP) { double cX = randomIntBetweenDivisible(-180, 179); double cY = randomIntBetweenDivisible(-90, 90); double cR_dist = randomIntBetweenDivisible(0, 180); return ctx.makeCircle(cX, cY, cR_dist); } @Override protected Point randomPointInEmptyShape(Circle shape) { return shape.getCenter(); } @Override protected void onAssertFail(AssertionError e, Circle s, Rectangle r, SpatialRelation ic) { //Check if the circle's edge appears to coincide with the shape. final double radius = s.getRadius(); if (radius == 180) throw e;//if this happens, then probably a bug if (radius == 0) { Point p = s.getCenter(); //if touches a side then don't throw if (p.getX() == r.getMinX() || p.getX() == r.getMaxX() || p.getY() == r.getMinY() || p.getY() == r.getMaxY()) return; throw e; } final double eps = 0.0000001; s.reset(s.getCenter().getX(), s.getCenter().getY(), radius - eps); SpatialRelation rel1 = s.relate(r); s.reset(s.getCenter().getX(), s.getCenter().getY(), radius + eps); SpatialRelation rel2 = s.relate(r); if (rel1 == rel2) throw e; s.reset(s.getCenter().getX(), s.getCenter().getY(), radius);//reset System.out.println("Seed "+getContext().getRunnerSeedAsString()+": Hid assertion due to ambiguous edge touch: "+s+" "+r); } }.testRelateWithRectangle(); } @Test public void testMakeRect() { //test rectangle constructor assertEquals(new RectangleImpl(1,3,2,4, ctx), new RectangleImpl(new PointImpl(1,2, ctx),new PointImpl(3,4, ctx), ctx)); //test ctx.makeRect assertEquals(ctx.makeRectangle(1, 3, 2, 4), ctx.makeRectangle(ctx.makePoint(1, 2), ctx.makePoint(3, 4))); } protected void testEmptiness(Shape emptyShape) { assertTrue(emptyShape.isEmpty()); Point emptyPt = emptyShape.getCenter(); assertTrue(emptyPt.isEmpty()); Rectangle emptyRect = emptyShape.getBoundingBox(); assertTrue(emptyRect.isEmpty()); assertEquals(emptyRect, emptyShape.getBoundingBox()); assertEquals(emptyPt, emptyShape.getCenter()); assertRelation("EMPTY", DISJOINT, emptyShape, emptyPt); assertRelation("EMPTY", DISJOINT, emptyShape, randomPoint()); assertRelation("EMPTY", DISJOINT, emptyShape, emptyRect); assertRelation("EMPTY", DISJOINT, emptyShape, randomRectangle(10)); assertTrue(emptyShape.getBuffered(randomInt(4), ctx).isEmpty()); } }