diff --git a/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java
index 945557ca5b6bae9a54f60431981b66d9c2f5a225..dfd4f874f7117dca03d0f22a3383826bc1d3dd2a 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java
@@ -18,6 +18,9 @@ import ucar.nc2.Group;
 
 public class NetcdfUtils {
 
+  // Tolerance for longitude/latitude comparisons
+  static final double LOCATION_TOLERANCE = 0.000001;
+
   /**
    * Creates a border going clockwise of the given longitudes and latitudes.
    * 
@@ -32,15 +35,16 @@ public class NetcdfUtils {
       builder.add(lat, longitudes[0]);
     }
 
-    for (var lon : longitudes) {
-      builder.add(latitudes[latitudes.length - 1], lon);
+    // omit duplicate points at corners
+    for (var i = 1; i < longitudes.length; i++) {
+      builder.add(latitudes[latitudes.length - 1], longitudes[i]);
     }
 
-    for (var i = latitudes.length - 1; i >= 0; i--) {
+    for (var i = latitudes.length - 2; i >= 0; i--) {
       builder.add(latitudes[i], longitudes[longitudes.length - 1]);
     }
 
-    for (var i = longitudes.length - 1; i >= 0; i--) {
+    for (var i = longitudes.length - 2; i >= 0; i--) {
       builder.add(latitudes[0], longitudes[i]);
     }
 
@@ -94,7 +98,6 @@ public class NetcdfUtils {
   static int getIdxLTEQ(double[] a, double t) {
     // assumes array is in sorted order (a[i] < a[i+1])
     // make sure target is within the range of the array
-    var tol = 0.000001;
     var n = a.length;
 
     if (t < a[0] || t > a[n - 1]) {
@@ -105,11 +108,11 @@ public class NetcdfUtils {
     }
 
     // if (t == a[0]) {
-    if (DoubleMath.fuzzyEquals(a[0], t, tol)) {
+    if (DoubleMath.fuzzyEquals(a[0], t, LOCATION_TOLERANCE)) {
       return 0;
     }
     // if (t == a[n - 1]) {
-    if (DoubleMath.fuzzyEquals(a[n - 1], t, tol)) {
+    if (DoubleMath.fuzzyEquals(a[n - 1], t, LOCATION_TOLERANCE)) {
       return n - 2; // return second to last index number
     }
 
@@ -129,12 +132,10 @@ public class NetcdfUtils {
    * Calculate fractional distance from a1 to t, between a1 and a2
    */
   static double calcFrac(double a1, double a2, double t) {
-    var tol = 0.00001;
-
-    if (Math.abs(t - a1) < tol) {
+    if (Math.abs(t - a1) < LOCATION_TOLERANCE) {
       // target value == a1
       return 0.0;
-    } else if (Math.abs(t - a2) < tol) {
+    } else if (Math.abs(t - a2) < LOCATION_TOLERANCE) {
       // target value == a2
       return 1.0;
     } else {
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmNetcdfReaderTests.java b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmNetcdfReaderTests.java
index 0584f365eab948ee73ac7d531077811c6c5cac09..d02c735f71a62610233e0b4d9a560aab4f28c716 100644
--- a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmNetcdfReaderTests.java
+++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmNetcdfReaderTests.java
@@ -30,7 +30,7 @@ import gov.usgs.earthquake.nshmp.netcdf.reader.NetcdfUtils;
 import gov.usgs.earthquake.nshmp.netcdf.reader.StaticHazard;
 import gov.usgs.earthquake.nshmp.netcdf.reader.StaticHazards;
 
-class NshmNetcdfReaderTests {
+public class NshmNetcdfReaderTests {
 
   static final String CONUS_TEST_FILE = "conus-test.nc";
   static final Path NETCDF_PATH = Paths.get(Resources.getResource(CONUS_TEST_FILE).getPath());
@@ -46,8 +46,9 @@ class NshmNetcdfReaderTests {
 
   static final Location TARGET_LOCATION = Location.create(39.213, -105.234);
 
-  static final double[] EXPECTED_LONGITUDES = new double[] { -105.3, -105.25, -105.2, -105.15 };
-  static final double[] EXPECTED_LATITUDES = new double[] { 39.15, 39.2, 39.25, 39.3 };
+  public static final double[] EXPECTED_LONGITUDES =
+      new double[] { -105.3, -105.25, -105.2, -105.15 };
+  public static final double[] EXPECTED_LATITUDES = new double[] { 39.15, 39.2, 39.25, 39.3 };
 
   static final LocationList BOUNDING_LOCATIONS = LocationList.builder()
       .add(EXPECTED_LATITUDES[1], EXPECTED_LONGITUDES[1])
diff --git a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtilsTests.java b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtilsTests.java
index 00f90792ef6b978b0290105d701675a805022459..28543f08b9e96de11c1c272550fa6c31d7e38c73 100644
--- a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtilsTests.java
+++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtilsTests.java
@@ -11,6 +11,7 @@ import java.util.List;
 import org.junit.jupiter.api.Test;
 
 import gov.usgs.earthquake.nshmp.data.XySequence;
+import gov.usgs.earthquake.nshmp.geo.LocationList;
 import gov.usgs.earthquake.nshmp.gmm.Imt;
 import gov.usgs.earthquake.nshmp.netcdf.SiteClass;
 
@@ -25,6 +26,10 @@ class NetcdfUtilsTests {
       39.13, 39.14, 39.15, 39.16, 39.17, 39.18, 39.19, 39.20, 39.21, 39.22, 39.23, 39.24, 39.25,
       39.26, 39.27, 39.28, 39.29, 39.30, 39.31, 39.32, 39.33, 39.34, 39.35 };
 
+  private static final double[] BORDER_LONGITUDES = new double[] { 5.0, 6.0 };
+  private static final double[] BORDER_LATITUDES = new double[] { 8.0, 9.0 };
+  private static final LocationList BORDER_LOCATIONS;
+
   private static final double TOL = 1e-7;
 
   static StaticHazards mapHaz0;
@@ -38,6 +43,15 @@ class NetcdfUtilsTests {
   private static final double FRAC = 0.5;
 
   static {
+
+    BORDER_LOCATIONS = LocationList.builder()
+        .add(BORDER_LATITUDES[0], BORDER_LONGITUDES[0])
+        .add(BORDER_LATITUDES[1], BORDER_LONGITUDES[0])
+        .add(BORDER_LATITUDES[1], BORDER_LONGITUDES[1])
+        .add(BORDER_LATITUDES[0], BORDER_LONGITUDES[1])
+        .add(BORDER_LATITUDES[0], BORDER_LONGITUDES[0])
+        .build();
+
     var siteClasses = List.of(SiteClass.B, SiteClass.C, SiteClass.D);
     var imts = List.of(Imt.PGA, Imt.SA0P1, Imt.SA1P5);
     var imlValues = new double[] { 0.1, 0.5, 0.75 };
@@ -106,7 +120,12 @@ class NetcdfUtilsTests {
   }
 
   @Test
-  final void testGetIdxLTEQ() {
+  final void buildBorderTest() {
+    assertEquals(BORDER_LOCATIONS, NetcdfUtils.buildBorder(BORDER_LONGITUDES, BORDER_LATITUDES));
+  }
+
+  @Test
+  final void getIdxLTEQTest() {
     // target is out of range, expect IAE
     assertThrows(IllegalArgumentException.class, () -> {
       NetcdfUtils.getIdxLTEQ(LONGITUDES, -100.0);
@@ -134,7 +153,25 @@ class NetcdfUtilsTests {
   }
 
   @Test
-  final void testCalcGridFrac() {
+  final void calcFracTest() {
+    double bottom = 5.0;
+    double top = bottom + 1.0;
+    double frac = 0.36;
+    assertEquals(0.0, NetcdfUtils.calcFrac(bottom, top, bottom));
+    assertEquals(1.0, NetcdfUtils.calcFrac(bottom, top, top));
+    assertEquals(0.0, NetcdfUtils.calcFrac(
+        bottom,
+        top,
+        bottom + NetcdfUtils.LOCATION_TOLERANCE * 0.9));
+    assertEquals(1.0, NetcdfUtils.calcFrac(
+        bottom,
+        top,
+        top - NetcdfUtils.LOCATION_TOLERANCE * 0.9));
+    assertEquals(frac, NetcdfUtils.calcFrac(bottom, top, bottom + frac), 1e-4);
+  }
+
+  @Test
+  final void calcGridFracTest() {
     double f = 0.13;
     int i = 4;
     assertEquals(f,
@@ -147,7 +184,7 @@ class NetcdfUtilsTests {
   }
 
   @Test
-  final void testLinearInterpolate() {
+  final void linearInterpolateTest() {
     var actual = NetcdfUtils.linearInterpolate(mapHaz0, mapHaz1, FRAC);
     assertTrue(mapHazTarget.keySet().containsAll(actual.keySet()));