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/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazard.java b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazard.java index fa202d9d019e233979ab9e88de95d4e3d0922a47..5ee1f26dd08af76d6968f48b29821438a4d82884 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazard.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazard.java @@ -3,7 +3,6 @@ package gov.usgs.earthquake.nshmp.netcdf.reader; import static com.google.common.base.Preconditions.checkState; import java.util.EnumMap; -import java.util.Map; import gov.usgs.earthquake.nshmp.data.XySequence; import gov.usgs.earthquake.nshmp.gmm.Imt; @@ -20,9 +19,10 @@ public class StaticHazard extends EnumMap<Imt, XySequence> { putAll(staticHazard); } - public Map<Imt, XySequence> staticHazard() { - return Map.copyOf(this); - } + // unnecessary method? + // public Map<Imt, XySequence> staticHazard() { + // return Map.copyOf(this); + // } public static Builder builder() { return new Builder(); diff --git a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmGroupTests.java b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmGroupTests.java new file mode 100644 index 0000000000000000000000000000000000000000..a86458b65261c848d902a6593ec0871daf379a07 --- /dev/null +++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmGroupTests.java @@ -0,0 +1,16 @@ +package gov.usgs.earthquake.nshmp.netcdf; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class NshmGroupTests { + + @Test + final void testCONUS_2018() { + assertEquals(NshmGroup.CONUS_2018.toString(), "2018 Conterminous U.S. NSHM"); + assertEquals(NshmGroup.CONUS_2018.baseGroup(), "/CONUS/2018/v1.1"); + assertEquals(NshmGroup.CONUS_2018.locationPrecision(), 2); + } + +} 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..157504f786b00aeb5e8cb960ffbf7d332651b691 100644 --- a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmNetcdfReaderTests.java +++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/NshmNetcdfReaderTests.java @@ -20,17 +20,14 @@ import org.junit.jupiter.params.provider.MethodSource; import com.google.common.io.Resources; import gov.usgs.earthquake.nshmp.data.XySequence; -import gov.usgs.earthquake.nshmp.geo.BorderType; import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.geo.LocationList; -import gov.usgs.earthquake.nshmp.geo.Regions; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.netcdf.reader.BoundingHazards; -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()); @@ -41,13 +38,14 @@ class NshmNetcdfReaderTests { // difference tolerance, until we can incorporate precision into // NshmNetcdfReader - static final double IML_TOL = 1e-6; + public static final double IML_TOL = 1e-6; static final double HAZ_TOL = 1e-8; 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]) @@ -87,8 +85,8 @@ class NshmNetcdfReaderTests { new double[] { 4.1153710E-02, 3.8681961E-03, 1.2028426E-04, 3.5812084E-07 }, new double[] { 5.2503492E-02, 3.4418438E-03, 4.0267402E-05, 4.5693341E-08 }); - static final List<SiteClass> SITE_CLASSES = List.of(SiteClass.CD, SiteClass.C); - static final List<Imt> IMTS = List.of(Imt.PGA, Imt.SA0P4); + public static final List<SiteClass> SITE_CLASSES = List.of(SiteClass.CD, SiteClass.C); + public static final List<Imt> IMTS = List.of(Imt.PGA, Imt.SA0P4); static final int TARGET_LOWER_LEFT_LONGITUDE_IDX = 1; static final int TARGET_LOWER_LEFT_LATITUDE_IDX = 1; @@ -96,11 +94,12 @@ class NshmNetcdfReaderTests { static final double TARGET_LONGITUDE_FRAC; static final double TARGET_LATITUDE_FRAC; - static Map<SiteClass, Map<Imt, double[]>> IMLS = new HashMap<>(); + public static Map<SiteClass, Map<Imt, double[]>> IMLS = new HashMap<>(); static BoundingHazards BOUNDING_HAZARDS; - static final NshmNetcdfReader NETCDF = new NshmNetcdfReader(NshmGroup.CONUS_2018, NETCDF_PATH); + public static final NshmNetcdfReader NETCDF = + new NshmNetcdfReader(NshmGroup.CONUS_2018, NETCDF_PATH); static { var builder = BoundingHazards.builder(); @@ -154,57 +153,6 @@ class NshmNetcdfReaderTests { assertTrue(NshmGroup.CONUS_2018.equals(NETCDF.nshmGroup())); } - @Test - final void coordinatesTests() { - var coords = NETCDF.coordinates(); - - // Check locations - assertArrayEquals(EXPECTED_LATITUDES, coords.latitudes()); - assertArrayEquals(EXPECTED_LONGITUDES, coords.longitudes()); - - // Check IMTs - assertEquals(IMTS, coords.imts()); - assertEquals(IMTS.size(), coords.imts().size()); - - // Check site classes - assertEquals(SITE_CLASSES, coords.siteClasses()); - - // Check region - var border = NetcdfUtils.buildBorder(EXPECTED_LONGITUDES, EXPECTED_LATITUDES); - var expectedRegion = Regions.create("test-region", border, BorderType.MERCATOR_LINEAR); - var expectedBorder = expectedRegion.border(); - - var actualRegion = coords.region(); - var actualBorder = actualRegion.border(); - - assertEquals(expectedBorder.size(), actualBorder.size()); - assertEquals(expectedBorder.bounds().max, expectedBorder.bounds().max); - assertEquals(expectedBorder.bounds().min, expectedBorder.bounds().min); - - for (var i = 0; i < expectedBorder.size(); i++) { - assertEquals(expectedBorder.get(i), actualBorder.get(i)); - } - - // Check IMLs - var actualImls = coords.imls(); - assertEquals(IMLS.size(), actualImls.size()); - - for (var imlEntry : IMLS.entrySet()) { - var siteClass = imlEntry.getKey(); - assertTrue(actualImls.containsKey(siteClass)); - assertEquals(imlEntry.getValue().size(), actualImls.get(siteClass).size()); - - for (var siteEntry : imlEntry.getValue().entrySet()) { - var imt = siteEntry.getKey(); - var expectedValue = siteEntry.getValue(); - var actualValue = actualImls.get(siteClass).get(imt); - - assertTrue(actualImls.get(siteClass).containsKey(imt)); - assertArrayEquals(expectedValue, actualValue, IML_TOL); - } - } - } - @Test final void pathTests() { assertEquals(NETCDF_PATH, NETCDF.path()); diff --git a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/BoundingHazardsTests.java b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/BoundingHazardsTests.java new file mode 100644 index 0000000000000000000000000000000000000000..ea5864ed9e6bea5e774a517fad767b5ac8b744ec --- /dev/null +++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/BoundingHazardsTests.java @@ -0,0 +1,17 @@ +package gov.usgs.earthquake.nshmp.netcdf.reader; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class BoundingHazardsTests { + + @Test + final void builderTest() { + // calling build() on empty builder should fail + assertThrows(IllegalStateException.class, () -> { + var boundingHazards = BoundingHazards.builder().build(); + }); + } + +} diff --git a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfCoordinatesTest.java b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfCoordinatesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bde5ef19762bd22cdc898679c9d40f515671f08b --- /dev/null +++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfCoordinatesTest.java @@ -0,0 +1,68 @@ +package gov.usgs.earthquake.nshmp.netcdf.reader; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import gov.usgs.earthquake.nshmp.geo.BorderType; +import gov.usgs.earthquake.nshmp.geo.Regions; +import gov.usgs.earthquake.nshmp.netcdf.NshmNetcdfReaderTests; + +class NetcdfCoordinatesTest { + + @Test + final void coordinatesTests() { + var coords = NshmNetcdfReaderTests.NETCDF.coordinates(); + + // Check locations + assertArrayEquals(NshmNetcdfReaderTests.EXPECTED_LATITUDES, coords.latitudes()); + assertArrayEquals(NshmNetcdfReaderTests.EXPECTED_LONGITUDES, coords.longitudes()); + + // Check IMTs + assertEquals(NshmNetcdfReaderTests.IMTS, coords.imts()); + assertEquals(NshmNetcdfReaderTests.IMTS.size(), coords.imts().size()); + + // Check site classes + assertEquals(NshmNetcdfReaderTests.SITE_CLASSES, coords.siteClasses()); + + // Check region + var border = NetcdfUtils.buildBorder( + NshmNetcdfReaderTests.EXPECTED_LONGITUDES, + NshmNetcdfReaderTests.EXPECTED_LATITUDES); + var expectedRegion = Regions.create("test-region", border, BorderType.MERCATOR_LINEAR); + var expectedBorder = expectedRegion.border(); + + var actualRegion = coords.region(); + var actualBorder = actualRegion.border(); + + assertEquals(expectedBorder.size(), actualBorder.size()); + assertEquals(expectedBorder.bounds().max, actualBorder.bounds().max); + assertEquals(expectedBorder.bounds().min, actualBorder.bounds().min); + + for (var i = 0; i < expectedBorder.size(); i++) { + assertEquals(expectedBorder.get(i), actualBorder.get(i)); + } + + // Check IMLs + var actualImls = coords.imls(); + assertEquals(NshmNetcdfReaderTests.IMLS.size(), actualImls.size()); + + for (var imlEntry : NshmNetcdfReaderTests.IMLS.entrySet()) { + var siteClass = imlEntry.getKey(); + assertTrue(actualImls.containsKey(siteClass)); + assertEquals(imlEntry.getValue().size(), actualImls.get(siteClass).size()); + + for (var siteEntry : imlEntry.getValue().entrySet()) { + var imt = siteEntry.getKey(); + var expectedValue = siteEntry.getValue(); + var actualValue = actualImls.get(siteClass).get(imt); + + assertTrue(actualImls.get(siteClass).containsKey(imt)); + assertArrayEquals(expectedValue, actualValue, NshmNetcdfReaderTests.IML_TOL); + } + } + } + +} 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 0f7357d6bb1f9391bcff56c17d1705ffd9d66fab..9e0f818a5856aac68584779c430ed4d6dbb57100 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())); diff --git a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazardTests.java b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazardTests.java new file mode 100644 index 0000000000000000000000000000000000000000..aaba2e59cb25ec881f44d3b00fe533ee66ce368b --- /dev/null +++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazardTests.java @@ -0,0 +1,17 @@ +package gov.usgs.earthquake.nshmp.netcdf.reader; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class StaticHazardTests { + + @Test + final void builderTest() { + // calling build() on empty builder should fail + assertThrows(IllegalStateException.class, () -> { + var staticHazard = StaticHazard.builder().build(); + }); + } + +} diff --git a/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazardsTests.java b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazardsTests.java new file mode 100644 index 0000000000000000000000000000000000000000..7bedfabc839ab4112ca93dcfcc38ff0f227d3bc6 --- /dev/null +++ b/src/test/java/gov/usgs/earthquake/nshmp/netcdf/reader/StaticHazardsTests.java @@ -0,0 +1,17 @@ +package gov.usgs.earthquake.nshmp.netcdf.reader; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class StaticHazardsTests { + + @Test + final void builderTest() { + // calling build() on empty builder should fail + assertThrows(IllegalStateException.class, () -> { + var staticHazards = StaticHazards.builder().build(); + }); + } + +}