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 28e892e5c97e205dc1133aba502796cc303ae784..29b8bac9adbf72a72110a4db45c2029bc1123224 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 f65eb5342bba674afc3934252ad4ac2bfd9619c6..0bae48db783f0b1aa17a3a9a8a9450a1511f795b 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 8f93c878386daac919eeefb4748306c9f9b71843..bab75c23c139df69fe62a50aaa42bf1b7e16573d 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 8634d4229ac0292cab863749fd9476bfe39d74f6..98f2003d3f63869675306d050efe9afa6c5ceb51 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) {