From 60c53dd07bcd907829343b2a2a8b3374c19daf82 Mon Sep 17 00:00:00 2001 From: Peter Powers <pmpowers@usgs.gov> Date: Thu, 27 Jan 2022 11:26:05 -0700 Subject: [PATCH] expanded map-region rectangle --- .../gov/usgs/earthquake/nshmp/calc/Sites.java | 34 +------- .../gov/usgs/earthquake/nshmp/geo/Region.java | 2 +- .../earthquake/nshmp/geo/json/GeoJson.java | 79 +++++++++++++++++++ .../earthquake/nshmp/model/HazardModel.java | 5 +- 4 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java b/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java index 28e892e5..29b8bac9 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/calc/Sites.java @@ -1,6 +1,5 @@ package gov.usgs.earthquake.nshmp.calc; -import static com.google.common.base.Preconditions.checkArgument; import static gov.usgs.earthquake.nshmp.Text.LOG_INDENT; import static gov.usgs.earthquake.nshmp.geo.BorderType.MERCATOR_LINEAR; import static java.util.stream.Collectors.toUnmodifiableList; @@ -188,7 +187,7 @@ public final class Sites { mapRegionIndex = 1; Feature mapPoly = features.get(0); LocationList mapPolyBorder = mapPoly.asPolygonBorder(); - mapBounds = Optional.of(validateExtents(mapPolyBorder).bounds()); + mapBounds = Optional.of(GeoJson.validateBoundingBox(mapPolyBorder).bounds()); boundsName = mapPoly.properties().getString(Site.Key.NAME).orElse("Map Extents"); } @@ -204,7 +203,7 @@ public final class Sites { Region calcRegion = null; try { - Bounds b = validateExtents(sitesPolyBorder).bounds(); + Bounds b = GeoJson.validateBoundingBox(sitesPolyBorder).bounds(); calcRegion = Regions.createRectangular(mapName, b.min, b.max); } catch (IllegalArgumentException iae) { calcRegion = Regions.create(mapName, sitesPolyBorder, MERCATOR_LINEAR); @@ -230,35 +229,6 @@ public final class Sites { .collect(toUnmodifiableList()); } - private static LocationList validateExtents(LocationList locations) { - - /* Rectangular linear ring check. */ - checkArgument( - locations.size() == 5, - "Extents polygon must contain 5 coordinates: %s", - locations); - - /* Order agnostic rectilinear lat-lon check. */ - Location p1 = locations.get(0); - Location p2 = locations.get(1); - Location p3 = locations.get(2); - Location p4 = locations.get(3); - boolean rectangular = (p1.latitude == p2.latitude) - ? (p3.latitude == p4.latitude && - p1.longitude == p4.longitude && - p2.longitude == p3.longitude) - : (p1.latitude == p4.latitude && - p2.latitude == p3.latitude && - p1.longitude == p2.longitude && - p3.longitude == p4.longitude); - checkArgument( - rectangular, - "Extents polygon does not define a lat-lon Mercator rectangle: %s", - locations); - - return locations; - } - private static final int TO_STRING_LIMIT = 5; private static final String SITE_INDENT = LOG_INDENT + " "; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/geo/Region.java b/src/main/java/gov/usgs/earthquake/nshmp/geo/Region.java index f65eb534..0bae48db 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/geo/Region.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/geo/Region.java @@ -435,7 +435,7 @@ public class Region { /* * Initialize a rectangular region from two opposing corners expanding north - * and east border slightly to satisfy constains operations + * and east border slightly to satisfy contains operations */ void initRectangular(Location loc1, Location loc2) { diff --git a/src/main/java/gov/usgs/earthquake/nshmp/geo/json/GeoJson.java b/src/main/java/gov/usgs/earthquake/nshmp/geo/json/GeoJson.java index 8f93c878..bab75c23 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/geo/json/GeoJson.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/geo/json/GeoJson.java @@ -23,6 +23,10 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import gov.usgs.earthquake.nshmp.Text; +import gov.usgs.earthquake.nshmp.geo.Bounds; +import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.geo.LocationList; +import gov.usgs.earthquake.nshmp.geo.Locations; /** * Entry point for creating and parsing <a href="http://geojson.org">GeoJSON</a> @@ -191,6 +195,81 @@ public abstract class GeoJson { return new FromUrl(json); } + /** + * Checks that bounding box location list contains 5 coordinates defining a + * mercator lat/lon rectangle. + * + * @param locations to check + * @return the supplied locaiton list + */ + public static LocationList validateBoundingBox(LocationList locations) { + + /* Rectangular linear ring check. */ + checkArgument( + locations.size() == 5, + "Extents polygon must contain 5 coordinates: %s", + locations); + + /* Order agnostic rectilinear lat-lon check. */ + Location p1 = locations.get(0); + Location p2 = locations.get(1); + Location p3 = locations.get(2); + Location p4 = locations.get(3); + boolean rectangular = (p1.latitude == p2.latitude) + ? (p3.latitude == p4.latitude && + p1.longitude == p4.longitude && + p2.longitude == p3.longitude) + : (p1.latitude == p4.latitude && + p2.latitude == p3.latitude && + p1.longitude == p2.longitude && + p3.longitude == p4.longitude); + checkArgument( + rectangular, + "Extents polygon does not define a lat-lon Mercator rectangle: %s", + locations); + + return locations; + } + + /** + * Checks that the supplied location list defines a rectangular lat/lon area + * and returns a slightly expanded version that will enclosepoints on the + * borders of the area. Points coincident with east and north edges of + * polygons are considered inside as a result. + */ + public static LocationList expandRectangularArea(LocationList locations) { + Bounds b = validateBoundingBox(locations).bounds(); + + double lat1 = b.min.latitude; + double lat2 = b.max.latitude; + double lon1 = b.min.longitude; + double lon2 = b.max.longitude; + + // checkArgument(lat1 != lat2, "Input lats cannot be the same"); + // checkArgument(lon1 != lon2, "Input lons cannot be the same"); + + double minLat = Math.min(lat1, lat2); + double minLon = Math.min(lon1, lon2); + double maxLat = Math.max(lat1, lat2); + double maxLon = Math.max(lon1, lon2); + double offset = Locations.TOLERANCE; + + // ternaries prevent exceedance of max lat-lon values + maxLat += (maxLat <= 90.0 - offset) ? offset : 0.0; + maxLon += (maxLon <= 180.0 - offset) ? offset : 0.0; + minLat -= (minLat >= -90.0 + offset) ? offset : 0.0; + minLon -= (minLon >= -180.0 + offset) ? offset : 0.0; + + LocationList expanded = LocationList.of( + Location.create(minLon, minLat), + Location.create(minLon, maxLat), + Location.create(maxLon, maxLat), + Location.create(maxLon, minLat)); + + // initBordered(locs, MERCATOR_LINEAR); + return expanded; + } + /** * A reusable GeoJSON builder. */ diff --git a/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java b/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java index 8634d422..98f2003d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/model/HazardModel.java @@ -33,8 +33,10 @@ import com.google.gson.annotations.SerializedName; import gov.usgs.earthquake.nshmp.calc.CalcConfig; import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.geo.LocationList; import gov.usgs.earthquake.nshmp.geo.Locations; import gov.usgs.earthquake.nshmp.geo.json.Feature; +import gov.usgs.earthquake.nshmp.geo.json.GeoJson; import gov.usgs.earthquake.nshmp.gmm.Gmm; import gov.usgs.earthquake.nshmp.gmm.GroundMotionModel; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; @@ -354,7 +356,8 @@ public final class HazardModel implements Iterable<SourceTree> { MapRegion(Feature feature) { this.feature = feature; - this.area = new Area(Locations.toPath(feature.asPolygonBorder())); + LocationList areaLocs = GeoJson.expandRectangularArea(feature.asPolygonBorder()); + this.area = new Area(Locations.toPath(areaLocs)); } boolean contains(Location loc) { -- GitLab