diff --git a/src/main/java/gov/usgs/earthquake/nshmp/geo/Bounds.java b/src/main/java/gov/usgs/earthquake/nshmp/geo/Bounds.java
index bdb9962dfd37704363450f000feafc329c9c3f0c..1b458a109fd88ec5f6ba20aa2e348f189a6ac6d4 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/geo/Bounds.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/geo/Bounds.java
@@ -1,5 +1,7 @@
 package gov.usgs.earthquake.nshmp.geo;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -8,7 +10,7 @@ import java.util.Objects;
  * coordinate ({@link #min}) and an upper-right coordinate ({@link #max}).
  *
  * <p>Bounds are 2-dimensional in that the depth component of the corners will
- * always be 0. The bounds of any {@code Iterable<Location>} may be computed
+ * always be zero. The bounds of any {@code Iterable<Location>} may be computed
  * using {@link Locations#bounds(Iterable)}.
  *
  * @author U.S. Geological Survey
@@ -21,19 +23,31 @@ public final class Bounds {
   /** The upper right coordinate (maximum longitude and latitude). */
   public final Location max;
 
-  /*
-   * Bounds creation is restricted to the geo package so the burden of checking
-   * that min and max are actually min and max lies with classes that create
-   * bounds objects.
+  /**
+   * Create a new {@code Bounds} object.
+   *
+   * @param minLat minimum latitude
+   * @param minLon minimum longitude
+   * @param maxLat maximum latitude
+   * @param maxLon maximum longitude
    */
+  public static Bounds create(
+      double minLat,
+      double minLon,
+      double maxLat,
+      double maxLon) {
 
-  Bounds(Location min, Location max) {
-    this.min = min;
-    this.max = max;
+    checkArgument(minLat < maxLat, "minLat >= maxLat");
+    checkArgument(minLon < maxLon, "minLon >= maxLon");
+
+    return new Bounds(
+        Location.create(minLon, minLat),
+        Location.create(maxLon, maxLat));
   }
 
-  Bounds(double minLat, double minLon, double maxLat, double maxLon) {
-    this(Location.create(minLon, minLat), Location.create(maxLon, maxLat));
+  private Bounds(Location min, Location max) {
+    this.min = min;
+    this.max = max;
   }
 
   /**
@@ -71,9 +85,6 @@ public final class Bounds {
       return false;
     }
     Bounds other = (Bounds) obj;
-    if (min == other.min && max == other.max) {
-      return true;
-    }
     return min.equals(other.min) && max.equals(other.max);
   }
 
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/geo/Locations.java b/src/main/java/gov/usgs/earthquake/nshmp/geo/Locations.java
index d9b0a3cc8788d6930bb43053723903a6ca0f80b3..d08f23541c37cc0c1f58b84710229053fda96278 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/geo/Locations.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/geo/Locations.java
@@ -562,7 +562,7 @@ public final class Locations {
       minLat = loc.latitude < minLat ? loc.latitude : minLat;
       maxLat = loc.latitude > maxLat ? loc.latitude : maxLat;
     }
-    return new Bounds(minLat, minLon, maxLat, maxLon);
+    return Bounds.create(minLat, minLon, maxLat, maxLon);
   }
 
   /**
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 0bae48db783f0b1aa17a3a9a8a9450a1511f795b..f2af7202dd6a20f67e5a36030551212019ef3c61 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/geo/Region.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/geo/Region.java
@@ -289,7 +289,7 @@ public class Region {
    */
   public Bounds bounds() {
     Rectangle2D bounds = area.getBounds2D();
-    return new Bounds(
+    return Bounds.create(
         bounds.getMinY(),
         bounds.getMinX(),
         bounds.getMaxY(),
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/geo/BoundsTests.java b/src/test/java/gov/usgs/earthquake/nshmp/geo/BoundsTests.java
index fb04d6b067d834567569ed1641d6947c96ce356f..aac1b1990d11072957e6b3953733e72abdc7721a 100644
--- a/src/test/java/gov/usgs/earthquake/nshmp/geo/BoundsTests.java
+++ b/src/test/java/gov/usgs/earthquake/nshmp/geo/BoundsTests.java
@@ -3,7 +3,7 @@ package gov.usgs.earthquake.nshmp.geo;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import org.junit.jupiter.api.Test;
 
@@ -21,21 +21,27 @@ class BoundsTests {
 
   private static final Location MIN = Location.create(MIN_LON, MIN_LAT);
   private static final Location MAX = Location.create(MAX_LON, MAX_LAT);
-  private static final Location BAD = Location.create(0.0, 0.0);
 
   @Test
-  final void minMax() {
-    Bounds b = new Bounds(MIN, MAX);
-    assertSame(MIN, b.min);
-    assertSame(MAX, b.max);
-    b = new Bounds(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
+  void createIAE() {
+    assertThrows(IllegalArgumentException.class, () -> {
+      Bounds.create(MIN_LAT, MAX_LON, MAX_LAT, MIN_LON);
+    });
+    assertThrows(IllegalArgumentException.class, () -> {
+      Bounds.create(MAX_LAT, MIN_LON, MIN_LAT, MAX_LON);
+    });
+  }
+
+  @Test
+  void minMax() {
+    Bounds b = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
     assertEquals(MIN, b.min);
     assertEquals(MAX, b.max);
   }
 
   @Test
   void toList() {
-    Bounds b = new Bounds(MIN, MAX);
+    Bounds b = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
     LocationList bList = b.toList();
     // Expected bounds are anti-clockwise starting at the bottom left point
     LocationList oList = LocationList.of(
@@ -49,41 +55,38 @@ class BoundsTests {
 
   @Test
   void toArray() {
-    Bounds b = new Bounds(MIN, MAX);
+    Bounds b = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
     double[] coords = new double[] { MIN_LON, MIN_LAT, MAX_LON, MAX_LAT };
     assertArrayEquals(coords, b.toArray(), 0.0);
   }
 
   @Test
   void equalsTest() {
-    Bounds b1 = new Bounds(MIN, MAX);
+    Bounds b1 = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
     assertEquals(b1, b1);
     assertNotEquals(b1, null);
     assertNotEquals(b1, "test");
 
-    Bounds b2 = new Bounds(MIN, MAX);
+    Bounds b2 = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
     assertEquals(b1, b2);
-    b2 = new Bounds(MIN, BAD);
+    b2 = Bounds.create(MIN_LAT, MIN_LON, 0.0, 0.0);
     assertNotEquals(b1, b2);
-    b2 = new Bounds(BAD, MAX);
+    b2 = Bounds.create(0.0, 0.0, MAX_LAT, MAX_LON);
     assertNotEquals(b1, b2);
-
-    b2 = new Bounds(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
-    assertEquals(b1, b2);
   }
 
   @Test
   void hashCodeTest() {
-    Bounds b1 = new Bounds(MIN, MAX);
-    Bounds b2 = new Bounds(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
+    Bounds b1 = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
+    Bounds b2 = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
     assertEquals(b1.hashCode(), b2.hashCode());
-    b2 = new Bounds(0, 0, 2, 2);
+    b2 = Bounds.create(0, 0, 2, 2);
     assertNotEquals(b1.hashCode(), b2.hashCode());
   }
 
   @Test
   void toStringTest() {
-    Bounds b = new Bounds(MIN, MAX);
+    Bounds b = Bounds.create(MIN_LAT, MIN_LON, MAX_LAT, MAX_LON);
     assertEquals("[-10.0, -10.0, 10.0, 10.0]", b.toString());
   }