From 5254ae444dda03236c94f4e29a246327e97312f5 Mon Sep 17 00:00:00 2001 From: Bentley Breithaupt Date: Mon, 16 Jul 2018 14:48:17 -0700 Subject: [PATCH] Overlapping AOI Polygon Check (#58) * initial commit - intersection logic & default configurable aoi tags * Created tag combination test. * Reworked to use one configurable filter list and give one instruction * Added config and unit tests * Added overlappingAOIPolygonCheck.md, changed configurable name * Removed PARK from some default AOI tag filters * changed unit test/rule names * correct typo * code clean up * changed validCheckForObject ordering; added/updated comments * code clean up * added minimum overlap unit test * update docs * refactored hasMinimumOverlapProportion into a utility * fix function name * move utilities to more global location * intersectionPercentage unit tests --- config/configuration.json | 16 ++ docs/checks/overlappingAOIPolygonCheck.md | 161 +++++++++++++++++ .../checks/utility/IntersectionUtilities.java | 75 ++++++++ .../areas/OverlappingAOIPolygonCheck.java | 170 ++++++++++++++++++ .../utility/IntersectionUtilitiesTest.java | 57 ++++++ .../areas/OverlappingAOIPolygonCheckTest.java | 65 +++++++ .../OverlappingAOIPolygonCheckTestRule.java | 104 +++++++++++ 7 files changed, 648 insertions(+) create mode 100644 docs/checks/overlappingAOIPolygonCheck.md create mode 100644 src/main/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilities.java create mode 100644 src/main/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheck.java create mode 100644 src/test/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilitiesTest.java create mode 100644 src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTest.java create mode 100644 src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTestRule.java diff --git a/config/configuration.json b/config/configuration.json index 6f31260aa..22698c40c 100644 --- a/config/configuration.json +++ b/config/configuration.json @@ -179,6 +179,22 @@ "difficulty": "HARD" } }, + "OverlappingAOIPolygonCheck":{ + "aoi.tags.filters": ["amenity->FESTIVAL_GROUNDS", "amenity->GRAVE_YARD|landuse->CEMETERY", + "boundary->NATIONAL_PARK,PROTECTED_AREA|leisure->NATURE_RESERVE,PARK", + "historic->BATTLEFIELD", "landuse->FOREST|natural->WOOD", + "landuse->RECREATION_GROUND|leisure->RECREATION_GROUND", + "landuse->VILLAGE_GREEN|leisure->PARK", "leisure->GARDEN", + "leisure->GOLF_COURSE|sport->GOLF", "leisure->PARK&name->*", "natural->BEACH", + "tourism->ZOO"], + "intersect.minimum.limit":0.01, + "challenge": { + "description": "Tasks containing overlapping AOI ways", + "blurb": "Overlapping AOI Polygon", + "instruction": "Correct the areas as necessary to not have overlapping ways representing the same AOI", + "difficulty": "HARD" + } + }, "RoundaboutClosedLoopCheck": { "challenge": { "description": "A roundabout should be formed by one-way edges with no dead-end nodes.", diff --git a/docs/checks/overlappingAOIPolygonCheck.md b/docs/checks/overlappingAOIPolygonCheck.md new file mode 100644 index 000000000..7c8a114a5 --- /dev/null +++ b/docs/checks/overlappingAOIPolygonCheck.md @@ -0,0 +1,161 @@ +# Overlapping AOI Polygon Check + +This check flags polygons that overlap and represent the same Area of Interest (AOI). + +AOIs are defined through the configurable value `aoi.tags.filters`. This is a list of tag filters. If a polygon has any of the tags in any of the filters, it is considered an AOI. + +The defaults AOI tag filters are: + +* `amenity->FESTIVAL_GROUNDS` +* `amenity->GRAVE_YARD|landuse->CEMETERY` +* `boundary->NATIONAL_PARK,PROTECTED_AREA|leisure->NATURE_RESERVE,PARK` +* `historic->BATTLEFIELD` +* `landuse->FOREST|natural->WOOD` +* `landuse->RECREATION_GROUND|leisure->RECREATION_GROUND` +* `landuse->VILLAGE_GREEN|leisure->PARK` +* `leisure->GARDEN` +* `leisure->GOLF_COURSE|sport->GOLF` +* `leisure->PARK&name->*` +* `natural->BEACH` +* `tourism->ZOO` + +#### Live Examples + +1. The way [id:99881325](https://www.openstreetmap.org/way/99881325) overlaps way [id:173830769](https://www.openstreetmap.org/way/173830769) and they are both tagged with `leisure=PARK`. +2. The way [id:54177792](https://www.openstreetmap.org/way/54177792), with tag `landuse=FOREST`, overlaps way [id:338963545](https://www.openstreetmap.org/way/338963545), with similar tag `natural=WOOD`. + +#### Code Review + +In [Atlas](https://github.com/osmlab/atlas), OSM elements are represented as Edges, Points, Lines, Nodes, Areas & Relations; in our case, we’re are looking at [Areas](https://github.com/osmlab/atlas/blob/dev/src/main/java/org/openstreetmap/atlas/geography/atlas/items/Area.java). + +First we check if the object is an Area and it has a tag that identifies it as an AOI. + +```java +@Override +public boolean validCheckForObject(final AtlasObject object) +{ + // Checks for areas, that are not flagged, and pass any of the TaggableFilters in + // aoiFilters. aoiFiltersTest() and aoiFilters is used in place of a single TaggableFilter + // so that each filter may be tested separately later. + return object instanceof Area && !this.isFlagged(object.getIdentifier()) + && aoiFiltersTest(object); +} +``` + +The AOI tag test is performed by looping through all the tag filters in the configurable value. + +```java +private boolean aoiFiltersTest(final AtlasObject object) + { + return this.aoiFilters.stream().anyMatch(filter -> filter.test(object)); + } +``` + +Then the Areas that overlap the initial Area are found, and tested to see if the minimum overlap is met (configurable) and if they have the same or similar AOI tag. + +```java +@Override +protected Optional flag(final AtlasObject object) +{ + final Area aoi = (Area) object; + final Polygon aoiPolygon = aoi.asPolygon(); + final Rectangle aoiBounds = aoiPolygon.bounds(); + boolean hasOverlap = false; + + // Set of overlapping area AOIs + final Set overlappingAreas = Iterables + .stream(object.getAtlas().areasIntersecting(aoiBounds, + area -> area.getIdentifier() != aoi.getIdentifier() + && !this.isFlagged(area.getIdentifier()) + && area.intersects(aoiPolygon) && aoiFiltersTest(area))) + .collectToSet(); + + final CheckFlag flag = new CheckFlag(this.getTaskIdentifier(object)); + flag.addObject(object); + + // Test each overlapping AOI to see if it overlaps enough and passes the same AOI filter as + // the object + for (final Area area : overlappingAreas) + { + if (this.hasMinimumOverlapProportion(aoiPolygon, area.asPolygon()) + && aoiFiltersTest(object, area)) + { + flag.addObject(area); + flag.addInstruction(this.getLocalizedInstruction(0, object.getOsmIdentifier(), + area.getOsmIdentifier())); + this.markAsFlagged(area.getIdentifier()); + hasOverlap = true; + } + } + + if (hasOverlap) + { + this.markAsFlagged(object.getIdentifier()); + return Optional.of(flag); + } + + return Optional.empty(); +} +``` + +The minimum overlap test uses the configurable double value `intersect.minimum.limit` and compares the areas by clipping one against the other. + +```java +private boolean hasMinimumOverlapProportion(final Polygon polygon, final Polygon otherPolygon) + { + + Clip clip = null; + try + { + clip = polygon.clip(otherPolygon, Clip.ClipType.AND); + } + catch (final TopologyException e) + { + System.out + .println(String.format("Error clipping [%s] and [%s].", polygon, otherPolygon)); + } + + // Skip if nothing is returned + if (clip == null) + { + return false; + } + + // Sum intersection area + long intersectionArea = 0; + for (final PolyLine polyline : clip.getClip()) + { + if (polyline != null && polyline instanceof Polygon) + { + final Polygon clippedPolygon = (Polygon) polyline; + intersectionArea += clippedPolygon.surface().asDm7Squared(); + } + } + + // Avoid division by zero + if (intersectionArea == 0) + { + return false; + } + + // Pick the smaller building's area as baseline + final long baselineArea = Math.min(polygon.surface().asDm7Squared(), + otherPolygon.surface().asDm7Squared()); + final double proportion = (double) intersectionArea / baselineArea; + + return proportion >= this.minimumIntersect; + } +``` + +The similar AOI tag test loops through the AOI filters and finds the one that the initial Area matches and tests it against the overlapping Area. + +```java +private boolean aoiFiltersTest(final AtlasObject object, final Area area) + { + return this.aoiFilters.stream() + .anyMatch(filter -> filter.test(object) && filter.test(area)); + } +``` + +To learn more about the code, please look at the comments in the source code for the check. +[OverlappingAOIPolygonCheck](../../src/main/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheck.java) \ No newline at end of file diff --git a/src/main/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilities.java b/src/main/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilities.java new file mode 100644 index 000000000..e16ae7659 --- /dev/null +++ b/src/main/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilities.java @@ -0,0 +1,75 @@ +package org.openstreetmap.atlas.checks.utility; + +import org.openstreetmap.atlas.geography.PolyLine; +import org.openstreetmap.atlas.geography.Polygon; +import org.openstreetmap.atlas.geography.clipping.Clip; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.vividsolutions.jts.geom.TopologyException; + +/** + * A set of utilities that are common among intersection checks. + * + * @author bbreithaupt + */ +public final class IntersectionUtilities +{ + private static final Logger logger = LoggerFactory.getLogger(IntersectionUtilities.class); + + private IntersectionUtilities() + { + } + + /** + * Find the percentage of overlap for given {@link Polygon}s. + * + * @param polygon + * {@link Polygon} to check for intersection + * @param otherPolygon + * Another {@link Polygon} to check against for intersection + * @return percentage of overlap as a double; 0 if unable to clip + */ + public static double findIntersectionPercentage(final Polygon polygon, + final Polygon otherPolygon) + { + Clip clip = null; + try + { + clip = polygon.clip(otherPolygon, Clip.ClipType.AND); + } + catch (final TopologyException e) + { + logger.warn(String.format("Skipping intersection check. Error clipping [%s] and [%s].", + polygon, otherPolygon), e); + } + + // Skip if nothing is returned + if (clip == null) + { + return 0.0; + } + + // Sum intersection area + long intersectionArea = 0; + for (final PolyLine polyline : clip.getClip()) + { + if (polyline != null && polyline instanceof Polygon) + { + final Polygon clippedPolygon = (Polygon) polyline; + intersectionArea += clippedPolygon.surface().asDm7Squared(); + } + } + + // Avoid division by zero + if (intersectionArea == 0) + { + return 0.0; + } + + // Pick the smaller building's area as baseline + final long baselineArea = Math.min(polygon.surface().asDm7Squared(), + otherPolygon.surface().asDm7Squared()); + return (double) intersectionArea / baselineArea; + } +} diff --git a/src/main/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheck.java b/src/main/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheck.java new file mode 100644 index 000000000..1c71a4a4f --- /dev/null +++ b/src/main/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheck.java @@ -0,0 +1,170 @@ +package org.openstreetmap.atlas.checks.validation.areas; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.openstreetmap.atlas.checks.base.BaseCheck; +import org.openstreetmap.atlas.checks.flag.CheckFlag; +import org.openstreetmap.atlas.checks.utility.IntersectionUtilities; +import org.openstreetmap.atlas.geography.Polygon; +import org.openstreetmap.atlas.geography.Rectangle; +import org.openstreetmap.atlas.geography.atlas.items.Area; +import org.openstreetmap.atlas.geography.atlas.items.AtlasObject; +import org.openstreetmap.atlas.tags.filters.TaggableFilter; +import org.openstreetmap.atlas.utilities.collections.Iterables; +import org.openstreetmap.atlas.utilities.configuration.Configuration; + +/** + * This check flags {@link Area}s that overlap and represent the same Area of Interest (AOI) AOIs + * are defined by {@link TaggableFilter}s, which are grouped in a {@link List}. + * + * @author danielbaah + * @author bbreithaupt + */ +public class OverlappingAOIPolygonCheck extends BaseCheck +{ + + private static final long serialVersionUID = -3286838841854959683L; + + private static final List FALLBACK_INSTRUCTIONS = Arrays.asList( + "Area (id={0,number,#}) overlaps area (id={1,number,#}) and represent the same AOI."); + + private static final List AOI_FILTERS_DEFAULT = Arrays.asList( + "amenity->FESTIVAL_GROUNDS", "amenity->GRAVE_YARD|landuse->CEMETERY", + "boundary->NATIONAL_PARK,PROTECTED_AREA|leisure->NATURE_RESERVE,PARK", + "historic->BATTLEFIELD", "landuse->FOREST|natural->WOOD", + "landuse->RECREATION_GROUND|leisure->RECREATION_GROUND", + "landuse->VILLAGE_GREEN|leisure->PARK", "leisure->GARDEN", + "leisure->GOLF_COURSE|sport->GOLF", "leisure->PARK&name->*", "natural->BEACH", + "tourism->ZOO"); + + private static final double MINIMUM_PROPORTION_DEFAULT = 0.01; + + private final double minimumIntersect; + + // List of TaggableFilters where each filter represents all tags for AOIs that should not + // overlap + private final List aoiFilters = new ArrayList<>(); + + /** + * The default constructor that must be supplied. The Atlas Checks framework will generate the + * checks with this constructor, supplying a configuration that can be used to adjust any + * parameters that the check uses during operation. + * + * @param configuration + * the JSON configuration for this check + */ + public OverlappingAOIPolygonCheck(final Configuration configuration) + { + super(configuration); + this.minimumIntersect = (Double) this.configurationValue(configuration, + "intersect.minimum.limit", MINIMUM_PROPORTION_DEFAULT); + final List aoiFiltersString = (List) configurationValue(configuration, + "aoi.tags.filters", AOI_FILTERS_DEFAULT); + aoiFiltersString.forEach(string -> this.aoiFilters.add(new TaggableFilter(string))); + } + + /** + * This function will validate if the supplied atlas object is valid for the check. + * + * @param object + * the atlas object supplied by the Atlas-Checks framework for evaluation + * @return {@code true} if this object should be checked + */ + @Override + public boolean validCheckForObject(final AtlasObject object) + { + // Checks for areas, that are not flagged, and pass any of the TaggableFilters in + // aoiFilters. aoiFiltersTest() and aoiFilters is used in place of a single TaggableFilter + // so that each filter may be tested separately later. + return object instanceof Area && !this.isFlagged(object.getIdentifier()) + && aoiFiltersTest(object); + } + + /** + * This is the actual function that will check to see whether the object needs to be flagged. + * + * @param object + * the atlas object supplied by the Atlas-Checks framework for evaluation + * @return an optional {@link CheckFlag} object that + */ + @Override + protected Optional flag(final AtlasObject object) + { + final Area aoi = (Area) object; + final Polygon aoiPolygon = aoi.asPolygon(); + final Rectangle aoiBounds = aoiPolygon.bounds(); + boolean hasOverlap = false; + + // Set of overlapping area AOIs + final Set overlappingAreas = Iterables + .stream(object.getAtlas().areasIntersecting(aoiBounds, + area -> area.getIdentifier() != aoi.getIdentifier() + && !this.isFlagged(area.getIdentifier()) + && area.intersects(aoiPolygon) && aoiFiltersTest(area))) + .collectToSet(); + + final CheckFlag flag = new CheckFlag(this.getTaskIdentifier(object)); + flag.addObject(object); + + // Test each overlapping AOI to see if it overlaps enough and passes the same AOI filter as + // the object + for (final Area area : overlappingAreas) + { + if (IntersectionUtilities.findIntersectionPercentage(aoiPolygon, + area.asPolygon()) >= this.minimumIntersect && aoiFiltersTest(object, area)) + { + flag.addObject(area); + flag.addInstruction(this.getLocalizedInstruction(0, object.getOsmIdentifier(), + area.getOsmIdentifier())); + this.markAsFlagged(area.getIdentifier()); + hasOverlap = true; + } + } + + if (hasOverlap) + { + this.markAsFlagged(object.getIdentifier()); + return Optional.of(flag); + } + + return Optional.empty(); + } + + @Override + protected List getFallbackInstructions() + { + return FALLBACK_INSTRUCTIONS; + } + + /** + * Tests the input {@link AtlasObject} against the list of {@link TaggableFilter}s. + * + * @param object + * {@link AtlasObject} to be tested + * @return true if any of the filters return true + */ + private boolean aoiFiltersTest(final AtlasObject object) + { + return this.aoiFilters.stream().anyMatch(filter -> filter.test(object)); + } + + /** + * Tests the input {@link AtlasObject} and {@link Area} against the list of + * {@link TaggableFilter}s and each other. + * + * @param object + * {@link AtlasObject} to be tested + * @param area + * {@link Area} to be tested against {@code object} + * @return true if any of the filters return true for both {@code object} and {@code area} + */ + private boolean aoiFiltersTest(final AtlasObject object, final Area area) + { + return this.aoiFilters.stream() + .anyMatch(filter -> filter.test(object) && filter.test(area)); + } +} diff --git a/src/test/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilitiesTest.java b/src/test/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilitiesTest.java new file mode 100644 index 000000000..9e742694c --- /dev/null +++ b/src/test/java/org/openstreetmap/atlas/checks/utility/IntersectionUtilitiesTest.java @@ -0,0 +1,57 @@ +package org.openstreetmap.atlas.checks.utility; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openstreetmap.atlas.geography.Location; +import org.openstreetmap.atlas.geography.Polygon; + +/** + * Tests for {@link IntersectionUtilities} + * + * @author bbreithaupt + */ +public class IntersectionUtilitiesTest +{ + // Locations + private static final Location LOCATION1 = Location + .forString("47.244117672349,-122.396137421285"); + private static final Location LOCATION2 = Location + .forString("47.2434634265599,-122.396147058415"); + private static final Location LOCATION3 = Location + .forString("47.2434634265599,-122.395549556359"); + private static final Location LOCATION4 = Location + .forString("47.2441111299311,-122.395549556359"); + private static final Location LOCATION5 = Location + .forString("47.2449550951688,-122.396469902267"); + private static final Location LOCATION6 = Location + .forString("47.2442158085205,-122.396479539397"); + private static final Location LOCATION7 = Location + .forString("47.2440097223504,-122.395814577432"); + private static final Location LOCATION8 = Location + .forString("47.2449485528544,-122.395809758867"); + // Polygons + private static final Polygon POLYGON1 = new Polygon(LOCATION1, LOCATION2, LOCATION3, LOCATION4); + private static final Polygon POLYGON2 = new Polygon(LOCATION5, LOCATION6, LOCATION7, LOCATION8); + private static final Polygon POLYGON3 = new Polygon(LOCATION5, LOCATION6, LOCATION8); + + @Test + public void findIntersectionPercentageSamePolygonTest() + { + assertTrue(IntersectionUtilities.findIntersectionPercentage(POLYGON1, POLYGON1) >= 1); + } + + @Test + public void findIntersectionPercentageIntersectingPolygonTest() + { + final double intersectPolygon = IntersectionUtilities.findIntersectionPercentage(POLYGON1, + POLYGON2); + assertTrue(intersectPolygon < 1 && intersectPolygon > 0); + } + + @Test + public void findIntersectionPercentageNonIntersectingPolygonTest() + { + assertTrue(IntersectionUtilities.findIntersectionPercentage(POLYGON1, POLYGON3) == 0); + } +} diff --git a/src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTest.java b/src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTest.java new file mode 100644 index 000000000..c01a9babe --- /dev/null +++ b/src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTest.java @@ -0,0 +1,65 @@ +package org.openstreetmap.atlas.checks.validation.areas; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.openstreetmap.atlas.checks.configuration.ConfigurationResolver; +import org.openstreetmap.atlas.checks.validation.verifier.ConsumerBasedExpectedCheckVerifier; +import org.openstreetmap.atlas.utilities.configuration.Configuration; + +/** + * Tests for {@link OverlappingAOIPolygonCheck} + * + * @author bbreithaupt + */ +public class OverlappingAOIPolygonCheckTest +{ + @Rule + public OverlappingAOIPolygonCheckTestRule setup = new OverlappingAOIPolygonCheckTestRule(); + + @Rule + public ConsumerBasedExpectedCheckVerifier verifier = new ConsumerBasedExpectedCheckVerifier(); + + private final Configuration inlineConfiguration = ConfigurationResolver.inlineConfiguration( + "{\"OverlappingAOIPolygonCheck\":{\"aoi.tags.filters\": [\"amenity->GRAVE_YARD|landuse->CEMETERY\"],\"intersect.minimum.limit\":0.01}}"); + + @Test + public void sameAOIsNoOverlapTest() + { + this.verifier.actual(this.setup.sameAOIsNoOverlapAtlas(), + new OverlappingAOIPolygonCheck(inlineConfiguration)); + this.verifier.globallyVerify(flags -> Assert.assertEquals(0, flags.size())); + } + + @Test + public void sameAOIsMinOverlapTest() + { + this.verifier.actual(this.setup.sameAOIsMinOverlapAtlas(), + new OverlappingAOIPolygonCheck(inlineConfiguration)); + this.verifier.globallyVerify(flags -> Assert.assertEquals(0, flags.size())); + } + + @Test + public void sameAOIsTest() + { + this.verifier.actual(this.setup.sameAOIsAtlas(), + new OverlappingAOIPolygonCheck(inlineConfiguration)); + this.verifier.globallyVerify(flags -> Assert.assertEquals(1, flags.size())); + } + + @Test + public void similarAOIsTest() + { + this.verifier.actual(this.setup.similarAOIsAtlas(), + new OverlappingAOIPolygonCheck(inlineConfiguration)); + this.verifier.globallyVerify(flags -> Assert.assertEquals(1, flags.size())); + } + + @Test + public void differentAOIsTest() + { + this.verifier.actual(this.setup.differentAOIsAtlas(), + new OverlappingAOIPolygonCheck(inlineConfiguration)); + this.verifier.globallyVerify(flags -> Assert.assertEquals(0, flags.size())); + } +} diff --git a/src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTestRule.java b/src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTestRule.java new file mode 100644 index 000000000..00a4ae21d --- /dev/null +++ b/src/test/java/org/openstreetmap/atlas/checks/validation/areas/OverlappingAOIPolygonCheckTestRule.java @@ -0,0 +1,104 @@ +package org.openstreetmap.atlas.checks.validation.areas; + +import org.openstreetmap.atlas.geography.atlas.Atlas; +import org.openstreetmap.atlas.utilities.testing.CoreTestRule; +import org.openstreetmap.atlas.utilities.testing.TestAtlas; +import org.openstreetmap.atlas.utilities.testing.TestAtlas.Area; +import org.openstreetmap.atlas.utilities.testing.TestAtlas.Loc; + +/** + * Tests for {@link OverlappingAOIPolygonCheck} + * + * @author bbreithaupt + */ +public class OverlappingAOIPolygonCheckTestRule extends CoreTestRule +{ + private static final String TEST_1 = "47.244117672349,-122.396137421285"; + private static final String TEST_2 = "47.2434634265599,-122.396147058415"; + private static final String TEST_3 = "47.2434634265599,-122.395549556359"; + private static final String TEST_4 = "47.2441111299311,-122.395549556359"; + private static final String TEST_5 = "47.2449550951688,-122.396469902267"; + private static final String TEST_6 = "47.2442158085205,-122.396479539397"; + private static final String TEST_7 = "47.2440097223504,-122.395814577432"; + private static final String TEST_8 = "47.2449485528544,-122.395809758867"; + + @TestAtlas( + // areas + areas = { + @Area(id = "1000000001", coordinates = { @Loc(value = TEST_1), + @Loc(value = TEST_2), @Loc(value = TEST_3), + @Loc(value = TEST_4) }, tags = { "amenity=GRAVE_YARD" }), + @Area(id = "1002000001", coordinates = { @Loc(value = TEST_5), + @Loc(value = TEST_6), + @Loc(value = TEST_8) }, tags = { "amenity=GRAVE_YARD" }) }) + private Atlas sameAOIsNoOverlapAtlas; + + @TestAtlas( + // areas + areas = { + @Area(id = "1000000001", coordinates = { @Loc(value = TEST_1), + @Loc(value = TEST_2), @Loc(value = TEST_3), + @Loc(value = TEST_4) }, tags = { "amenity=GRAVE_YARD" }), + @Area(id = "1002000001", coordinates = { @Loc(value = TEST_5), + @Loc(value = TEST_6), @Loc(value = TEST_1), @Loc(value = TEST_4), + @Loc(value = TEST_8) }, tags = { "amenity=GRAVE_YARD" }) }) + private Atlas sameAOIsMinOverlapAtlas; + + @TestAtlas( + // areas + areas = { + @Area(id = "1000000001", coordinates = { @Loc(value = TEST_1), + @Loc(value = TEST_2), @Loc(value = TEST_3), + @Loc(value = TEST_4) }, tags = { "amenity=GRAVE_YARD" }), + @Area(id = "1002000001", coordinates = { @Loc(value = TEST_5), + @Loc(value = TEST_6), @Loc(value = TEST_7), + @Loc(value = TEST_8) }, tags = { "amenity=GRAVE_YARD" }) }) + private Atlas sameAOIsAtlas; + + @TestAtlas( + // areas + areas = { + @Area(id = "1000000001", coordinates = { @Loc(value = TEST_1), + @Loc(value = TEST_2), @Loc(value = TEST_3), + @Loc(value = TEST_4) }, tags = { "amenity=GRAVE_YARD" }), + @Area(id = "1002000001", coordinates = { @Loc(value = TEST_5), + @Loc(value = TEST_6), @Loc(value = TEST_7), + @Loc(value = TEST_8) }, tags = { "landuse=CEMETERY" }) }) + private Atlas similarAOIsAtlas; + + @TestAtlas( + // areas + areas = { + @Area(id = "1000000001", coordinates = { @Loc(value = TEST_1), + @Loc(value = TEST_2), @Loc(value = TEST_3), + @Loc(value = TEST_4) }, tags = { "amenity=GRAVE_YARD" }), + @Area(id = "1002000001", coordinates = { @Loc(value = TEST_5), + @Loc(value = TEST_6), @Loc(value = TEST_7), + @Loc(value = TEST_8) }, tags = { "tourism=ZOO" }) }) + private Atlas differentAOIsAtlas; + + public Atlas sameAOIsNoOverlapAtlas() + { + return this.sameAOIsNoOverlapAtlas; + } + + public Atlas sameAOIsMinOverlapAtlas() + { + return this.sameAOIsMinOverlapAtlas; + } + + public Atlas sameAOIsAtlas() + { + return this.sameAOIsAtlas; + } + + public Atlas similarAOIsAtlas() + { + return this.similarAOIsAtlas; + } + + public Atlas differentAOIsAtlas() + { + return this.differentAOIsAtlas; + } +}