From a0600eba9249c6e6918614188f19a9708fdad983 Mon Sep 17 00:00:00 2001
From: Brandon Clayton <bclayton@usgs.gov>
Date: Wed, 4 Aug 2021 17:25:22 -0600
Subject: [PATCH] Crete hazard map data sets

---
 .../gov/usgs/earthquake/nshmp/HazardMaps.java | 168 ++++++++++++++++++
 1 file changed, 168 insertions(+)
 create mode 100644 src/main/java/gov/usgs/earthquake/nshmp/HazardMaps.java

diff --git a/src/main/java/gov/usgs/earthquake/nshmp/HazardMaps.java b/src/main/java/gov/usgs/earthquake/nshmp/HazardMaps.java
new file mode 100644
index 000000000..9c543e04d
--- /dev/null
+++ b/src/main/java/gov/usgs/earthquake/nshmp/HazardMaps.java
@@ -0,0 +1,168 @@
+package gov.usgs.earthquake.nshmp;
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import gov.usgs.earthquake.nshmp.data.Interpolator;
+
+/**
+ * Utility class to create hazard map datasets from a hazard curve results.
+ * Methods in class assume *.csv curve files have no comments and have a header
+ * row that starts with {@code "name,lon,lat,..."} or {@code "lon,lat,..."}.
+ *
+ * @author U.S. Geological Survey
+ */
+public class HazardMaps {
+
+  private static final String COMMA = ",";
+  private static final String CURVES_FILE = "curves.csv";
+  private static final String MAP_FILE = "map.csv";
+  private static final Interpolator INTERPOLATOR = Interpolator.builder()
+      .logx()
+      .logy()
+      .decreasingX()
+      .build();
+
+  private HazardMaps() {}
+
+  /**
+   * Command line application to create a file of return period slices through a
+   * hazard curve dataset. Result of slicing job is saved to a {@code map.csv}
+   * file in the same directory as the source.
+   *
+   * @param args a path to a hazard curve result file or directory. If the
+   *        supplied path is a directory, application will recurse through file
+   *        tree slicing each {@code curves.csv} file encountered.
+   */
+  public static void main(String[] args) {
+    if (args.length < 2) {
+      System.out.println("Usage: Supply a path to a file of hazard curve results and");
+      System.out.println("       a space separated list of return periods (in yr)");
+    }
+
+    Path curvesPath = Path.of(args[0]);
+    List<Integer> returnPeriods = Arrays.stream(args)
+        .skip(1)
+        .mapToInt(Integer::valueOf)
+        .boxed()
+        .collect(Collectors.toList());
+
+    try {
+      createDataSets(curvesPath, returnPeriods);
+    } catch (Exception e) {
+      System.out.println("Processing Error");
+      System.out.println("Arguments: " + Arrays.toString(args));
+      e.printStackTrace();
+    }
+  }
+
+  static void createDataSets(Path curvesPath, List<Integer> returnPeriods) throws IOException {
+    if (Files.isDirectory(curvesPath)) {
+      CurvesVisitor curvesFinder = new CurvesVisitor(returnPeriods);
+      Files.walkFileTree(curvesPath, curvesFinder);
+    } else {
+      processCurveFile(curvesPath, returnPeriods);
+    }
+  }
+
+  private static List<String> create(List<String> lines, List<Integer> returnPeriods) {
+    int headerCount = lines.get(0).startsWith("name") ? 3 : 2;
+    List<String> header = Arrays.asList(lines.get(0).split(COMMA));
+
+    String siteStr = header.subList(0, headerCount)
+        .stream()
+        .collect(Collectors.joining(COMMA));
+
+    double[] imls = header.subList(headerCount, header.size())
+        .stream()
+        .mapToDouble(Double::valueOf)
+        .toArray();
+
+    StringBuilder mapHeader = new StringBuilder(siteStr);
+    returnPeriods.forEach(rp -> mapHeader.append(COMMA).append(rp));
+
+    List<String> linesOut = new ArrayList<>(lines.size());
+    linesOut.add(mapHeader.toString());
+
+    Slicer slicer = new Slicer(returnPeriods, imls, headerCount);
+
+    lines.stream()
+        .skip(1)
+        .map(slicer::slice)
+        .forEach(linesOut::add);
+
+    return linesOut;
+  }
+
+  private static void processCurveFile(Path curves, List<Integer> returnPeriods) {
+    try (Stream<String> stream = Files.lines(curves)) {
+      List<String> linesIn = stream.collect(Collectors.toList());
+      List<String> linesOut = create(linesIn, returnPeriods);
+      Path maps = curves.resolveSibling(MAP_FILE);
+      Files.write(maps, linesOut);
+    } catch (IOException ioe) {
+      throw new RuntimeException(ioe);
+    }
+  }
+
+  private static class CurvesVisitor extends SimpleFileVisitor<Path> {
+    List<Integer> returnPeriods;
+
+    public CurvesVisitor(List<Integer> returnPeriods) {
+      this.returnPeriods = returnPeriods;
+    }
+
+    @Override
+    public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
+      Path fileName = path.getFileName();
+      if (fileName != null && fileName.endsWith(CURVES_FILE)) {
+        processCurveFile(path, returnPeriods);
+      }
+      return FileVisitResult.CONTINUE;
+    }
+  }
+
+  private static class Slicer {
+    private final List<Integer> returnPeriods;
+    private final double[] imls;
+    private final int headerCount;
+
+    private Slicer(List<Integer> returnPeriods, double imls[], int headerCount) {
+      this.returnPeriods = returnPeriods;
+      this.imls = imls;
+      this.headerCount = headerCount;
+    }
+
+    private String slice(String line) {
+      List<String> elements = Arrays.asList(line.split(COMMA));
+      String siteStr = elements.subList(0, headerCount)
+        .stream()
+        .collect(Collectors.joining(COMMA));
+
+      StringBuilder lineOut = new StringBuilder(siteStr);
+
+      double[] rates = elements
+          .stream()
+          .skip(headerCount)
+          .mapToDouble(Double::valueOf)
+          .toArray();
+
+      for (double returnPeriod : returnPeriods) {
+        lineOut.append(COMMA);
+        lineOut.append(INTERPOLATOR.findX(imls, rates, 1 / returnPeriod));
+      }
+
+      return lineOut.toString();
+    }
+  }
+
+}
-- 
GitLab