diff --git a/README.md b/README.md index 8ecf632d..8b8773d5 100644 --- a/README.md +++ b/README.md @@ -42,18 +42,18 @@ We don't presently deploy versioned artifacts into a public repository like the #### **As a single runnable JAR** ```shell -java -jar coda-calibration/calibration-standalone/target/calibration-standalone-1.0.7-runnable.jar +java -jar coda-calibration/calibration-standalone/target/calibration-standalone-1.0.8-runnable.jar ``` #### **GUI alone** ```shell -java -jar coda-calibration/calibration-gui/target/calibration-gui-1.0.7-runnable.jar +java -jar coda-calibration/calibration-gui/target/calibration-gui-1.0.8-runnable.jar ``` #### **Calibration REST service alone** ```shell -java -jar coda-calibration/calibration-service/application/target/application-1.0.7-runnable.jar +java -jar coda-calibration/calibration-service/application/target/application-1.0.8-runnable.jar ``` #### A note about HTTPS diff --git a/calibration-gui/pom.xml b/calibration-gui/pom.xml index fe3693f8..996231b2 100644 --- a/calibration-gui/pom.xml +++ b/calibration-gui/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 calibration-gui diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/AppProperties.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/AppProperties.java index 2bed41c6..52ceba8b 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/AppProperties.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/AppProperties.java @@ -18,9 +18,11 @@ import java.util.List; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; import gov.llnl.gnem.apps.coda.common.mapping.WMSLayerDescriptor; +@Component @ConfigurationProperties("app") public class AppProperties { diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/CodaGuiController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/CodaGuiController.java index 04ab2eaa..689ac5ec 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/CodaGuiController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/CodaGuiController.java @@ -25,6 +25,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import javax.annotation.PreDestroy; @@ -43,6 +44,8 @@ import gov.llnl.gnem.apps.coda.calibration.gui.controllers.MeasuredMwsController; import gov.llnl.gnem.apps.coda.calibration.gui.controllers.PathController; import gov.llnl.gnem.apps.coda.calibration.gui.controllers.ReferenceEventLoadingController; +import gov.llnl.gnem.apps.coda.calibration.gui.controllers.RefreshableController; +import gov.llnl.gnem.apps.coda.calibration.gui.controllers.ScreenshotEnabledController; import gov.llnl.gnem.apps.coda.calibration.gui.controllers.ShapeController; import gov.llnl.gnem.apps.coda.calibration.gui.controllers.SiteController; import gov.llnl.gnem.apps.coda.calibration.gui.controllers.parameters.ParametersController; @@ -59,15 +62,19 @@ import gov.llnl.gnem.apps.coda.common.gui.data.client.api.WaveformClient; import gov.llnl.gnem.apps.coda.common.gui.events.ShowFailureReportEvent; import gov.llnl.gnem.apps.coda.common.gui.util.ProgressMonitor; +import gov.llnl.gnem.apps.coda.common.gui.util.SnapshotUtils; import gov.llnl.gnem.apps.coda.common.mapping.api.GeoMap; +import gov.llnl.gnem.apps.coda.common.model.domain.Pair; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.CheckMenuItem; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; import javafx.scene.input.TransferMode; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; @@ -83,11 +90,15 @@ public class CodaGuiController { private WaveformGui waveformGui; private DataController data; + private ParametersController param; private ShapeController shape; private PathController path; private SiteController site; private MeasuredMwsController measuredMws; + @FXML + private TabPane mainTabPane; + @FXML private Tab dataTab; @@ -106,13 +117,8 @@ public class CodaGuiController { @FXML private Tab measuredMwsTab; - private Runnable dataRefresh; - private Runnable paramRefresh; - private Runnable shapeRefresh; - private Runnable pathRefresh; - private Runnable siteRefresh; - private Runnable measuredMwsRefresh; private Runnable activeTabRefresh; + private Consumer activeTabScreenshot; @FXML private Button showMapButton; @@ -120,6 +126,9 @@ public class CodaGuiController { @FXML private Button refreshButton; + @FXML + private Button snapshotButton; + @FXML private CheckMenuItem waveformFocus; @@ -140,6 +149,8 @@ public class CodaGuiController { private CalibrationClient calibrationClient; private DirectoryChooser sacDirFileChooser = new DirectoryChooser(); + private DirectoryChooser screenshotFolderChooser = new DirectoryChooser(); + private FileChooser sacFileChooser = new FileChooser(); private FileChooser codaParamsFileChooser = new FileChooser(); private FileChooser codaJsonParamsFileChooser = new FileChooser(); @@ -179,6 +190,7 @@ public CodaGuiController(GeoMap mapController, WaveformClient waveformClient, En this.paramExporter = paramExporter; this.waveformGui = waveformGui; this.data = data; + this.param = param; this.shape = shape; this.path = path; this.site = site; @@ -186,15 +198,11 @@ public CodaGuiController(GeoMap mapController, WaveformClient waveformClient, En this.bus = bus; bus.register(this); - dataRefresh = data.getRefreshFunction(); - paramRefresh = param.getRefreshFunction(); - shapeRefresh = shape.getRefreshFunction(); - pathRefresh = path.getRefreshFunction(); - siteRefresh = site.getRefreshFunction(); - measuredMwsRefresh = measuredMws.getRefreshFunction(); - activeTabRefresh = dataRefresh; + activeTabRefresh = data.getRefreshFunction(); sacDirFileChooser.setTitle("Coda STACK File Directory"); + screenshotFolderChooser.setTitle("Screenshot Export Folder"); + sacFileChooser.getExtensionFilters().add(new ExtensionFilter("Coda STACK Files (.sac,.env)", "*.sac", "*.env")); sacFileChooser.getExtensionFilters().add(allFilesFilter); @@ -301,7 +309,7 @@ private void openMdacFiWindow() { @FXML private void runCalibration() { - calibrationClient.runCalibration(Boolean.FALSE).subscribe(value -> log.trace(value), err -> log.trace(err.getMessage(), err)); + calibrationClient.runCalibration(Boolean.FALSE).doOnError(err -> log.trace(err.getMessage(), err)).subscribe(); } @FXML @@ -311,12 +319,14 @@ private void measureMws() { @FXML private void clearData() { - calibrationClient.clearData().subscribe(value -> log.trace(value), err -> log.trace(err.getMessage(), err), () -> dataRefresh.run()); + calibrationClient.clearData().subscribe(val -> { + }, err -> log.trace(err.getMessage(), err), () -> data.getRefreshFunction().run()); } @FXML private void runAutoPickingCalibration() { - calibrationClient.runCalibration(Boolean.TRUE).subscribe(value -> log.trace(value), err -> log.trace(err.getMessage(), err)); + calibrationClient.runCalibration(Boolean.TRUE).subscribe(value -> { + }, err -> log.trace(err.getMessage(), err)); } @FXML @@ -329,20 +339,25 @@ public void initialize() { activeMapIcon = makeMapLabel(); showMapIcon = makeMapLabel(); - addMapEnabledTabListeners(dataTab, data, dataRefresh); + snapshotButton.setGraphic(makeSnapshotLabel()); + snapshotButton.setContentDisplay(ContentDisplay.CENTER); + + addEnabledTabListeners(dataTab, data); + activeTabScreenshot = (folder) -> SnapshotUtils.writePng(folder, new Pair<>(dataTab.getText(), dataTab.getContent())); data.setVisible(true); paramTab.setOnSelectionChanged(e -> { if (paramTab.isSelected()) { mapController.clearIcons(); - activeTabRefresh = paramRefresh; + activeTabRefresh = param.getRefreshFunction(); + activeTabScreenshot = (folder) -> SnapshotUtils.writePng(folder, new Pair<>(paramTab.getText(), paramTab.getContent())); } }); - addMapEnabledTabListeners(shapeTab, shape, shapeRefresh); - addMapEnabledTabListeners(pathTab, path, pathRefresh); - addMapEnabledTabListeners(siteTab, site, siteRefresh); - addMapEnabledTabListeners(measuredMwsTab, measuredMws, measuredMwsRefresh); + addEnabledTabListeners(shapeTab, shape); + addEnabledTabListeners(pathTab, path); + addEnabledTabListeners(siteTab, site); + addEnabledTabListeners(measuredMwsTab, measuredMws); rootElement.setOnDragOver(event -> { if (event.getGestureSource() != rootElement && event.getDragboard().hasFiles()) { @@ -370,13 +385,22 @@ public void initialize() { } - private void addMapEnabledTabListeners(Tab tab, MapListeningController controller, Runnable runnable) { + private void addEnabledTabListeners(Tab tab, MapListeningController controller) { tab.setOnSelectionChanged(e -> { if (tab.isSelected()) { controller.setVisible(true); controller.refreshView(); tab.setGraphic(activeMapIcon); - activeTabRefresh = runnable; + if (controller instanceof RefreshableController) { + activeTabRefresh = ((RefreshableController) controller).getRefreshFunction(); + } else { + activeTabRefresh = () -> controller.refreshView(); + } + if (controller instanceof ScreenshotEnabledController) { + activeTabScreenshot = ((ScreenshotEnabledController) controller).getScreenshotFunction(); + } else { + activeTabScreenshot = (folder) -> SnapshotUtils.writePng(folder, new Pair<>(tab.getText(), tab.getContent())); + } } else { tab.setGraphic(null); controller.setVisible(false); @@ -389,6 +413,19 @@ private void refreshTab(ActionEvent e) { activeTabRefresh.run(); } + @FXML + private void snapshotTab(ActionEvent e) { + File folder = screenshotFolderChooser.showDialog(rootElement.getScene().getWindow()); + try { + if (folder != null && folder.exists() && folder.isDirectory() && folder.canWrite()) { + screenshotFolderChooser.setInitialDirectory(folder); + Platform.runLater(() -> activeTabScreenshot.accept(folder)); + } + } catch (SecurityException ex) { + log.warn("Exception trying to write screenshots to folder {} : {}", folder, ex.getLocalizedMessage(), ex); + } + } + private Label makeMapLabel() { Label mapLabel = new Label("\uE55B"); mapLabel.getStyleClass().add("material-icons-medium"); @@ -405,6 +442,14 @@ private Label makeRefreshLabel() { return label; } + private Label makeSnapshotLabel() { + Label label = new Label("\uE3B0"); + label.getStyleClass().add("material-icons-medium"); + label.setMaxHeight(16); + label.setMinWidth(16); + return label; + } + //TODO: Move this to a controller @Subscribe private void listener(CalibrationStatusEvent event) { diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/WebfluxConfig.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/WebfluxConfig.java index 35732d3f..9b7df1d4 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/WebfluxConfig.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/WebfluxConfig.java @@ -58,8 +58,12 @@ public WebfluxConfig(ObjectMapper objectMapper) { @Bean public ExchangeStrategies configureJacksonExchangeStrategies() { return ExchangeStrategies.builder().codecs(clientCodecConfigurer -> { - clientCodecConfigurer.customCodecs().decoder(new Jackson2JsonDecoder(objectMapper)); + Jackson2JsonDecoder decoder = new Jackson2JsonDecoder(objectMapper); + decoder.setMaxInMemorySize(-1); + clientCodecConfigurer.customCodecs().decoder(decoder); clientCodecConfigurer.customCodecs().encoder(new Jackson2JsonEncoder(objectMapper)); + //Unlimited + clientCodecConfigurer.defaultCodecs().maxInMemorySize(-1); }).build(); } } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java index 5ce36710..1fea323d 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/DataController.java @@ -62,6 +62,7 @@ import javafx.scene.control.CheckBox; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; +import javafx.scene.control.ScrollPane; import javafx.scene.control.SelectionMode; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellDataFeatures; @@ -77,6 +78,9 @@ public class DataController implements MapListeningController, RefreshableContro @FXML private MenuItem importWaveforms; + @FXML + private ScrollPane scrollPane; + @FXML private TableView tableView; diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/MeasuredMwsController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/MeasuredMwsController.java index 759e2592..5acc1f6c 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/MeasuredMwsController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/MeasuredMwsController.java @@ -18,6 +18,8 @@ import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.io.File; +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; import java.nio.file.Paths; import java.text.NumberFormat; import java.time.Duration; @@ -41,11 +43,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.SwingUtilities; +import org.apache.batik.svggen.SVGGraphics2DIOException; import org.apache.commons.io.FilenameUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -77,8 +81,10 @@ import gov.llnl.gnem.apps.coda.common.gui.util.NumberFormatFactory; import gov.llnl.gnem.apps.coda.common.gui.util.ProgressListener; import gov.llnl.gnem.apps.coda.common.gui.util.ProgressMonitor; +import gov.llnl.gnem.apps.coda.common.gui.util.SnapshotUtils; import gov.llnl.gnem.apps.coda.common.mapping.api.GeoMap; import gov.llnl.gnem.apps.coda.common.model.domain.Event; +import gov.llnl.gnem.apps.coda.common.model.domain.Pair; import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; import gov.llnl.gnem.apps.coda.common.model.messaging.WaveformChangeEvent; import javafx.application.Platform; @@ -95,6 +101,7 @@ import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; +import javafx.scene.control.Tab; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellDataFeatures; import javafx.scene.control.TableView; @@ -110,12 +117,15 @@ //TODO: This is effectively a subset of SiteController. Revisit and see if we can factor out common functions into a third shared class at some point. @Component -public class MeasuredMwsController implements MapListeningController, RefreshableController { +public class MeasuredMwsController implements MapListeningController, RefreshableController, ScreenshotEnabledController { private static final Logger log = LoggerFactory.getLogger(MeasuredMwsController.class); private static final String X_AXIS_LABEL = "center freq"; + @FXML + private Tab plotsTab; + @FXML private StackPane measuredMws; @@ -138,9 +148,36 @@ public class MeasuredMwsController implements MapListeningController, Refreshabl @FXML private TableColumn measuredMwCol; + // @FXML + // private TableColumn measuredMwSdCol; + @FXML private TableColumn measuredStressCol; + @FXML + private TableColumn measuredCornerFreqCol; + + @FXML + private TableColumn mistfitCol; + + // @FXML + // private TableColumn measuredCornerFreqSdCol; + + @FXML + private TableColumn measuredMwUq1LowCol; + + @FXML + private TableColumn measuredMwUq1HighCol; + + @FXML + private TableColumn measuredMwUq2LowCol; + + @FXML + private TableColumn measuredMwUq2HighCol; + + @FXML + private TableColumn iterationsCol; + @FXML private TableColumn dataCountCol; @@ -166,7 +203,7 @@ public class MeasuredMwsController implements MapListeningController, Refreshabl private MapPlottingUtilities iconFactory; private List spectralMeasurements = new ArrayList<>(); - private Map fitSpectra = new HashMap<>(); + private Map> fitSpectra = new HashMap<>(); private ObservableList evids = FXCollections.observableArrayList(); private ObservableList mwParameters = FXCollections.observableArrayList(); private ObservableList stationSymbols = FXCollections.observableArrayList(); @@ -194,12 +231,21 @@ public class MeasuredMwsController implements MapListeningController, Refreshabl private final AtomicReference minFreq = new AtomicReference<>(1.0); private final AtomicReference maxFreq = new AtomicReference<>(-0.0); + private final AtomicReference minY = new AtomicReference<>(1.0); + private final AtomicReference maxY = new AtomicReference<>(-0.0); + @FXML private Button xAxisShrink; private boolean shouldXAxisShrink = false; private Label xAxisShrinkOn; private Label xAxisShrinkOff; + @FXML + private Button yAxisShrink; + private boolean shouldYAxisShrink = false; + private Label yAxisShrinkOn; + private Label yAxisShrinkOff; + private ThreadPoolExecutor exec = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> { Thread thread = new Thread(r); thread.setName("MeasureMwController"); @@ -270,6 +316,27 @@ public double getProgress() { refreshView(); }); + yAxisShrinkOn = new Label("><"); + yAxisShrinkOn.setRotate(90.0); + yAxisShrinkOn.setStyle("-fx-font-weight:bold; -fx-font-size: 12px;"); + yAxisShrinkOn.setPadding(Insets.EMPTY); + yAxisShrinkOff = new Label("<>"); + yAxisShrinkOff.setRotate(90.0); + yAxisShrinkOff.setStyle("-fx-font-weight:bold; -fx-font-size: 12px;"); + yAxisShrinkOff.setPadding(Insets.EMPTY); + yAxisShrink.setGraphic(yAxisShrinkOn); + yAxisShrink.setPadding(new Insets(yAxisShrink.getPadding().getTop(), 0, yAxisShrink.getPadding().getBottom(), 0)); + yAxisShrink.prefHeightProperty().bind(evidCombo.heightProperty()); + yAxisShrink.setOnAction(e -> { + shouldYAxisShrink = !shouldYAxisShrink; + if (shouldYAxisShrink) { + yAxisShrink.setGraphic(yAxisShrinkOff); + } else { + yAxisShrink.setGraphic(yAxisShrinkOn); + } + refreshView(); + }); + SwingUtilities.invokeLater(() -> { fitPlot = new SpectralPlot(); fitPlot.addPlotObjectObserver(new Observer() { @@ -280,7 +347,7 @@ public void update(Observable observable, Object obj) { }); fitPlotSwingNode.setContent(fitPlot); - fitPlot.setLabels("Mw Spectra", X_AXIS_LABEL, "log10(amplitude)"); + fitPlot.setLabels("Moment Rate Spectra", X_AXIS_LABEL, "log10(dyne-cm)"); fitPlot.setYaxisVisibility(true); }); @@ -294,7 +361,18 @@ public void update(Observable observable, Object obj) { evidCol.comparatorProperty().set(new MaybeNumericStringComparator()); CellBindingUtils.attachTextCellFactories(measuredMwCol, MeasuredMwDetails::getMw, dfmt4); + // CellBindingUtils.attachTextCellFactories(measuredMwSdCol, MeasuredMwDetails::getMwSd, dfmt4); CellBindingUtils.attachTextCellFactories(measuredStressCol, MeasuredMwDetails::getApparentStressInMpa, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredCornerFreqCol, MeasuredMwDetails::getCornerFreq, dfmt4); + CellBindingUtils.attachTextCellFactories(mistfitCol, MeasuredMwDetails::getMisfit, dfmt4); + // CellBindingUtils.attachTextCellFactories(measuredCornerFreqSdCol, MeasuredMwDetails::getCornerFreqSd, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq1LowCol, MeasuredMwDetails::getMw1Min, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq1HighCol, MeasuredMwDetails::getMw1Max, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq2LowCol, MeasuredMwDetails::getMw2Min, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq2HighCol, MeasuredMwDetails::getMw2Max, dfmt4); + + iterationsCol.setCellValueFactory( + x -> Bindings.createIntegerBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(MeasuredMwDetails::getIterations).orElseGet(() -> 0)).asObject()); dataCountCol.setCellValueFactory( x -> Bindings.createIntegerBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(MeasuredMwDetails::getDataCount).orElseGet(() -> 0)).asObject()); @@ -354,8 +432,9 @@ private void plotSpectra() { if (evidCombo != null && evidCombo.getSelectionModel().getSelectedIndex() > 0) { filteredMeasurements = filterToEvent(evidCombo.getSelectionModel().getSelectedItem(), spectralMeasurements); Spectra referenceSpectra = spectraClient.getReferenceSpectra(evidCombo.getSelectionModel().getSelectedItem()).block(Duration.ofSeconds(2)); - Spectra theoreticalSpectra = fitSpectra.get(evidCombo.getSelectionModel().getSelectedItem()); - fitPlot.plotXYdata(toPlotPoints(filteredMeasurements, SpectraMeasurement::getPathAndSiteCorrected), Boolean.TRUE, referenceSpectra, theoreticalSpectra); + List fittingSpectra = new ArrayList<>(fitSpectra.get(evidCombo.getSelectionModel().getSelectedItem())); + fittingSpectra.add(referenceSpectra); + fitPlot.plotXYdata(toPlotPoints(filteredMeasurements, SpectraMeasurement::getPathAndSiteCorrected), Boolean.TRUE, fittingSpectra); if (filteredMeasurements != null && filteredMeasurements.size() > 0 && filteredMeasurements.get(0).getWaveform() != null) { Event event = filteredMeasurements.get(0).getWaveform().getEvent(); eventTime.setText("Date: " + DateTimeFormatter.ISO_INSTANT.format(event.getOriginTime().toInstant())); @@ -371,6 +450,23 @@ private void plotSpectra() { } fitSymbolMap.putAll(mapSpectraToPoint(filteredMeasurements, SpectraMeasurement::getPathAndSiteCorrected)); mapMeasurements(filteredMeasurements); + + minY.set(100.0); + maxY.set(0.0); + DoubleSummaryStatistics stats = filteredMeasurements.stream() + .filter(Objects::nonNull) + .map(spec -> spec.getPathAndSiteCorrected()) + .filter(v -> v != 0.0) + .collect(Collectors.summarizingDouble(Double::doubleValue)); + maxY.set(stats.getMax() + .1); + minY.set(stats.getMin() - .1); + + fitPlot.setAutoCalculateYaxisRange(shouldYAxisShrink); + if (shouldYAxisShrink) { + fitPlot.setAllYlimits(minY.get(), maxY.get()); + } else { + fitPlot.setAllYlimits(); + } } private void mapMeasurements(List measurements) { @@ -454,6 +550,14 @@ private double centerFreq(Double lowFrequency, Double highFrequency) { private void reloadData() { try { + progressGui.show(); + progressGui.toFront(); + + Platform.runLater(() -> { + evids.clear(); + evids.add("All"); + }); + exec.submit(() -> { maxFreq.set(-0.0); minFreq.set(1.0); @@ -470,8 +574,8 @@ private void reloadData() { MeasuredMwReportByEvent mfs = calibrationClient.makeMwMeasurements(Boolean.TRUE) .doOnError(err -> log.trace(err.getMessage(), err)) - .doAfterTerminate(() -> Platform.runLater(() -> progressGui.hide())) - .block(Duration.of(10l, ChronoUnit.SECONDS)); + .doFinally((s) -> Platform.runLater(() -> progressGui.hide())) + .block(Duration.of(1000l, ChronoUnit.SECONDS)); fitSpectra.putAll(mfs.getFitSpectra()); spectralMeasurements.addAll( @@ -508,13 +612,8 @@ public String apply(SpectraMeasurement t) { clearSpectraPlots(); spectralMeasurements.clear(); fitSpectra.clear(); - evids.clear(); - evids.add("All"); - - progressGui.show(); - progressGui.toFront(); } catch (RejectedExecutionException e) { - /*nop*/ + progressGui.hide(); } } @@ -544,6 +643,21 @@ public Runnable getRefreshFunction() { return () -> reloadData(); } + @Override + public Consumer getScreenshotFunction() { + return (folder) -> { + String timestamp = SnapshotUtils.getTimestampWithLeadingSeparator(); + SnapshotUtils.writePng(folder, new Pair<>("Measured_Mws", plotsTab.getContent()), timestamp); + try { + if (evidCombo.getValue() != null) { + fitPlot.exportSVG(folder + File.separator + "Measured_Mws_" + evidCombo.getValue() + timestamp + ".svg"); + } + } catch (UnsupportedEncodingException | FileNotFoundException | SVGGraphics2DIOException e) { + log.error("Error attempting to write plots for path controller : {}", e.getLocalizedMessage(), e); + } + }; + } + private void selectDataByCriteria(Boolean selected, String key) { List points = plotPointMap.get(key); if (selected) { diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/PathController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/PathController.java index 946f2031..297ef9cb 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/PathController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/PathController.java @@ -19,6 +19,9 @@ import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; import java.text.NumberFormat; import java.time.Duration; import java.util.ArrayList; @@ -37,11 +40,15 @@ import java.util.TreeMap; import java.util.TreeSet; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.stream.Collectors; import javax.swing.SwingUtilities; +import org.apache.batik.svggen.SVGGraphics2DIOException; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -56,6 +63,7 @@ import gov.llnl.gnem.apps.coda.common.gui.util.CommonGuiUtils; import gov.llnl.gnem.apps.coda.common.gui.util.EventStaFreqStringComparator; import gov.llnl.gnem.apps.coda.common.gui.util.NumberFormatFactory; +import gov.llnl.gnem.apps.coda.common.gui.util.SnapshotUtils; import gov.llnl.gnem.apps.coda.common.mapping.api.GeoMap; import gov.llnl.gnem.apps.coda.common.model.domain.Event; import gov.llnl.gnem.apps.coda.common.model.domain.FrequencyBand; @@ -93,7 +101,9 @@ import llnl.gnem.core.util.Geometry.EModel; @Component -public class PathController implements MapListeningController, RefreshableController { +public class PathController implements MapListeningController, RefreshableController, ScreenshotEnabledController { + + private static final Logger log = LoggerFactory.getLogger(PathController.class); private JMultiAxisPlot stationPlot; private JMultiAxisPlot sdPlot; @@ -471,6 +481,37 @@ public Runnable getRefreshFunction() { return () -> reloadData(); } + @Override + public Consumer getScreenshotFunction() { + return (folder) -> { + String timestamp = SnapshotUtils.getTimestampWithLeadingSeparator(); + SnapshotUtils.writePng(folder, new Pair<>("Path", path), timestamp); + try { + if (frequencyBandComboBox.getButtonCell() != null && frequencyBandComboBox.getButtonCell().getText() != null) { + sdPlot.exportSVG(folder + File.separator + "Path_SD_" + frequencyBandComboBox.getButtonCell().getText() + timestamp + ".svg"); + if (station1ComboBox.getButtonCell() != null + && station1ComboBox.getButtonCell().getText() != null + && station2ComboBox.getButtonCell() != null + && station2ComboBox.getButtonCell().getText() != null) { + stationPlot.exportSVG( + folder + + File.separator + + "Path_" + + frequencyBandComboBox.getButtonCell().getText() + + "_" + + station1ComboBox.getButtonCell().getText() + + "_" + + station2ComboBox.getButtonCell().getText() + + timestamp + + ".svg"); + } + } + } catch (UnsupportedEncodingException | FileNotFoundException | SVGGraphics2DIOException e) { + log.error("Error attempting to write plots for path controller : {}", e.getLocalizedMessage(), e); + } + }; + } + private void plotPaths() { mapImpl.clearIcons(); if (measurementsFreqBandMap != null && !measurementsFreqBandMap.isEmpty()) { diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/RefreshableController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/RefreshableController.java index 2fa67341..c14abb45 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/RefreshableController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/RefreshableController.java @@ -15,7 +15,5 @@ package gov.llnl.gnem.apps.coda.calibration.gui.controllers; public interface RefreshableController { - - Runnable getRefreshFunction(); - + public Runnable getRefreshFunction(); } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/ScreenshotEnabledController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/ScreenshotEnabledController.java new file mode 100644 index 00000000..a6e4d432 --- /dev/null +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/ScreenshotEnabledController.java @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.calibration.gui.controllers; + +import java.io.File; +import java.util.function.Consumer; + +public interface ScreenshotEnabledController { + public Consumer getScreenshotFunction(); +} diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/SiteController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/SiteController.java index 326970b8..ce9caa09 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/SiteController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/SiteController.java @@ -17,6 +17,9 @@ import java.awt.Color; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.text.NumberFormat; import java.time.Duration; @@ -33,15 +36,16 @@ import java.util.Observable; import java.util.Observer; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import javax.swing.SwingUtilities; +import org.apache.batik.svggen.SVGGraphics2DIOException; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,8 +71,10 @@ import gov.llnl.gnem.apps.coda.common.gui.util.CellBindingUtils; import gov.llnl.gnem.apps.coda.common.gui.util.MaybeNumericStringComparator; import gov.llnl.gnem.apps.coda.common.gui.util.NumberFormatFactory; +import gov.llnl.gnem.apps.coda.common.gui.util.SnapshotUtils; import gov.llnl.gnem.apps.coda.common.mapping.api.GeoMap; import gov.llnl.gnem.apps.coda.common.model.domain.Event; +import gov.llnl.gnem.apps.coda.common.model.domain.Pair; import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; import gov.llnl.gnem.apps.coda.common.model.messaging.WaveformChangeEvent; import javafx.application.Platform; @@ -85,6 +91,7 @@ import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; +import javafx.scene.control.Tab; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellDataFeatures; import javafx.scene.control.TableView; @@ -112,7 +119,7 @@ import reactor.core.scheduler.Schedulers; @Component -public class SiteController implements MapListeningController, RefreshableController { +public class SiteController implements MapListeningController, RefreshableController, ScreenshotEnabledController { private static final Logger log = LoggerFactory.getLogger(SiteController.class); @@ -120,6 +127,9 @@ public class SiteController implements MapListeningController, RefreshableContro private static final int MAX_LEGEND_COLORS = 8; + @FXML + private Tab resultsTab; + @FXML private SwingNode mwPlotSwingNode; private JMultiAxisPlot mwPlot; @@ -170,9 +180,36 @@ public class SiteController implements MapListeningController, RefreshableContro @FXML private TableColumn measuredMwCol; + @FXML + private TableColumn mistfitCol; + + // @FXML + // private TableColumn measuredMwSdCol; + @FXML private TableColumn measuredStressCol; + @FXML + private TableColumn measuredCornerFreqCol; + + // @FXML + // private TableColumn measuredCornerFreqSdCol; + + @FXML + private TableColumn measuredMwUq1LowCol; + + @FXML + private TableColumn measuredMwUq1HighCol; + + @FXML + private TableColumn measuredMwUq2LowCol; + + @FXML + private TableColumn measuredMwUq2HighCol; + + @FXML + private TableColumn iterationsCol; + @FXML private TableColumn dataCountCol; @@ -229,12 +266,21 @@ public class SiteController implements MapListeningController, RefreshableContro private final AtomicReference minFreq = new AtomicReference<>(1.0); private final AtomicReference maxFreq = new AtomicReference<>(-0.0); + private final AtomicReference minY = new AtomicReference<>(1.0); + private final AtomicReference maxY = new AtomicReference<>(-0.0); + @FXML private Button xAxisShrink; private boolean shouldXAxisShrink = false; private Label xAxisShrinkOn; private Label xAxisShrinkOff; + @FXML + private Button yAxisShrink; + private boolean shouldYAxisShrink = false; + private Label yAxisShrinkOn; + private Label yAxisShrinkOff; + private boolean isVisible = false; @Autowired @@ -285,6 +331,28 @@ public void initialize() { refreshView(); }); + yAxisShrinkOn = new Label("><"); + yAxisShrinkOn.setRotate(90.0); + yAxisShrinkOn.setStyle("-fx-font-weight:bold; -fx-font-size: 12px;"); + yAxisShrinkOn.setPadding(Insets.EMPTY); + yAxisShrinkOff = new Label("<>"); + yAxisShrinkOff.setRotate(90.0); + yAxisShrinkOff.setStyle("-fx-font-weight:bold; -fx-font-size: 12px;"); + yAxisShrinkOff.setPadding(Insets.EMPTY); + yAxisShrink.setGraphic(yAxisShrinkOn); + yAxisShrink.setPadding(new Insets(yAxisShrink.getPadding().getTop(), 0, yAxisShrink.getPadding().getBottom(), 0)); + yAxisShrink.prefHeightProperty().bind(evidCombo.heightProperty()); + + yAxisShrink.setOnAction(e -> { + shouldYAxisShrink = !shouldYAxisShrink; + if (shouldYAxisShrink) { + yAxisShrink.setGraphic(yAxisShrinkOff); + } else { + yAxisShrink.setGraphic(yAxisShrinkOn); + } + refreshView(); + }); + SwingUtilities.invokeLater(() -> { rawPlot = new SpectralPlot(); @@ -315,19 +383,19 @@ public void update(Observable observable, Object obj) { }); sitePlotSwingNode.setContent(sitePlot); - rawPlot.setLabels("Raw Plot", X_AXIS_LABEL, "log10(?)"); + rawPlot.setLabels("Raw Plot", X_AXIS_LABEL, "log10(non-dim)"); rawPlot.setYaxisVisibility(true); rawPlot.setAllXlimits(0.0, 0.0); rawPlot.setDefaultYMin(-2.0); rawPlot.setDefaultYMax(7.0); - pathPlot.setLabels("Path Corrected", X_AXIS_LABEL, "log10(?)"); + pathPlot.setLabels("Path Corrected", X_AXIS_LABEL, "log10(non-dim)"); pathPlot.setYaxisVisibility(true); pathPlot.setAllXlimits(0.0, 0.0); pathPlot.setDefaultYMin(-2.0); pathPlot.setDefaultYMax(7.0); - sitePlot.setLabels("Site Corrected", X_AXIS_LABEL, "log10(amplitude)"); + sitePlot.setLabels("Moment Rate Spectra", X_AXIS_LABEL, "log10(dyne-cm)"); sitePlot.setYaxisVisibility(true); mwPlot = new JMultiAxisPlot(); @@ -338,7 +406,7 @@ public void update(Observable observable, Object obj) { mwPlot.setYaxisVisibility(true); mwPlotSwingNode.setContent(mwPlot); - mwPlotFigure.getYaxis().setLabelOffset(2d * mwPlot.getXaxis().getLabelOffset()); + mwPlotFigure.getYaxis().setLabelOffset(2.5d * mwPlot.getXaxis().getLabelOffset()); mwPlotFigure.setAxisLimits(0.0, 10.0, 0.0, 10.0); mwPlotFigure.getYaxis().setLabelText("Reference"); @@ -350,7 +418,7 @@ public void update(Observable observable, Object obj) { stressPlot.setYaxisVisibility(true); stressPlotSwingNode.setContent(stressPlot); - stressPlotFigure.getYaxis().setLabelOffset(2d * stressPlot.getXaxis().getLabelOffset()); + stressPlotFigure.getYaxis().setLabelOffset(2.5d * stressPlot.getXaxis().getLabelOffset()); stressPlotFigure.setAxisLimits(0.0, 10.0, 0.0, 10.0); stressPlotFigure.getYaxis().setLabelText("Reference"); @@ -362,7 +430,7 @@ public void update(Observable observable, Object obj) { sdPlot.setYaxisVisibility(true); sdPlotSwingNode.setContent(sdPlot); - sdPlotFigure.getYaxis().setLabelOffset(2d * sdPlot.getXaxis().getLabelOffset()); + sdPlotFigure.getYaxis().setLabelOffset(2.5d * sdPlot.getXaxis().getLabelOffset()); sdPlotFigure.setAxisLimits(0.0, 10.0, 0.0, 2.0); sdPlotFigure.getYaxis().setLabelText("Standard Deviation"); @@ -390,7 +458,18 @@ public void update(Observable observable, Object obj) { CellBindingUtils.attachTextCellFactories(mwCol, MeasuredMwDetails::getRefMw, dfmt4); CellBindingUtils.attachTextCellFactories(stressCol, MeasuredMwDetails::getRefApparentStressInMpa, dfmt4); CellBindingUtils.attachTextCellFactories(measuredMwCol, MeasuredMwDetails::getMw, dfmt4); + CellBindingUtils.attachTextCellFactories(mistfitCol, MeasuredMwDetails::getMisfit, dfmt4); + // CellBindingUtils.attachTextCellFactories(measuredMwSdCol, MeasuredMwDetails::getMwSd, dfmt4); CellBindingUtils.attachTextCellFactories(measuredStressCol, MeasuredMwDetails::getApparentStressInMpa, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredCornerFreqCol, MeasuredMwDetails::getCornerFreq, dfmt4); + // CellBindingUtils.attachTextCellFactories(measuredCornerFreqSdCol, MeasuredMwDetails::getCornerFreqSd, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq1LowCol, MeasuredMwDetails::getMw1Min, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq1HighCol, MeasuredMwDetails::getMw1Max, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq2LowCol, MeasuredMwDetails::getMw2Min, dfmt4); + CellBindingUtils.attachTextCellFactories(measuredMwUq2HighCol, MeasuredMwDetails::getMw2Max, dfmt4); + + iterationsCol.setCellValueFactory( + x -> Bindings.createIntegerBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(MeasuredMwDetails::getIterations).orElseGet(() -> 0)).asObject()); dataCountCol.setCellValueFactory( x -> Bindings.createIntegerBinding(() -> Optional.ofNullable(x).map(CellDataFeatures::getValue).map(MeasuredMwDetails::getDataCount).orElseGet(() -> 0)).asObject()); @@ -456,10 +535,11 @@ private void plotSpectra() { if (evidCombo != null && evidCombo.getSelectionModel().getSelectedIndex() > 0) { filteredMeasurements = filterToEvent(evidCombo.getSelectionModel().getSelectedItem(), spectralMeasurements); Spectra referenceSpectra = spectraClient.getReferenceSpectra(evidCombo.getSelectionModel().getSelectedItem()).block(Duration.ofSeconds(2)); - Spectra theoreticalSpectra = spectraClient.getFitSpectra(evidCombo.getSelectionModel().getSelectedItem()).block(Duration.ofSeconds(2)); + List fittingSpectra = new ArrayList<>(spectraClient.getFitSpectra(evidCombo.getSelectionModel().getSelectedItem()).block(Duration.ofSeconds(2))); + fittingSpectra.add(referenceSpectra); rawPlot.plotXYdata(toPlotPoints(filteredMeasurements, SpectraMeasurement::getRawAtMeasurementTime), Boolean.TRUE); pathPlot.plotXYdata(toPlotPoints(filteredMeasurements, SpectraMeasurement::getPathCorrected), Boolean.TRUE); - sitePlot.plotXYdata(toPlotPoints(filteredMeasurements, SpectraMeasurement::getPathAndSiteCorrected), Boolean.TRUE, referenceSpectra, theoreticalSpectra); + sitePlot.plotXYdata(toPlotPoints(filteredMeasurements, SpectraMeasurement::getPathAndSiteCorrected), Boolean.TRUE, fittingSpectra); if (filteredMeasurements != null && filteredMeasurements.size() > 0 && filteredMeasurements.get(0).getWaveform() != null) { Event event = filteredMeasurements.get(0).getWaveform().getEvent(); eventTime.setText("Date: " + DateTimeFormatter.ISO_INSTANT.format(event.getOriginTime().toInstant())); @@ -479,6 +559,23 @@ private void plotSpectra() { pathSymbolMap.putAll(mapSpectraToPoint(filteredMeasurements, SpectraMeasurement::getPathCorrected)); siteSymbolMap.putAll(mapSpectraToPoint(filteredMeasurements, SpectraMeasurement::getPathAndSiteCorrected)); mapMeasurements(filteredMeasurements); + + minY.set(100.0); + maxY.set(0.0); + DoubleSummaryStatistics stats = filteredMeasurements.stream() + .filter(Objects::nonNull) + .map(spec -> spec.getPathAndSiteCorrected()) + .filter(v -> v != 0.0) + .collect(Collectors.summarizingDouble(Double::doubleValue)); + maxY.set(stats.getMax() + .1); + minY.set(stats.getMin() - .1); + + sitePlot.setAutoCalculateYaxisRange(shouldYAxisShrink); + if (shouldYAxisShrink) { + sitePlot.setAllYlimits(minY.get(), maxY.get()); + } else { + sitePlot.setAllYlimits(); + } } private void mapMeasurements(List measurements) { @@ -798,6 +895,27 @@ public Runnable getRefreshFunction() { return () -> reloadData(); } + @Override + public Consumer getScreenshotFunction() { + return (folder) -> { + String timestamp = SnapshotUtils.getTimestampWithLeadingSeparator(); + try { + if (resultsTab.isSelected() && evidCombo.getValue() != null) { + SnapshotUtils.writePng(folder, new Pair<>("Site", resultsTab.getContent()), timestamp); + rawPlot.exportSVG(folder + File.separator + "Site_Raw_" + evidCombo.getValue() + timestamp + ".svg"); + pathPlot.exportSVG(folder + File.separator + "Site_Path_" + evidCombo.getValue() + timestamp + ".svg"); + sitePlot.exportSVG(folder + File.separator + "Site_Full_" + evidCombo.getValue() + timestamp + ".svg"); + } else { + mwPlot.exportSVG(folder + File.separator + "Site_Mw" + timestamp + ".svg"); + stressPlot.exportSVG(folder + File.separator + "Site_Stress" + timestamp + ".svg"); + sdPlot.exportSVG(folder + File.separator + "Site_Station_Event_SD" + timestamp + ".svg"); + } + } catch (UnsupportedEncodingException | FileNotFoundException | SVGGraphics2DIOException e) { + log.error("Error attempting to write plots for path controller : {}", e.getLocalizedMessage(), e); + } + }; + } + private void selectDataByCriteria(Boolean selected, String key) { List points = plotPointMap.get(key); if (selected) { @@ -968,7 +1086,7 @@ private void removeRefEvents() { private void removeRefEvents(List evs) { if (evs != null && !evs.isEmpty()) { referenceEventClient.removeReferenceEventsByEventId(evs.stream().map(mwd -> mwd.getEventId()).distinct().collect(Collectors.toList())) - .doOnSuccess((v) -> CompletableFuture.runAsync(() -> reloadData())) + .doOnSuccess((v) -> Platform.runLater(() -> reloadData())) .subscribe(); } } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ParametersController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ParametersController.java index f533bed0..07fcc047 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ParametersController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ParametersController.java @@ -88,5 +88,5 @@ private void reloadData() { @Override public Runnable getRefreshFunction() { return () -> reloadData(); - } + } } \ No newline at end of file diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ShapeConfigurationController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ShapeConfigurationController.java index c5eb6dd6..585ef432 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ShapeConfigurationController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/ShapeConfigurationController.java @@ -138,6 +138,8 @@ public class ShapeConfigurationController { TableColumn maxGamma; @FXML TableColumn iterations; + @FXML + TableColumn fittingPointCount; private ObservableList data = FXCollections.observableArrayList(); @@ -206,6 +208,8 @@ public void initialize() { CellBindingUtils.attachEditableTextCellFactories(minGamma, ShapeFitterConstraints::getMinGamma, ShapeFitterConstraints::setMinGamma, dfmt2); CellBindingUtils.attachEditableTextCellFactories(maxGamma, ShapeFitterConstraints::getMaxGamma, ShapeFitterConstraints::setMaxGamma, dfmt2); CellBindingUtils.attachEditableIntegerCellFactories(iterations, ShapeFitterConstraints::getIterations, ShapeFitterConstraints::setIterations); + CellBindingUtils.attachEditableIntegerCellFactories(fittingPointCount, ShapeFitterConstraints::getFittingPointCount, ShapeFitterConstraints::setFittingPointCount); + shapeVelConfTableView.setItems(data); shapeBetaConfTableView.setItems(data); shapeGammaConfTableView.setItems(data); diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/SharedBandController.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/SharedBandController.java index 480de50d..7da849fa 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/SharedBandController.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/controllers/parameters/SharedBandController.java @@ -149,8 +149,8 @@ private void updateNotification(BandParametersDataChangeEvent event) { @FXML public void initialize() { - CellBindingUtils.attachEditableTextCellFactories(lowFreqCol, SharedFrequencyBandParameters::getLowFrequency, SharedFrequencyBandParameters::setLowFrequency); - CellBindingUtils.attachEditableTextCellFactories(highFreqCol, SharedFrequencyBandParameters::getHighFrequency, SharedFrequencyBandParameters::setHighFrequency); + CellBindingUtils.attachEditableTextCellFactories(lowFreqCol, SharedFrequencyBandParameters::getLowFrequency, SharedFrequencyBandParameters::setLowFrequency, dfmt4); + CellBindingUtils.attachEditableTextCellFactories(highFreqCol, SharedFrequencyBandParameters::getHighFrequency, SharedFrequencyBandParameters::setHighFrequency, dfmt4); CellBindingUtils.attachTextCellFactories(v0Col, SharedFrequencyBandParameters::getVelocity0, dfmt4); CellBindingUtils.attachTextCellFactories(v1Col, SharedFrequencyBandParameters::getVelocity1, dfmt4); diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/converters/sac/CodaStackedSacFileLoader.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/converters/sac/CodaStackedSacFileLoader.java index 638d6712..0126b889 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/converters/sac/CodaStackedSacFileLoader.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/converters/sac/CodaStackedSacFileLoader.java @@ -27,6 +27,7 @@ import gov.llnl.gnem.apps.coda.common.gui.converters.api.FileToEnvelopeConverter; import gov.llnl.gnem.apps.coda.common.gui.converters.api.StackInfo; import gov.llnl.gnem.apps.coda.common.gui.converters.sac.SacLoader; +import gov.llnl.gnem.apps.coda.common.model.domain.Stream; import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; import gov.llnl.gnem.apps.coda.common.model.messaging.Result; import llnl.gnem.core.io.SAC.SACHeader; @@ -56,7 +57,7 @@ public Mono> convertFile(File file) { Waveform waveform = result.getResultPayload().get(); Result res = filenameParser.parse(file.getName().toUpperCase(Locale.ENGLISH)); if (res.isSuccess()) { - waveform.getStream().setChannelName("STACK"); + waveform.getStream().setChannelName(Stream.TYPE_STACK); waveform.setSegmentType(res.getResultPayload().get().getDataType()); waveform.setSegmentUnits(DEFAULT_VEL_UNITS); waveform.setLowFrequency(res.getResultPayload().get().getLowFrequency()); diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/CalibrationWebClient.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/CalibrationWebClient.java index 3b748db9..d2c2c2a3 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/CalibrationWebClient.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/CalibrationWebClient.java @@ -25,6 +25,7 @@ import gov.llnl.gnem.apps.coda.calibration.gui.data.client.api.CalibrationClient; import gov.llnl.gnem.apps.coda.calibration.model.domain.MeasuredMwReportByEvent; +import gov.llnl.gnem.apps.coda.calibration.model.domain.MeasurementJob; import reactor.core.publisher.Mono; @Component @@ -46,16 +47,18 @@ public Mono runCalibration(Boolean autoPickingEnabled) { @Override public Mono makeMwMeasurements(Boolean autoPickingEnabled) { - return client.get().uri("/measurement/measure-mws/" + autoPickingEnabled).accept(MediaType.APPLICATION_JSON).exchange().flatMap(resp -> resp.bodyToMono(MeasuredMwReportByEvent.class)); + return makeMwMeasurements(autoPickingEnabled, null); } @Override public Mono makeMwMeasurements(Boolean autoPickingEnabled, List eventIds) { return client.post() - .uri("/measurement/measure-mws/" + autoPickingEnabled) - .bodyValue(eventIds) + .uri("/measurement/measure-mws") + .bodyValue(new MeasurementJob().setAutopickingEnabled(autoPickingEnabled).setPersistResults(Boolean.TRUE).setEventIds(eventIds)) .accept(MediaType.APPLICATION_JSON) .exchange() + .doOnError(e -> log.trace(e.getMessage(), e)) + .doOnSuccess(cr -> log.trace(cr.toString())) .flatMap(resp -> resp.bodyToMono(MeasuredMwReportByEvent.class)); } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/SpectraWebClient.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/SpectraWebClient.java index 666eae16..a1129624 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/SpectraWebClient.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/SpectraWebClient.java @@ -14,7 +14,11 @@ */ package gov.llnl.gnem.apps.coda.calibration.gui.data.client; +import java.util.ArrayList; +import java.util.List; + import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; @@ -45,7 +49,6 @@ public Flux getMeasuredSpectra() { .onErrorReturn(new SpectraMeasurement()); } - @Override public Flux getMeasuredSpectraMetadata() { return client.get() @@ -55,7 +58,7 @@ public Flux getMeasuredSpectraMetadata() { .flatMapMany(response -> response.bodyToFlux(SpectraMeasurement.class)) .onErrorReturn(new SpectraMeasurement()); } - + @Override public Mono getReferenceSpectra(String eventId) { return client.post() @@ -69,14 +72,15 @@ public Mono getReferenceSpectra(String eventId) { } @Override - public Mono getFitSpectra(String eventId) { + public Mono> getFitSpectra(String eventId) { return client.post() .uri("/spectra-measurements/fit-spectra") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .bodyValue(eventId) .exchange() - .flatMap(response -> response.bodyToMono(Spectra.class)) - .onErrorReturn(new Spectra()); + .flatMap(response -> response.bodyToMono(new ParameterizedTypeReference>() { + })) + .onErrorReturn(new ArrayList()); } } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/api/SpectraClient.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/api/SpectraClient.java index f1a0999d..4df136a6 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/api/SpectraClient.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/data/client/api/SpectraClient.java @@ -14,6 +14,8 @@ */ package gov.llnl.gnem.apps.coda.calibration.gui.data.client.api; +import java.util.List; + import gov.llnl.gnem.apps.coda.calibration.model.domain.Spectra; import gov.llnl.gnem.apps.coda.calibration.model.domain.SpectraMeasurement; import reactor.core.publisher.Flux; @@ -22,10 +24,10 @@ public interface SpectraClient { public Flux getMeasuredSpectra(); - + public Flux getMeasuredSpectraMetadata(); public Mono getReferenceSpectra(String eventId); - public Mono getFitSpectra(String eventId); + public Mono> getFitSpectra(String eventId); } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java index d1396fe3..ab8bb26a 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlot.java @@ -62,6 +62,10 @@ public class CodaWaveformPlot extends SeriesPlot { + private static final long serialVersionUID = 1L; + + private static final int DEFAULT_LINE_WIDTH = 3; + private static final Logger log = LoggerFactory.getLogger(CodaWaveformPlot.class); private NumberFormat dfmt4 = NumberFormatFactory.fourDecimalOneLeadingZero(); @@ -78,6 +82,8 @@ public class CodaWaveformPlot extends SeriesPlot { private SyntheticCoda synthetic; + private String plotIdentifier; + private enum PLOT_ORDERING { BACKGROUND(0), NOISE_BOX(1), WAVEFORM(2), NOISE_LINE(3), SHAPE_FIT(4), MODEL_FIT(5), PICKS(6); @@ -100,8 +106,6 @@ public CodaWaveformPlot(WaveformClient waveformClient, ShapeMeasurementClient sh this.velocityClient = velocityClient; } - private static final long serialVersionUID = 1L; - public void setWaveform(Waveform waveform) { setWaveform(waveform, null); } @@ -127,18 +131,8 @@ public void setWaveform(Waveform waveform, SyntheticCoda synth) { double distance = EModel.getDistanceWGS84(event.getLatitude(), event.getLongitude(), station.getLatitude(), station.getLongitude()); double baz = EModel.getBAZ(station.getLatitude(), station.getLongitude(), event.getLatitude(), event.getLongitude()); - String labelText = waveform.getEvent().getEventId() - + "_" - + waveform.getStream().getStation().getStationName() - + "_" - + waveform.getLowFrequency() - + "_" - + waveform.getHighFrequency() - + "; Distance: " - + dfmt4.format(distance) - + "km BAz: " - + dfmt4.format(baz) - + "° "; + plotIdentifier = waveform.getEvent().getEventId() + "_" + waveform.getStream().getStation().getStationName() + "_" + waveform.getLowFrequency() + "_" + waveform.getHighFrequency(); + String labelText = plotIdentifier + "; Distance: " + dfmt4.format(distance) + "km BAz: " + dfmt4.format(baz) + "° "; PinnedText legend = createLegend(labelText); PlotObject legendRef = subplot.AddPlotObject(legend); @@ -179,7 +173,9 @@ public void setWaveform(Waveform waveform, SyntheticCoda synth) { if (shape != null && shape.getId() != null) { try { TimeSeries interpolatedSeries = new TimeSeries(waveformSegment, waveform.getSampleRate(), beginTime); - interpolatedSeries.interpolate(1.0); + if (interpolatedSeries.getSamprate() > 1.0) { + interpolatedSeries.interpolate(1.0); + } float[] interpolatedData = interpolatedSeries.getData(); float[] fitSegment = new float[interpolatedData.length]; @@ -188,8 +184,10 @@ public void setWaveform(Waveform waveform, SyntheticCoda synth) { double intercept = shape.getMeasuredIntercept(); int timeShift = (int) (new TimeT(shape.getMeasuredTime()).subtractD(beginTime) - 0.5); + double sampleRate = interpolatedSeries.getSamprate(); for (int i = 0; i < interpolatedData.length; i++) { - fitSegment[i] = (float) (intercept - gamma * Math.log10(i + 1) + beta * (i + 1)); + double t = (i / sampleRate) + 1.0; + fitSegment[i] = (float) (intercept - gamma * Math.log10(t) + beta * (t)); } TimeSeries fitSeries = new TimeSeries(fitSegment, interpolatedSeries.getSamprate(), interpolatedSeries.getTime()); @@ -226,6 +224,8 @@ public void setWaveform(Waveform waveform, SyntheticCoda synth) { repaint(); }); + } else { + plotIdentifier = ""; } } @@ -289,7 +289,7 @@ private void plotSynthetic(Waveform waveform, SyntheticCoda synth, final TimeT b if (endTime.lt(synthSeriesRemaining.getEndtime())) { synthSeriesRemaining.cutBefore(endTime); int remainingStartTimeShift = (int) (endTime.subtractD(beginTime) + 0.5); - subplot.AddPlotObject(createLine(remainingStartTimeShift, median, synthSeriesRemaining, Color.GREEN, 3, PenStyle.DASH), PLOT_ORDERING.MODEL_FIT.getZOrder()); + subplot.AddPlotObject(createLine(remainingStartTimeShift, median, synthSeriesRemaining, Color.GREEN, DEFAULT_LINE_WIDTH, PenStyle.DASH), PLOT_ORDERING.MODEL_FIT.getZOrder()); } repaint(); } @@ -301,11 +301,11 @@ private PinnedText createLegend(String text) { } private PlotObject createLine(int timeShift, double valueShift, TimeSeries timeSeries, Color lineColor) { - return createLine(timeShift, valueShift, timeSeries, lineColor, 3, PenStyle.SOLID); + return createLine(timeShift, valueShift, timeSeries, lineColor, DEFAULT_LINE_WIDTH, PenStyle.SOLID); } private PlotObject createLine(int timeShift, double valueShift, TimeSeries timeSeries, Color lineColor, int width, PenStyle style) { - Line line = new Line(timeShift, timeSeries.getDelta(), SeriesMath.add(timeSeries.getData(), valueShift), 1); + Line line = new Line(timeShift, timeSeries.getDelta(), SeriesMath.add(timeSeries.getData(), valueShift), DEFAULT_LINE_WIDTH); line.setPenStyle(style); line.setColor(lineColor); line.setWidth(width); @@ -315,7 +315,7 @@ private PlotObject createLine(int timeShift, double valueShift, TimeSeries timeS //Only used for Waveforms @Override public AbstractLine addLine(TimeSeries seismogram, Color lineColor) { - Line line = new Line(0.0, seismogram.getDelta(), seismogram.getData(), 1); + Line line = new Line(0.0, seismogram.getDelta(), seismogram.getData(), DEFAULT_LINE_WIDTH); line.setColor(lineColor); getSubplot(seismogram).AddPlotObject(line, PLOT_ORDERING.WAVEFORM.getZOrder()); return line; @@ -327,7 +327,7 @@ private PlotObject createFixedLine(double value, int length, Color lineColor, Pe Line line = new Line(0, 1.0, data, 1); line.setColor(lineColor); line.setPenStyle(penStyle); - line.setWidth(3); + line.setWidth(1); return line; } @@ -385,4 +385,8 @@ public static float[] doublesToFloats(double[] x) { } return xfloats; } + + public String getPlotIdentifier() { + return plotIdentifier; + } } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlotManager.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlotManager.java index c2c15a3a..848b7c93 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlotManager.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/CodaWaveformPlotManager.java @@ -15,10 +15,14 @@ package gov.llnl.gnem.apps.coda.calibration.gui.plotting; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; @@ -39,6 +43,7 @@ import javax.swing.JToolBar; import javax.swing.SwingUtilities; +import org.apache.batik.svggen.SVGGraphics2DIOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,6 +51,7 @@ import gov.llnl.gnem.apps.coda.calibration.gui.data.client.api.PeakVelocityClient; import gov.llnl.gnem.apps.coda.calibration.gui.data.client.api.ShapeMeasurementClient; import gov.llnl.gnem.apps.coda.common.gui.data.client.api.WaveformClient; +import gov.llnl.gnem.apps.coda.common.gui.util.SnapshotUtils; import gov.llnl.gnem.apps.coda.common.mapping.api.GeoMap; import gov.llnl.gnem.apps.coda.common.mapping.api.Icon; import gov.llnl.gnem.apps.coda.common.model.domain.Pair; @@ -234,17 +240,25 @@ public void setOrderedWaveformIDs(List waveformIDs) { allWaveformIDs.add(waveformIDs.get(i)); } if (waveformIDs.size() > pageSize) { - this.add(toolbar, BorderLayout.NORTH); totalPages = (int) ((waveformIDs.size() - 1) / pageSize); } else { - this.remove(toolbar); totalPages = 1; } + adjustShowingToolbar(totalPages); + currentPage = 0; loadWaveformsForPage(currentPage); } + private void adjustShowingToolbar(int totalPages) { + if (totalPages > 1) { + this.add(toolbar, BorderLayout.NORTH); + } else { + this.remove(toolbar); + } + } + private void loadWaveformsForPage(int pageNumber) { clear(); List> results = new ArrayList<>(); @@ -310,4 +324,23 @@ public void triggerKeyEvent(javafx.scene.input.KeyEvent event) { SwingUtilities.invokeLater(() -> forwardAction.actionPerformed(new ActionEvent(event.getSource(), KeyEvent.KEY_RELEASED, "ForwardAction"))); } } + + public void exportScreenshots(File folder) { + String timestamp = SnapshotUtils.getTimestampWithLeadingSeparator(); + for (CodaWaveformPlot wp : orderedWaveformPlots.values()) { + Color originalBackground = wp.getBackground(); + wp.setBackground(Color.WHITE); + try { + String plotId = wp.getPlotIdentifier(); + if (plotId != null && !plotId.isEmpty()) { + wp.exportSVG(folder + File.separator + "Waveform_" + plotId + "_" + timestamp + ".svg"); + } else { + wp.exportSVG(folder + File.separator + "Waveform_" + SnapshotUtils.getTimestampWithLeadingSeparator() + ".svg"); + } + } catch (UnsupportedEncodingException | FileNotFoundException | SVGGraphics2DIOException e) { + log.error("Error attempting to write plots for waveform plot {} : {}", e.getLocalizedMessage(), e); + } + wp.setBackground(originalBackground); + } + } } diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/SpectralPlot.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/SpectralPlot.java index a94bcad9..a2e7cf50 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/SpectralPlot.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/SpectralPlot.java @@ -27,6 +27,7 @@ import gov.llnl.gnem.apps.coda.calibration.model.domain.Spectra; import gov.llnl.gnem.apps.coda.common.gui.plotting.LabeledPlotPoint; import gov.llnl.gnem.apps.coda.common.gui.plotting.PlotPoint; +import gov.llnl.gnem.apps.coda.common.model.util.SPECTRA_TYPES; import llnl.gnem.core.gui.plotting.HorizPinEdge; import llnl.gnem.core.gui.plotting.Legend; import llnl.gnem.core.gui.plotting.PaintMode; @@ -63,7 +64,7 @@ public class SpectralPlot extends JMultiAxisPlot { private double defaultXMin = 0.0; private double defaultXMax = 0.0; - private double defaultYMin = 19.0; + private double defaultYMin = 15.0; private double defaultYMax = 27.0; private final Map> symbolMap = new HashMap<>(); @@ -134,7 +135,7 @@ public void clearPlot() { * @param showLegend */ public void plotXYdata(final List plots, Boolean showLegend) { - plotXYdata(plots, showLegend, null, null); + plotXYdata(plots, showLegend, null); } /** @@ -144,18 +145,13 @@ public void plotXYdata(final List plots, Boolean showLegend) { * as { log10(centerFrequency), log10(amplitiude), Symbol } *

* @param showLegend - * @param referenceSpectra + * @param spectra *

- * A source spectra containing the amp/freq/mw information for a - * reference event used in the calibration - *

- * @param theoreticalSpectra - *

- * A source spectra containing the amp/freq/mw information for a - * theoretical best fit spectra + * Spectra containing the amp/freq/mw information for a + * calibration spectra *

*/ - public void plotXYdata(final List plots, Boolean showLegend, Spectra... spectra) { + public void plotXYdata(final List plots, Boolean showLegend, List spectra) { symbolMap.clear(); // line based legends for trending @@ -178,8 +174,11 @@ public void plotXYdata(final List plots, Boolean showLegend, Spectra. legend.addLabeledLine(AVG_MW_LEGEND_LABEL, line); } - for (int j = 0; j < spectra.length; j++) { - plotSpectraObject(jsubplot, spectra[j], legend); + if (spectra != null) { + spectra.sort((s1, s2) -> (s1.getType() == SPECTRA_TYPES.UQ1 || s1.getType() == SPECTRA_TYPES.UQ2) ? -1 : 1); + for (Spectra spec : spectra) { + plotSpectraObject(jsubplot, spec, legend); + } } plotXYdata(plots); @@ -204,22 +203,34 @@ private void plotSpectraObject(JSubplot jsubplot, Spectra spectra, Legend legend y[i] = point.getY().floatValue(); } - Line line; + Line line = null; switch (spectra.getType()) { case REF: line = new Line(x, y, Color.BLACK, PaintMode.COPY, PenStyle.DASH, 2); break; - default: + case UQ1: + line = new Line(x, y, Color.LIGHT_GRAY, PaintMode.COPY, PenStyle.DASH, 2); + break; + case UQ2: + line = new Line(x, y, Color.LIGHT_GRAY, PaintMode.COPY, PenStyle.DOT, 2); + break; + case FIT: line = new Line(x, y, Color.RED, PaintMode.COPY, PenStyle.DASH, 2); break; + default: + break; } - jsubplot.AddPlotObject(line); - DecimalFormat df = new DecimalFormat("#.0#"); - if (spectra.getApparentStress() > 0.0) { - DecimalFormat df2 = new DecimalFormat("#0.0#"); - legend.addLabeledLine(spectra.getType().name() + ' ' + df.format(spectra.getMw()) + " @ " + df2.format(spectra.getApparentStress()) + "MPa", line); - } else { - legend.addLabeledLine(spectra.getType().name() + ' ' + df.format(spectra.getMw()), line); + if (line != null) { + jsubplot.AddPlotObject(line); + if (SPECTRA_TYPES.UQ1 != spectra.getType() && SPECTRA_TYPES.UQ2 != spectra.getType()) { + DecimalFormat df = new DecimalFormat("#.0#"); + if (spectra.getApparentStress() > 0.0) { + DecimalFormat df2 = new DecimalFormat("#0.0#"); + legend.addLabeledLine(spectra.getType().name() + ' ' + df.format(spectra.getMw()) + " @ " + df2.format(spectra.getApparentStress()) + "MPa", line); + } else { + legend.addLabeledLine(spectra.getType().name() + ' ' + df.format(spectra.getMw()), line); + } + } } } } @@ -369,6 +380,18 @@ public void setAllXlimits() { properties.setMinXAxisValue(xmin); } + public void setAllYlimits(double ymin, double ymax) { + jsubplot.setYlimits(ymin, ymax); + this.ymin = ymin; + this.ymax = ymax; + } + + public void setAllYlimits() { + jsubplot.setYlimits(defaultYMin, defaultYMax); + this.ymin = defaultYMin; + this.ymax = defaultYMax; + } + public void refreshPlotAxes() { double xMin; double xMax; @@ -398,6 +421,7 @@ public void setLabels(String title, String xlabel, String ylabel) { this.getTitle().setText(title); this.getXaxis().setLabelText(xlabel); jsubplot.getYaxis().setLabelText(ylabel); + jsubplot.getYaxis().setLabelOffset(12d); } public JSubplot getSubplot() { diff --git a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/WaveformGui.java b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/WaveformGui.java index 7a1853db..3ac0b040 100644 --- a/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/WaveformGui.java +++ b/calibration-gui/src/main/java/gov/llnl/gnem/apps/coda/calibration/gui/plotting/WaveformGui.java @@ -14,10 +14,13 @@ */ package gov.llnl.gnem.apps.coda.calibration.gui.plotting; +import java.io.File; import java.io.IOException; import javax.swing.SwingUtilities; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -34,25 +37,36 @@ import javafx.beans.property.Property; import javafx.beans.property.SimpleBooleanProperty; import javafx.embed.swing.SwingNode; +import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.Label; import javafx.scene.input.KeyEvent; import javafx.scene.text.Font; +import javafx.stage.DirectoryChooser; import javafx.stage.Stage; import javafx.stage.StageStyle; @Controller public class WaveformGui { + private static final Logger log = LoggerFactory.getLogger(WaveformGui.class); + private Parent root; private Scene scene; private Stage stage; @FXML private SwingNode waveformPlotNode; + + @FXML + private Button snapshotButton; + private CodaWaveformPlotManager waveformPlotManager; private WaveformClient waveformClient; private ShapeMeasurementClient shapeClient; @@ -61,6 +75,7 @@ public class WaveformGui { private GeoMap map; private MapPlottingUtilities mapPlotUtilities; private Property shouldFocus = new SimpleBooleanProperty(false); + private DirectoryChooser screenshotFolderChooser = new DirectoryChooser(); @Autowired public WaveformGui(WaveformClient waveformClient, ShapeMeasurementClient shapeClient, ParameterClient paramsClient, PeakVelocityClient peakVelocityClient, GeoMap map, @@ -120,12 +135,32 @@ private void repaintWaveformWindow() { @FXML public void initialize() { + Label label = new Label("\uE3B0"); + label.getStyleClass().add("material-icons-medium"); + label.setMaxHeight(16); + label.setMinWidth(16); + snapshotButton.setGraphic(label); + snapshotButton.setContentDisplay(ContentDisplay.CENTER); + screenshotFolderChooser.setTitle("Screenshot Export Folder"); SwingUtilities.invokeLater(() -> { waveformPlotManager = new CodaWaveformPlotManager(waveformClient, shapeClient, paramsClient, peakVelocityClient, map, mapPlotUtilities); waveformPlotNode.setContent(waveformPlotManager); }); } + @FXML + private void screenshotPlots(ActionEvent e) { + File folder = screenshotFolderChooser.showDialog(scene.getWindow()); + try { + if (folder != null && folder.exists() && folder.isDirectory() && folder.canWrite()) { + screenshotFolderChooser.setInitialDirectory(folder); + Platform.runLater(() -> waveformPlotManager.exportScreenshots(folder)); + } + } catch (SecurityException ex) { + log.warn("Exception trying to write screenshots to folder {} : {}", folder, ex.getLocalizedMessage(), ex); + } + } + public void hide() { Platform.runLater(() -> { stage.hide(); diff --git a/calibration-gui/src/main/resources/application.properties b/calibration-gui/src/main/resources/application.properties index 436c4ce8..d35e6960 100644 --- a/calibration-gui/src/main/resources/application.properties +++ b/calibration-gui/src/main/resources/application.properties @@ -5,4 +5,5 @@ webclient.basePath=localhost:53921 webclient.subscriptions=/topic/status-events,/topic/calibration-events app.height=1200 app.width=800 -app.baseTitle=Coda Calibration \ No newline at end of file +app.baseTitle=Coda Calibration +spring.codec.max-in-memory-size=-1 \ No newline at end of file diff --git a/calibration-gui/src/main/resources/fxml/CodaGui.fxml b/calibration-gui/src/main/resources/fxml/CodaGui.fxml index b895c9fe..1ac520d6 100644 --- a/calibration-gui/src/main/resources/fxml/CodaGui.fxml +++ b/calibration-gui/src/main/resources/fxml/CodaGui.fxml @@ -15,6 +15,7 @@ + @@ -91,26 +92,32 @@ - + - + - + + + + @@ -67,9 +76,9 @@ - + - + @@ -96,8 +105,21 @@ + - + + + + + + + + + + + + + diff --git a/calibration-gui/src/main/resources/fxml/Shape.fxml b/calibration-gui/src/main/resources/fxml/Shape.fxml index 00c020e8..bd19e10f 100644 --- a/calibration-gui/src/main/resources/fxml/Shape.fxml +++ b/calibration-gui/src/main/resources/fxml/Shape.fxml @@ -17,89 +17,81 @@ - - - - + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - + + + diff --git a/calibration-gui/src/main/resources/fxml/ShapeConstraints.fxml b/calibration-gui/src/main/resources/fxml/ShapeConstraints.fxml index bd6b892e..0e0efad5 100644 --- a/calibration-gui/src/main/resources/fxml/ShapeConstraints.fxml +++ b/calibration-gui/src/main/resources/fxml/ShapeConstraints.fxml @@ -76,6 +76,7 @@ + diff --git a/calibration-gui/src/main/resources/fxml/Site.fxml b/calibration-gui/src/main/resources/fxml/Site.fxml index bdd679fa..f963a3af 100644 --- a/calibration-gui/src/main/resources/fxml/Site.fxml +++ b/calibration-gui/src/main/resources/fxml/Site.fxml @@ -26,7 +26,7 @@ - + @@ -50,6 +50,16 @@ + + + + @@ -67,9 +77,9 @@ - + - + @@ -77,9 +87,9 @@ - + - + @@ -150,10 +160,23 @@ - - + + + + + + + + + + + + + + + diff --git a/calibration-gui/src/main/resources/fxml/WaveformGui.fxml b/calibration-gui/src/main/resources/fxml/WaveformGui.fxml index c1dd9687..f0134ba9 100644 --- a/calibration-gui/src/main/resources/fxml/WaveformGui.fxml +++ b/calibration-gui/src/main/resources/fxml/WaveformGui.fxml @@ -1,13 +1,15 @@ + + + - - + @@ -17,7 +19,16 @@ - + + + + + + diff --git a/calibration-service/calibration-application/pom.xml b/calibration-service/calibration-application/pom.xml index e4ba253e..fc92c6e5 100644 --- a/calibration-service/calibration-application/pom.xml +++ b/calibration-service/calibration-application/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/MeasurementJsonController.java b/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/MeasurementJsonController.java index b41be351..f5754630 100644 --- a/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/MeasurementJsonController.java +++ b/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/MeasurementJsonController.java @@ -27,14 +27,13 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import gov.llnl.gnem.apps.coda.calibration.model.domain.MeasuredMwReportByEvent; +import gov.llnl.gnem.apps.coda.calibration.model.domain.MeasurementJob; import gov.llnl.gnem.apps.coda.calibration.service.api.CalibrationService; import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; import gov.llnl.gnem.apps.coda.common.model.messaging.Result; @@ -52,35 +51,28 @@ public MeasurementJsonController(CalibrationService service) { this.service = service; } - @GetMapping(value = "/measure-mws/{autoPickingEnabled}", name = "measureMws") - public ResponseEntity measureMws(@PathVariable(name = "autoPickingEnabled", required = false) Boolean autoPickingEnabled) { - return measureMw(autoPickingEnabled, null, null); + @PostMapping(value = "/measure-mws", name = "measureMws") + public ResponseEntity measureMws(@RequestBody MeasurementJob job) { + return measureMw(job.getAutopickingEnabled(), job.getPersistResults(), job.getEventIds(), job.getStacks()); } - @PostMapping(value = "/measure-mws/{autoPickingEnabled}", name = "measureMws") - public ResponseEntity measureMws(@PathVariable(name = "autoPickingEnabled", required = false) Boolean autoPickingEnabled, @RequestBody List eventIds) { - return measureMw(autoPickingEnabled, eventIds, null); - } - - @PostMapping(value = "/measure-mws-from-stacks/{autoPickingEnabled}", name = "measureMwsFromStacks") - public ResponseEntity measureMwsFromStacks(@PathVariable(name = "autoPickingEnabled", required = false) Boolean autoPickingEnabled, @RequestBody List stacks) { - return measureMw(autoPickingEnabled, null, stacks); - } - - private ResponseEntity measureMw(Boolean autoPickingEnabled, List evids, List stacks) { + private ResponseEntity measureMw(Boolean autoPickingEnabled, Boolean persistResults, List evids, List stacks) { if (autoPickingEnabled == null) { autoPickingEnabled = Boolean.FALSE; } + if (persistResults == null) { + persistResults = Boolean.FALSE; + } ResponseEntity resp = null; try { Result measuredMws; if (evids != null && !evids.isEmpty()) { - measuredMws = service.makeMwMeasurements(autoPickingEnabled, new HashSet<>(evids)).get(500, TimeUnit.SECONDS); + measuredMws = service.makeMwMeasurements(autoPickingEnabled, persistResults, new HashSet<>(evids)).get(4, TimeUnit.HOURS); } else if (stacks != null && !stacks.isEmpty()) { - measuredMws = service.makeMwMeasurements(autoPickingEnabled, stacks).get(500, TimeUnit.SECONDS); + measuredMws = service.makeMwMeasurements(autoPickingEnabled, persistResults, stacks).get(4, TimeUnit.HOURS); } else { - measuredMws = service.makeMwMeasurements(autoPickingEnabled).get(500, TimeUnit.SECONDS); + measuredMws = service.makeMwMeasurements(autoPickingEnabled, persistResults).get(4, TimeUnit.HOURS); } if (measuredMws != null) { if (measuredMws.isSuccess()) { diff --git a/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/SpectraMeasurementJsonController.java b/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/SpectraMeasurementJsonController.java index 1b3aa9b5..94a81a3e 100644 --- a/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/SpectraMeasurementJsonController.java +++ b/calibration-service/calibration-application/src/main/java/gov/llnl/gnem/apps/coda/calibration/application/web/SpectraMeasurementJsonController.java @@ -53,20 +53,20 @@ public List getMeasurements() { @GetMapping(name = "getMeasurementsMetadata", path = "/metadata/all") public List getMeasurementsMetadata() { - return service.findAllMetadataOnly().stream().map(md -> new SpectraMeasurement(md)).collect(Collectors.toList()); + return service.findAllMetadataOnly().stream().map(md -> new SpectraMeasurement(md)).collect(Collectors.toList()); } @PostMapping(value = "/reference-spectra", name = "computeSpectraForEventId") public ResponseEntity computeSpectraForEventId(@RequestBody String eventId, BindingResult result) { //FIXME: Accept a phase to use! - Spectra theoreticalSpectra = service.computeSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG); + Spectra theoreticalSpectra = service.computeReferenceSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG); return ResponseEntity.ok(theoreticalSpectra); } @PostMapping(value = "/fit-spectra", name = "getFitSpectraForEventId") public ResponseEntity getFitSpectraForEventId(@RequestBody String eventId, BindingResult result) { //FIXME: Accept a phase to use! - Spectra fitSpectra = service.getFitSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG); + List fitSpectra = service.getFitSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG); return ResponseEntity.ok(fitSpectra); } diff --git a/calibration-service/calibration-integration/pom.xml b/calibration-service/calibration-integration/pom.xml index 79d2f1f8..4a9a46f9 100644 --- a/calibration-service/calibration-integration/pom.xml +++ b/calibration-service/calibration-integration/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/calibration-service/calibration-model/pom.xml b/calibration-service/calibration-model/pom.xml index 4b252a86..6a5b1394 100644 --- a/calibration-service/calibration-model/pom.xml +++ b/calibration-service/calibration-model/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/InjectedCalibrationShapeFitterConstraints.java b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/InjectedCalibrationShapeFitterConstraints.java index 7b0fe361..6a945f4c 100644 --- a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/InjectedCalibrationShapeFitterConstraints.java +++ b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/InjectedCalibrationShapeFitterConstraints.java @@ -146,48 +146,52 @@ public class InjectedCalibrationShapeFitterConstraints { @Value("${shape-constraints.iterations:10}") private int iterations; + @Value("${shape-constraints.fitting-point-count:10000}") + private int fittingPointCount; + @Bean public ShapeFitterConstraints toCalibrationShapeFitterConstraints() { return new ShapeFitterConstraints().setMaxVP1(maxVP1) - .setMinVP1(minVP1) - .setV0reg(v0reg) - .setMaxVP2(maxVP2) - .setMinVP2(minVP2) - .setMaxVP3(maxVP3) - .setMinVP3(minVP3) - .setMaxBP1(maxBP1) - .setMinBP1(minBP1) - .setB0reg(b0reg) - .setMaxBP2(maxBP2) - .setMinBP2(minBP2) - .setMaxBP3(maxBP3) - .setMinBP3(minBP3) - .setMaxGP1(maxGP1) - .setMinGP1(minGP1) - .setG0reg(g0reg) - .setMaxGP2(maxGP2) - .setMinGP2(minGP2) - .setG1reg(g1reg) - .setMaxGP3(maxGP3) - .setMinGP3(minGP3) - .setYvvMin(yvvMin) - .setYvvMax(yvvMax) - .setvDistMax(vDistMax) - .setvDistMin(vDistMin) - .setYbbMin(ybbMin) - .setYbbMax(ybbMax) - .setbDistMax(bDistMax) - .setbDistMin(bDistMin) - .setYggMin(yggMin) - .setYggMax(yggMax) - .setgDistMin(gDistMin) - .setgDistMax(gDistMax) - .setMinIntercept(minIntercept) - .setMaxIntercept(maxIntercept) - .setMinBeta(minBeta) - .setMaxBeta(maxBeta) - .setMinGamma(minGamma) - .setMaxGamma(maxGamma) - .setIterations(iterations); + .setMinVP1(minVP1) + .setV0reg(v0reg) + .setMaxVP2(maxVP2) + .setMinVP2(minVP2) + .setMaxVP3(maxVP3) + .setMinVP3(minVP3) + .setMaxBP1(maxBP1) + .setMinBP1(minBP1) + .setB0reg(b0reg) + .setMaxBP2(maxBP2) + .setMinBP2(minBP2) + .setMaxBP3(maxBP3) + .setMinBP3(minBP3) + .setMaxGP1(maxGP1) + .setMinGP1(minGP1) + .setG0reg(g0reg) + .setMaxGP2(maxGP2) + .setMinGP2(minGP2) + .setG1reg(g1reg) + .setMaxGP3(maxGP3) + .setMinGP3(minGP3) + .setYvvMin(yvvMin) + .setYvvMax(yvvMax) + .setvDistMax(vDistMax) + .setvDistMin(vDistMin) + .setYbbMin(ybbMin) + .setYbbMax(ybbMax) + .setbDistMax(bDistMax) + .setbDistMin(bDistMin) + .setYggMin(yggMin) + .setYggMax(yggMax) + .setgDistMin(gDistMin) + .setgDistMax(gDistMax) + .setMinIntercept(minIntercept) + .setMaxIntercept(maxIntercept) + .setMinBeta(minBeta) + .setMaxBeta(maxBeta) + .setMinGamma(minGamma) + .setMaxGamma(maxGamma) + .setIterations(iterations) + .setFittingPointCount(fittingPointCount); } } \ No newline at end of file diff --git a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MdacParametersFI.java b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MdacParametersFI.java index 4c6665f5..94ee9bd0 100644 --- a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MdacParametersFI.java +++ b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MdacParametersFI.java @@ -158,8 +158,9 @@ public double getSigma() { return this.sigma; } - public void setSigma(double sigma) { + public MdacParametersFI setSigma(double sigma) { this.sigma = sigma; + return this; } public double getDelSigma() { @@ -174,8 +175,9 @@ public double getPsi() { return this.psi; } - public void setPsi(double psi) { + public MdacParametersFI setPsi(double psi) { this.psi = psi; + return this; } public double getDelPsi() { diff --git a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwDetails.java b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwDetails.java index 0a7023d0..dd9985c6 100644 --- a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwDetails.java +++ b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwDetails.java @@ -1,5 +1,5 @@ /* -* Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory * CODE-743439. * All rights reserved. * This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. @@ -15,6 +15,7 @@ package gov.llnl.gnem.apps.coda.calibration.model.domain; import java.time.format.DateTimeFormatter; +import java.util.Objects; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -26,12 +27,34 @@ public class MeasuredMwDetails { private Double mw; + private Double mwSd; + + private Double mw1Max; + + private Double mw1Min; + + private Double mw2Max; + + private Double mw2Min; + private Double refMw; private Double apparentStressInMpa; + private Double apparentStress1Max; + + private Double apparentStress1Min; + + private Double apparentStress2Max; + + private Double apparentStress2Min; + private Double refApparentStressInMpa; + private Double cornerFreq; + + private Double cornerFreqSd; + private Integer dataCount; private Double latitude; @@ -40,12 +63,29 @@ public class MeasuredMwDetails { private String datetime; + private Integer iterations; + + private Double misfit; + public MeasuredMwDetails(MeasuredMwParameters meas, ReferenceMwParameters ref, Event event) { if (meas != null) { - this.mw = meas.getMw(); this.eventId = meas.getEventId(); + this.mw = meas.getMw(); + // this.mwSd = meas.getMwSd(); + this.mw1Max = meas.getMw1Max(); + this.mw1Min = meas.getMw1Min(); + this.mw2Max = meas.getMw2Max(); + this.mw2Min = meas.getMw2Min(); this.apparentStressInMpa = meas.getApparentStressInMpa(); + this.apparentStress1Max = meas.getApparentStress1Max(); + this.apparentStress1Min = meas.getApparentStress1Min(); + this.apparentStress2Max = meas.getApparentStress2Max(); + this.apparentStress2Min = meas.getApparentStress2Min(); + this.cornerFreq = meas.getCornerFrequency(); + // this.cornerFreqSd = meas.getCornerFrequencySd(); this.dataCount = meas.getDataCount(); + this.iterations = meas.getIterations(); + this.misfit = meas.getMisfit(); } if (event != null && event.getEventId() != null) { this.eventId = event.getEventId(); @@ -86,6 +126,15 @@ public MeasuredMwDetails setMw(double mw) { return this; } + public Double getMwSd() { + return mwSd; + } + + public MeasuredMwDetails setMwSd(Double mwSd) { + this.mwSd = mwSd; + return this; + } + public Double getRefMw() { return refMw; } @@ -149,19 +198,190 @@ public MeasuredMwDetails setDatetime(String datetime) { return this; } + public Double getMw1Max() { + return mw1Max; + } + + public MeasuredMwDetails setMw1Max(Double mw1Max) { + this.mw1Max = mw1Max; + return this; + } + + public Double getMw1Min() { + return mw1Min; + } + + public MeasuredMwDetails setMw1Min(Double mw1Min) { + this.mw1Min = mw1Min; + return this; + } + + public Double getMw2Max() { + return mw2Max; + } + + public MeasuredMwDetails setMw2Max(Double mw2Max) { + this.mw2Max = mw2Max; + return this; + } + + public Double getMw2Min() { + return mw2Min; + } + + public MeasuredMwDetails setMw2Min(Double mw2Min) { + this.mw2Min = mw2Min; + return this; + } + + public Double getApparentStress1Max() { + return apparentStress1Max; + } + + public MeasuredMwDetails setApparentStress1Max(Double apparentStress1Max) { + this.apparentStress1Max = apparentStress1Max; + return this; + } + + public Double getApparentStress1Min() { + return apparentStress1Min; + } + + public MeasuredMwDetails setApparentStress1Min(Double apparentStress1Min) { + this.apparentStress1Min = apparentStress1Min; + return this; + } + + public Double getApparentStress2Max() { + return apparentStress2Max; + } + + public MeasuredMwDetails setApparentStress2Max(Double apparentStress2Max) { + this.apparentStress2Max = apparentStress2Max; + return this; + } + + public Double getApparentStress2Min() { + return apparentStress2Min; + } + + public MeasuredMwDetails setApparentStress2Min(Double apparentStress2Min) { + this.apparentStress2Min = apparentStress2Min; + return this; + } + + public Double getCornerFreq() { + return cornerFreq; + } + + public MeasuredMwDetails setCornerFreq(Double cornerFreq) { + this.cornerFreq = cornerFreq; + return this; + } + + public Double getCornerFreqSd() { + return cornerFreqSd; + } + + public MeasuredMwDetails setCornerFreqSd(Double cornerFreqSd) { + this.cornerFreqSd = cornerFreqSd; + return this; + } + + public Integer getIterations() { + return iterations; + } + + public MeasuredMwDetails setIterations(Integer iterations) { + this.iterations = iterations; + return this; + } + + public Double getMisfit() { + return misfit; + } + + public MeasuredMwDetails setMisfit(Double misfit) { + this.misfit = misfit; + return this; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("MeasuredMwDetails [eventId=") + .append(eventId) + .append(", mw=") + .append(mw) + .append(", mwSd=") + .append(mwSd) + .append(", mw1Max=") + .append(mw1Max) + .append(", mw1Min=") + .append(mw1Min) + .append(", mw2Max=") + .append(mw2Max) + .append(", mw2Min=") + .append(mw2Min) + .append(", refMw=") + .append(refMw) + .append(", apparentStressInMpa=") + .append(apparentStressInMpa) + .append(", apparentStress1Max=") + .append(apparentStress1Max) + .append(", apparentStress1Min=") + .append(apparentStress1Min) + .append(", apparentStress2Max=") + .append(apparentStress2Max) + .append(", apparentStress2Min=") + .append(apparentStress2Min) + .append(", refApparentStressInMpa=") + .append(refApparentStressInMpa) + .append(", cornerFreq=") + .append(cornerFreq) + .append(", cornerFreqSd=") + .append(cornerFreqSd) + .append(", dataCount=") + .append(dataCount) + .append(", latitude=") + .append(latitude) + .append(", longitude=") + .append(longitude) + .append(", datetime=") + .append(datetime) + .append(", iterations=") + .append(iterations) + .append(", misfit=") + .append(misfit) + .append("]"); + return builder.toString(); + } + @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((apparentStressInMpa == null) ? 0 : apparentStressInMpa.hashCode()); - result = prime * result + ((datetime == null) ? 0 : datetime.hashCode()); - result = prime * result + ((eventId == null) ? 0 : eventId.hashCode()); - result = prime * result + ((latitude == null) ? 0 : latitude.hashCode()); - result = prime * result + ((longitude == null) ? 0 : longitude.hashCode()); - result = prime * result + ((mw == null) ? 0 : mw.hashCode()); - result = prime * result + ((refApparentStressInMpa == null) ? 0 : refApparentStressInMpa.hashCode()); - result = prime * result + ((refMw == null) ? 0 : refMw.hashCode()); - return result; + return Objects.hash( + apparentStress1Max, + apparentStress1Min, + apparentStress2Max, + apparentStress2Min, + apparentStressInMpa, + cornerFreq, + cornerFreqSd, + dataCount, + datetime, + eventId, + iterations, + latitude, + longitude, + misfit, + mw, + mw1Max, + mw1Min, + mw2Max, + mw2Min, + mwSd, + refApparentStressInMpa, + refMw); } @Override @@ -169,91 +389,32 @@ public boolean equals(Object obj) { if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if (!(obj instanceof MeasuredMwDetails)) { return false; } MeasuredMwDetails other = (MeasuredMwDetails) obj; - if (apparentStressInMpa == null) { - if (other.apparentStressInMpa != null) { - return false; - } - } else if (!apparentStressInMpa.equals(other.apparentStressInMpa)) { - return false; - } - if (datetime == null) { - if (other.datetime != null) { - return false; - } - } else if (!datetime.equals(other.datetime)) { - return false; - } - if (eventId == null) { - if (other.eventId != null) { - return false; - } - } else if (!eventId.equals(other.eventId)) { - return false; - } - if (latitude == null) { - if (other.latitude != null) { - return false; - } - } else if (!latitude.equals(other.latitude)) { - return false; - } - if (longitude == null) { - if (other.longitude != null) { - return false; - } - } else if (!longitude.equals(other.longitude)) { - return false; - } - if (mw == null) { - if (other.mw != null) { - return false; - } - } else if (!mw.equals(other.mw)) { - return false; - } - if (refApparentStressInMpa == null) { - if (other.refApparentStressInMpa != null) { - return false; - } - } else if (!refApparentStressInMpa.equals(other.refApparentStressInMpa)) { - return false; - } - if (refMw == null) { - if (other.refMw != null) { - return false; - } - } else if (!refMw.equals(other.refMw)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "MeasuredMwDetails [eventId=" - + eventId - + ", mw=" - + mw - + ", refMw=" - + refMw - + ", apparentStressInMpa=" - + apparentStressInMpa - + ", refApparentStressInMpa=" - + refApparentStressInMpa - + ", latitude=" - + latitude - + ", longitude=" - + longitude - + ", datetime=" - + datetime - + "]"; + return Objects.equals(apparentStress1Max, other.apparentStress1Max) + && Objects.equals(apparentStress1Min, other.apparentStress1Min) + && Objects.equals(apparentStress2Max, other.apparentStress2Max) + && Objects.equals(apparentStress2Min, other.apparentStress2Min) + && Objects.equals(apparentStressInMpa, other.apparentStressInMpa) + && Objects.equals(cornerFreq, other.cornerFreq) + && Objects.equals(cornerFreqSd, other.cornerFreqSd) + && Objects.equals(dataCount, other.dataCount) + && Objects.equals(datetime, other.datetime) + && Objects.equals(eventId, other.eventId) + && Objects.equals(iterations, other.iterations) + && Objects.equals(latitude, other.latitude) + && Objects.equals(longitude, other.longitude) + && Objects.equals(misfit, other.misfit) + && Objects.equals(mw, other.mw) + && Objects.equals(mw1Max, other.mw1Max) + && Objects.equals(mw1Min, other.mw1Min) + && Objects.equals(mw2Max, other.mw2Max) + && Objects.equals(mw2Min, other.mw2Min) + && Objects.equals(mwSd, other.mwSd) + && Objects.equals(refApparentStressInMpa, other.refApparentStressInMpa) + && Objects.equals(refMw, other.refMw); } @JsonIgnore diff --git a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwParameters.java b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwParameters.java index a18b951e..1e790925 100644 --- a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwParameters.java +++ b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwParameters.java @@ -56,6 +56,22 @@ public class MeasuredMwParameters implements Serializable { @Column(nullable = true) private Double mwSd; + @NumberFormat + @Column(nullable = true) + private Double mw1Min; + + @NumberFormat + @Column(nullable = true) + private Double mw1Max; + + @NumberFormat + @Column(nullable = true) + private Double mw2Min; + + @NumberFormat + @Column(nullable = true) + private Double mw2Max; + @NumberFormat @Column(nullable = true) private Double apparentStressInMpa; @@ -66,7 +82,23 @@ public class MeasuredMwParameters implements Serializable { @NumberFormat @Column(nullable = true) - private Double stressSd; + private Double apparentStressSd; + + @NumberFormat + @Column(nullable = true) + private Double apparentStress1Min; + + @NumberFormat + @Column(nullable = true) + private Double apparentStress1Max; + + @NumberFormat + @Column(nullable = true) + private Double apparentStress2Min; + + @NumberFormat + @Column(nullable = true) + private Double apparentStress2Max; @NumberFormat @Column(nullable = true) @@ -80,8 +112,18 @@ public class MeasuredMwParameters implements Serializable { @Column(nullable = true) private Double misfitSd; + @NumberFormat + @Column(nullable = true) + private Double cornerFrequency; + + @NumberFormat + @Column(nullable = true) + private Double cornerFrequencySd; + private int dataCount; + private int iterations; + public Long getId() { return id; } @@ -122,6 +164,42 @@ public MeasuredMwParameters setMwSd(Double mwSd) { return this; } + public Double getMw1Min() { + return mw1Min; + } + + public MeasuredMwParameters setMw1Min(Double mw1Min) { + this.mw1Min = mw1Min; + return this; + } + + public Double getMw1Max() { + return mw1Max; + } + + public MeasuredMwParameters setMw1Max(Double mw1Max) { + this.mw1Max = mw1Max; + return this; + } + + public Double getMw2Min() { + return mw2Min; + } + + public MeasuredMwParameters setMw2Min(Double mw2Min) { + this.mw2Min = mw2Min; + return this; + } + + public Double getMw2Max() { + return mw2Max; + } + + public MeasuredMwParameters setMw2Max(Double mw2Max) { + this.mw2Max = mw2Max; + return this; + } + public Double getApparentStressInMpa() { return apparentStressInMpa; } @@ -131,12 +209,48 @@ public MeasuredMwParameters setApparentStressInMpa(Double apparentStressInMpa) { return this; } - public Double getStressSd() { - return stressSd; + public Double getApparentStressSd() { + return apparentStressSd; + } + + public MeasuredMwParameters setApparentStressSd(Double apparentStressSd) { + this.apparentStressSd = apparentStressSd; + return this; + } + + public Double getApparentStress1Min() { + return apparentStress1Min; } - public MeasuredMwParameters setStressSd(Double stressSd) { - this.stressSd = stressSd; + public MeasuredMwParameters setApparentStress1Min(Double apparentStress1Min) { + this.apparentStress1Min = apparentStress1Min; + return this; + } + + public Double getApparentStress1Max() { + return apparentStress1Max; + } + + public MeasuredMwParameters setApparentStress1Max(Double apparentStress1Max) { + this.apparentStress1Max = apparentStress1Max; + return this; + } + + public Double getApparentStress2Min() { + return apparentStress2Min; + } + + public MeasuredMwParameters setApparentStress2Min(Double apparentStress2Min) { + this.apparentStress2Min = apparentStress2Min; + return this; + } + + public Double getApparentStress2Max() { + return apparentStress2Max; + } + + public MeasuredMwParameters setApparentStress2Max(Double apparentStress2Max) { + this.apparentStress2Max = apparentStress2Max; return this; } @@ -194,23 +308,85 @@ public MeasuredMwParameters setMeanMisfit(Double meanMisfit) { return this; } + public Double getCornerFrequency() { + return cornerFrequency; + } + + public MeasuredMwParameters setCornerFrequency(Double cornerFrequency) { + this.cornerFrequency = cornerFrequency; + return this; + } + + public Double getCornerFrequencySd() { + return cornerFrequencySd; + } + + public MeasuredMwParameters setCornerFrequencySd(Double cornerFrequencySd) { + this.cornerFrequencySd = cornerFrequencySd; + return this; + } + + public int getIterations() { + return iterations; + } + + public MeasuredMwParameters setIterations(int iterations) { + this.iterations = iterations; + return this; + } + public MeasuredMwParameters merge(MeasuredMwParameters overlay) { this.mw = overlay.getMw(); this.meanMw = overlay.getMeanMw(); this.mwSd = overlay.getMwSd(); + this.mw1Min = overlay.getMw1Min(); + this.mw1Max = overlay.getMw1Max(); + this.mw2Min = overlay.getMw2Min(); + this.mw2Max = overlay.getMw2Max(); this.apparentStressInMpa = overlay.getApparentStressInMpa(); this.meanApparentStressInMpa = overlay.getMeanApparentStressInMpa(); - this.stressSd = overlay.getStressSd(); + this.apparentStressSd = overlay.getApparentStressSd(); + this.apparentStress1Min = overlay.getApparentStress1Min(); + this.apparentStress1Max = overlay.getApparentStress1Max(); + this.apparentStress2Min = overlay.getApparentStress2Min(); + this.apparentStress2Max = overlay.getApparentStress2Max(); this.misfit = overlay.getMisfit(); this.meanMisfit = overlay.getMeanMisfit(); this.misfitSd = overlay.getMisfitSd(); this.dataCount = overlay.getDataCount(); + this.iterations = overlay.getIterations(); + this.cornerFrequency = overlay.getCornerFrequency(); + this.cornerFrequencySd = overlay.getCornerFrequencySd(); return this; } @Override public int hashCode() { - return Objects.hash(apparentStressInMpa, dataCount, eventId, id, meanApparentStressInMpa, meanMisfit, meanMw, misfit, misfitSd, mw, mwSd, stressSd, version); + return Objects.hash( + apparentStress1Max, + apparentStress1Min, + apparentStress2Max, + apparentStress2Min, + apparentStressInMpa, + apparentStressSd, + cornerFrequency, + cornerFrequencySd, + dataCount, + eventId, + id, + iterations, + meanApparentStressInMpa, + meanMisfit, + meanMw, + misfit, + misfitSd, + mw, + mw1Max, + mw1Min, + mw2Max, + mw2Min, + mwSd, + version); } @Override @@ -222,18 +398,29 @@ public boolean equals(Object obj) { return false; } MeasuredMwParameters other = (MeasuredMwParameters) obj; - return Objects.equals(apparentStressInMpa, other.apparentStressInMpa) + return Objects.equals(apparentStress1Max, other.apparentStress1Max) + && Objects.equals(apparentStress1Min, other.apparentStress1Min) + && Objects.equals(apparentStress2Max, other.apparentStress2Max) + && Objects.equals(apparentStress2Min, other.apparentStress2Min) + && Objects.equals(apparentStressInMpa, other.apparentStressInMpa) + && Objects.equals(apparentStressSd, other.apparentStressSd) + && Objects.equals(cornerFrequency, other.cornerFrequency) + && Objects.equals(cornerFrequencySd, other.cornerFrequencySd) && dataCount == other.dataCount && Objects.equals(eventId, other.eventId) && Objects.equals(id, other.id) + && iterations == other.iterations && Objects.equals(meanApparentStressInMpa, other.meanApparentStressInMpa) && Objects.equals(meanMisfit, other.meanMisfit) && Objects.equals(meanMw, other.meanMw) && Objects.equals(misfit, other.misfit) && Objects.equals(misfitSd, other.misfitSd) && Double.doubleToLongBits(mw) == Double.doubleToLongBits(other.mw) + && Objects.equals(mw1Max, other.mw1Max) + && Objects.equals(mw1Min, other.mw1Min) + && Objects.equals(mw2Max, other.mw2Max) + && Objects.equals(mw2Min, other.mw2Min) && Objects.equals(mwSd, other.mwSd) - && Objects.equals(stressSd, other.stressSd) && Objects.equals(version, other.version); } @@ -252,20 +439,42 @@ public String toString() { .append(meanMw) .append(", mwSd=") .append(mwSd) + .append(", mw1Min=") + .append(mw1Min) + .append(", mw1Max=") + .append(mw1Max) + .append(", mw2Min=") + .append(mw2Min) + .append(", mw2Max=") + .append(mw2Max) .append(", apparentStressInMpa=") .append(apparentStressInMpa) .append(", meanApparentStressInMpa=") .append(meanApparentStressInMpa) - .append(", stressSd=") - .append(stressSd) + .append(", apparentStressSd=") + .append(apparentStressSd) + .append(", apparentStress1Min=") + .append(apparentStress1Min) + .append(", apparentStress1Max=") + .append(apparentStress1Max) + .append(", apparentStress2Min=") + .append(apparentStress2Min) + .append(", apparentStress2Max=") + .append(apparentStress2Max) .append(", misfit=") .append(misfit) .append(", meanMisfit=") .append(meanMisfit) .append(", misfitSd=") .append(misfitSd) + .append(", cornerFrequency=") + .append(cornerFrequency) + .append(", cornerFrequencySd=") + .append(cornerFrequencySd) .append(", dataCount=") .append(dataCount) + .append(", iterations=") + .append(iterations) .append("]"); return builder.toString(); } diff --git a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwReportByEvent.java b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwReportByEvent.java index bce93ca3..27df5fac 100644 --- a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwReportByEvent.java +++ b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasuredMwReportByEvent.java @@ -21,10 +21,10 @@ public class MeasuredMwReportByEvent { private Map measuredMwDetails; - private Map fitSpectra; + private Map> fitSpectra; private Map> spectraMeasurements; - public MeasuredMwReportByEvent(Map measuredMwDetails, Map fitSpectra, Map> spectraMeasurements) { + public MeasuredMwReportByEvent(Map measuredMwDetails, Map> fitSpectra, Map> spectraMeasurements) { super(); this.measuredMwDetails = measuredMwDetails; this.fitSpectra = fitSpectra; @@ -46,11 +46,11 @@ public MeasuredMwReportByEvent setMeasuredMwDetails(Map getFitSpectra() { + public Map> getFitSpectra() { return fitSpectra; } - public MeasuredMwReportByEvent setFitSpectra(Map fitSpectra) { + public MeasuredMwReportByEvent setFitSpectra(Map> fitSpectra) { this.fitSpectra = fitSpectra; return this; } diff --git a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasurementJob.java b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasurementJob.java new file mode 100644 index 00000000..a2ef9aef --- /dev/null +++ b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/MeasurementJob.java @@ -0,0 +1,67 @@ +/* +* Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.calibration.model.domain; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; + +public class MeasurementJob { + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private List eventIds = null; + private List stacks = null; + private Boolean autopickingEnabled = Boolean.FALSE; + private Boolean persistResults = Boolean.FALSE; + + public List getEventIds() { + return eventIds; + } + + public MeasurementJob setEventIds(List eventIds) { + this.eventIds = eventIds; + return this; + } + + public List getStacks() { + return stacks; + } + + public MeasurementJob setStacks(List stacks) { + this.stacks = stacks; + return this; + } + + public Boolean getAutopickingEnabled() { + return autopickingEnabled; + } + + public MeasurementJob setAutopickingEnabled(Boolean autopickingEnabled) { + this.autopickingEnabled = autopickingEnabled; + return this; + } + + public Boolean getPersistResults() { + return persistResults; + } + + public MeasurementJob setPersistResults(Boolean persistResults) { + this.persistResults = persistResults; + return this; + } + +} diff --git a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/ShapeFitterConstraints.java b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/ShapeFitterConstraints.java index 87d948cc..771488c8 100644 --- a/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/ShapeFitterConstraints.java +++ b/calibration-service/calibration-model/src/main/java/gov/llnl/gnem/apps/coda/calibration/model/domain/ShapeFitterConstraints.java @@ -126,6 +126,8 @@ public class ShapeFitterConstraints implements Serializable { private double maxGamma; @NumberFormat private int iterations; + @NumberFormat + private int fittingPointCount; public Long getId() { return id; @@ -514,6 +516,15 @@ public ShapeFitterConstraints setIterations(int iterations) { return this; } + public int getFittingPointCount() { + return fittingPointCount; + } + + public ShapeFitterConstraints setFittingPointCount(int fittingPointCount) { + this.fittingPointCount = fittingPointCount; + return this; + } + public ShapeFitterConstraints merge(ShapeFitterConstraints other) { if (other.getId() != null) { id = other.getId(); @@ -562,108 +573,17 @@ public ShapeFitterConstraints merge(ShapeFitterConstraints other) { iterations = other.getIterations(); gDistMin = other.getgDistMin(); gDistMax = other.getgDistMax(); + fittingPointCount = other.getFittingPointCount(); return this; } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("CalibrationShapeFitterConstraints [id=") - .append(id) - .append(", version=") - .append(version) - .append(", maxVP1=") - .append(maxVP1) - .append(", minVP1=") - .append(minVP1) - .append(", v0reg=") - .append(v0reg) - .append(", maxVP2=") - .append(maxVP2) - .append(", minVP2=") - .append(minVP2) - .append(", maxVP3=") - .append(maxVP3) - .append(", minVP3=") - .append(minVP3) - .append(", maxBP1=") - .append(maxBP1) - .append(", minBP1=") - .append(minBP1) - .append(", b0reg=") - .append(b0reg) - .append(", maxBP2=") - .append(maxBP2) - .append(", minBP2=") - .append(minBP2) - .append(", maxBP3=") - .append(maxBP3) - .append(", minBP3=") - .append(minBP3) - .append(", maxGP1=") - .append(maxGP1) - .append(", minGP1=") - .append(minGP1) - .append(", g0reg=") - .append(g0reg) - .append(", maxGP2=") - .append(maxGP2) - .append(", minGP2=") - .append(minGP2) - .append(", g1reg=") - .append(g1reg) - .append(", maxGP3=") - .append(maxGP3) - .append(", minGP3=") - .append(minGP3) - .append(", yvvMin=") - .append(yvvMin) - .append(", yvvMax=") - .append(yvvMax) - .append(", vDistMax=") - .append(vDistMax) - .append(", vDistMin=") - .append(vDistMin) - .append(", ybbMin=") - .append(ybbMin) - .append(", ybbMax=") - .append(ybbMax) - .append(", bDistMax=") - .append(bDistMax) - .append(", bDistMin=") - .append(bDistMin) - .append(", yggMin=") - .append(yggMin) - .append(", yggMax=") - .append(yggMax) - .append(", gDistMin=") - .append(gDistMin) - .append(", gDistMax=") - .append(gDistMax) - .append(", minIntercept=") - .append(minIntercept) - .append(", maxIntercept=") - .append(maxIntercept) - .append(", minBeta=") - .append(minBeta) - .append(", maxBeta=") - .append(maxBeta) - .append(", minGamma=") - .append(minGamma) - .append(", maxGamma=") - .append(maxGamma) - .append(", iterations=") - .append(iterations) - .append("]"); - return builder.toString(); - } - @Override public int hashCode() { return Objects.hash( b0reg, bDistMax, bDistMin, + fittingPointCount, g0reg, g1reg, gDistMax, @@ -718,6 +638,7 @@ public boolean equals(Object obj) { return Double.doubleToLongBits(b0reg) == Double.doubleToLongBits(other.b0reg) && Double.doubleToLongBits(bDistMax) == Double.doubleToLongBits(other.bDistMax) && Double.doubleToLongBits(bDistMin) == Double.doubleToLongBits(other.bDistMin) + && fittingPointCount == other.fittingPointCount && Double.doubleToLongBits(g0reg) == Double.doubleToLongBits(other.g0reg) && Double.doubleToLongBits(g1reg) == Double.doubleToLongBits(other.g1reg) && Double.doubleToLongBits(gDistMax) == Double.doubleToLongBits(other.gDistMax) @@ -759,4 +680,100 @@ public boolean equals(Object obj) { && Double.doubleToLongBits(yvvMax) == Double.doubleToLongBits(other.yvvMax) && Double.doubleToLongBits(yvvMin) == Double.doubleToLongBits(other.yvvMin); } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("ShapeFitterConstraints [id=") + .append(id) + .append(", version=") + .append(version) + .append(", maxVP1=") + .append(maxVP1) + .append(", minVP1=") + .append(minVP1) + .append(", v0reg=") + .append(v0reg) + .append(", maxVP2=") + .append(maxVP2) + .append(", minVP2=") + .append(minVP2) + .append(", maxVP3=") + .append(maxVP3) + .append(", minVP3=") + .append(minVP3) + .append(", maxBP1=") + .append(maxBP1) + .append(", minBP1=") + .append(minBP1) + .append(", b0reg=") + .append(b0reg) + .append(", maxBP2=") + .append(maxBP2) + .append(", minBP2=") + .append(minBP2) + .append(", maxBP3=") + .append(maxBP3) + .append(", minBP3=") + .append(minBP3) + .append(", maxGP1=") + .append(maxGP1) + .append(", minGP1=") + .append(minGP1) + .append(", g0reg=") + .append(g0reg) + .append(", maxGP2=") + .append(maxGP2) + .append(", minGP2=") + .append(minGP2) + .append(", g1reg=") + .append(g1reg) + .append(", maxGP3=") + .append(maxGP3) + .append(", minGP3=") + .append(minGP3) + .append(", yvvMin=") + .append(yvvMin) + .append(", yvvMax=") + .append(yvvMax) + .append(", vDistMax=") + .append(vDistMax) + .append(", vDistMin=") + .append(vDistMin) + .append(", ybbMin=") + .append(ybbMin) + .append(", ybbMax=") + .append(ybbMax) + .append(", bDistMax=") + .append(bDistMax) + .append(", bDistMin=") + .append(bDistMin) + .append(", yggMin=") + .append(yggMin) + .append(", yggMax=") + .append(yggMax) + .append(", gDistMin=") + .append(gDistMin) + .append(", gDistMax=") + .append(gDistMax) + .append(", minIntercept=") + .append(minIntercept) + .append(", maxIntercept=") + .append(maxIntercept) + .append(", minBeta=") + .append(minBeta) + .append(", maxBeta=") + .append(maxBeta) + .append(", minGamma=") + .append(minGamma) + .append(", maxGamma=") + .append(maxGamma) + .append(", iterations=") + .append(iterations) + .append(", fittingPointCount=") + .append(fittingPointCount) + .append("]"); + return builder.toString(); + } + } \ No newline at end of file diff --git a/calibration-service/calibration-repository/pom.xml b/calibration-service/calibration-repository/pom.xml index 67b525fb..8fd56f08 100644 --- a/calibration-service/calibration-repository/pom.xml +++ b/calibration-service/calibration-repository/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/calibration-service/calibration-service-api/pom.xml b/calibration-service/calibration-service-api/pom.xml index 30f88a87..498120dd 100644 --- a/calibration-service/calibration-service-api/pom.xml +++ b/calibration-service/calibration-service-api/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/CalibrationService.java b/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/CalibrationService.java index f28fd530..8bfdd687 100644 --- a/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/CalibrationService.java +++ b/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/CalibrationService.java @@ -28,9 +28,9 @@ public interface CalibrationService { public boolean clearData(); - public Future> makeMwMeasurements(Boolean autoPickingEnabled); + public Future> makeMwMeasurements(Boolean autoPickingEnabled, Boolean persistResults); - public Future> makeMwMeasurements(Boolean autoPickingEnabled, Set eventIds); + public Future> makeMwMeasurements(Boolean autoPickingEnabled, Boolean persistResults, Set eventIds); - public Future> makeMwMeasurements(Boolean autoPickingEnabled, List stacks); + public Future> makeMwMeasurements(Boolean autoPickingEnabled, Boolean persistResults, List stacks); } diff --git a/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/SpectraMeasurementService.java b/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/SpectraMeasurementService.java index a884153b..bf3a3d70 100644 --- a/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/SpectraMeasurementService.java +++ b/calibration-service/calibration-service-api/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/api/SpectraMeasurementService.java @@ -50,7 +50,7 @@ public List measureSpectra(List generatedSynt public long count(); - public Spectra computeSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase); + public Spectra computeReferenceSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase); - public Spectra getFitSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase); + public List getFitSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase); } diff --git a/calibration-service/calibration-service-impl/pom.xml b/calibration-service/calibration-service-impl/pom.xml index 78043885..d586aa92 100644 --- a/calibration-service/calibration-service-impl/pom.xml +++ b/calibration-service/calibration-service-impl/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration calibration-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/AutopickingServiceImpl.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/AutopickingServiceImpl.java index 1278fea3..407ffd2a 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/AutopickingServiceImpl.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/AutopickingServiceImpl.java @@ -90,7 +90,9 @@ public Collection autoPickVelocityMeasuredWaveforms(fin } TimeSeries segment = converter.convert(vel.getWaveform()); - segment.interpolate(1.0); + if (segment.getSamprate() > 1.0) { + segment.interpolate(1.0); + } double stopTime = endTimePicker.getEndTime( segment.getData(), diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/CalibrationServiceImpl.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/CalibrationServiceImpl.java index 7d3ae881..d73dfea2 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/CalibrationServiceImpl.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/CalibrationServiceImpl.java @@ -84,11 +84,13 @@ import gov.llnl.gnem.apps.coda.common.model.domain.FrequencyBand; import gov.llnl.gnem.apps.coda.common.model.domain.SharedFrequencyBandParameters; import gov.llnl.gnem.apps.coda.common.model.domain.Station; +import gov.llnl.gnem.apps.coda.common.model.domain.SyntheticCoda; import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; import gov.llnl.gnem.apps.coda.common.model.domain.WaveformPick; import gov.llnl.gnem.apps.coda.common.model.messaging.Result; import gov.llnl.gnem.apps.coda.common.model.util.LightweightIllegalStateException; import gov.llnl.gnem.apps.coda.common.model.util.PICK_TYPES; +import gov.llnl.gnem.apps.coda.common.model.util.SPECTRA_TYPES; import gov.llnl.gnem.apps.coda.common.service.api.NotificationService; import gov.llnl.gnem.apps.coda.common.service.api.WaveformService; import gov.llnl.gnem.apps.coda.common.service.util.WaveformUtils; @@ -157,13 +159,13 @@ public CalibrationServiceImpl(WaveformService waveformService, PeakVelocityMeasu } @Override - public Future> makeMwMeasurements(Boolean autoPickingEnabled) { + public Future> makeMwMeasurements(Boolean autoPickingEnabled, Boolean persistResults) { final Long id = atomicLong.getAndIncrement(); Future> future = CompletableFuture.completedFuture(new Result<>(false, new MeasuredMwReportByEvent())); Supplier measurementFunc = () -> { List stationNames = siteParamsService.findDistinctStationNames(); List stacks = waveformService.getAllActiveStacksInStationNames(stationNames); - return makeMwMeasurements(id, autoPickingEnabled, stacks); + return makeMwMeasurements(id, autoPickingEnabled, persistResults, stacks); }; future = getMeasurementFuture(id, measurementFunc); @@ -171,14 +173,14 @@ public Future> makeMwMeasurements(Boolean autoPi } @Override - public Future> makeMwMeasurements(Boolean autoPickingEnabled, Set eventIds) { + public Future> makeMwMeasurements(Boolean autoPickingEnabled, Boolean persistResults, Set eventIds) { final Long id = atomicLong.getAndIncrement(); Supplier measurementFunc = () -> { MeasuredMwReportByEvent measuredMws = new MeasuredMwReportByEvent(); List stationNames = siteParamsService.findDistinctStationNames(); List stacks = eventIds.stream().flatMap(eventId -> waveformService.findAllActiveStacksByEventIdAndStationNames(eventId, stationNames).stream()).collect(Collectors.toList()); if (stacks != null && !stacks.isEmpty()) { - measuredMws = makeMwMeasurements(id, autoPickingEnabled, stacks); + measuredMws = makeMwMeasurements(id, autoPickingEnabled, persistResults, stacks); } else { notificationService.post( new MeasurementStatusEvent(id, @@ -195,7 +197,7 @@ public Future> makeMwMeasurements(Boolean autoPi } @Override - public Future> makeMwMeasurements(Boolean autoPickingEnabled, List waveforms) { + public Future> makeMwMeasurements(Boolean autoPickingEnabled, Boolean persistResults, List waveforms) { final Long id = atomicLong.getAndIncrement(); Supplier measurementFunc = () -> { MeasuredMwReportByEvent measuredMws = new MeasuredMwReportByEvent(); @@ -207,7 +209,7 @@ public Future> makeMwMeasurements(Boolean autoPi .collect(Collectors.toList()); if (stacks != null && !stacks.isEmpty()) { - measuredMws = makeMwMeasurements(id, autoPickingEnabled, stacks); + measuredMws = makeMwMeasurements(id, autoPickingEnabled, persistResults, stacks); } else { String msg = "No valid waveforms provided. Waveforms must have event and station information and must match a station for which a site calibration exists."; log.trace(msg); @@ -240,7 +242,7 @@ private Future> getMeasurementFuture(final Long return future; } - private MeasuredMwReportByEvent makeMwMeasurements(Long id, Boolean autoPickingEnabled, List stacks) { + private MeasuredMwReportByEvent makeMwMeasurements(Long id, Boolean autoPickingEnabled, Boolean persistResults, List stacks) { log.info("Starting measurement at {}", LocalDateTime.now()); MeasuredMwReportByEvent details = new MeasuredMwReportByEvent(); if (stacks != null) { @@ -255,24 +257,19 @@ private MeasuredMwReportByEvent makeMwMeasurements(Long id, Boolean autoPickingE .collect(Collectors.toList()); if (autoPickingEnabled) { velocityMeasured = picker.autoPickVelocityMeasuredWaveforms(velocityMeasured, frequencyBandParameterMap); + if (persistResults) { + velocityMeasured = velocityMeasured.parallelStream().map(v -> v.setWaveform(waveformService.save(v.getWaveform()))).collect(Collectors.toList()); + } } - log.trace("velocity measurements {}", velocityMeasured); - final Map snrFilterMap = new HashMap<>(frequencyBandParameterMap); velocityMeasured = filterVelocityBySnr(snrFilterMap, velocityMeasured.stream()); measStacks = velocityMeasured.stream().map(vel -> vel.getWaveform()).collect(Collectors.toList()); measStacks = filterToEndPicked(measStacks); - log.trace("filtered stacks {}", measStacks); - - List spectra = spectraCalc.measureAmplitudes( - syntheticGenerationService.generateSynthetics(measStacks, frequencyBandParameterMap), - frequencyBandParameterMap, - velocityConfig, - stationFrequencyBandMap); + List synthetics = syntheticGenerationService.generateSynthetics(measStacks, frequencyBandParameterMap); - log.trace("spectra {}", spectra); + List spectra = spectraCalc.measureAmplitudes(synthetics, frequencyBandParameterMap, velocityConfig, stationFrequencyBandMap); List measuredMwsParams = siteCalibrationService.fitMws( spectraByFrequencyBand(spectra), @@ -282,8 +279,6 @@ private MeasuredMwReportByEvent makeMwMeasurements(Long id, Boolean autoPickingE stationFrequencyBandMap, PICK_TYPES.LG); - log.trace("measured mw params {}", measuredMwsParams); - Map measuredMwsMap = Optional.ofNullable(measuredMwsParams).orElseGet(ArrayList::new).stream().map(mwp -> { Event event = getEventForId(mwp.getEventId(), eventsInStacks); if (event != null) { @@ -293,15 +288,20 @@ private MeasuredMwReportByEvent makeMwMeasurements(Long id, Boolean autoPickingE } }).collect(Collectors.toMap(kv -> kv.getKey(), kv -> kv.getValue())); - Map fitSpectra = measuredMwsMap.entrySet() - .parallelStream() - .map( - mw -> new AbstractMap.SimpleEntry<>(mw.getKey().getEventId(), - spectraCalc.computeFitSpectra( - mw.getValue(), - frequencyBandParameterMap.keySet(), - PICK_TYPES.LG))) - .collect(Collectors.toConcurrentMap(kv -> kv.getKey(), kv -> kv.getValue())); + Map> fitSpectra = measuredMwsMap.entrySet() + .parallelStream() + .map( + mw -> new AbstractMap.SimpleEntry<>(mw.getKey().getEventId(), + computeFitSpectra(mw.getValue(), frequencyBandParameterMap.keySet(), PICK_TYPES.LG))) + .collect(Collectors.toConcurrentMap(kv -> kv.getKey(), kv -> kv.getValue())); + + if (persistResults) { + peakVelocityMeasurementsService.deleteAll(); + syntheticService.deleteAll(); + peakVelocityMeasurementsService.save(velocityMeasured); + syntheticService.save(synthetics); + } + details.setFitSpectra(fitSpectra); details.setMeasuredMwDetails( @@ -319,6 +319,19 @@ private MeasuredMwReportByEvent makeMwMeasurements(Long id, Boolean autoPickingE return details; } + private List computeFitSpectra(MeasuredMwParameters event, Set frequencyBands, PICK_TYPES selectedPhase) { + List spectra = new ArrayList<>(); + if (event != null) { + spectra.add(spectraCalc.computeFitSpectra(event, frequencyBands, selectedPhase)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw1Max(), event.getApparentStress1Max(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ1)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw1Min(), event.getApparentStress1Min(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ1)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw2Max(), event.getApparentStress2Max(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ2)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw2Min(), event.getApparentStress2Min(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ2)); + } + return spectra; + + } + private Event getEventForId(String eventId, List eventsInStacks) { return Optional.ofNullable(waveformService.findEventById(eventId)) .filter(Objects::nonNull) diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java index 661e175a..42231627 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/Joint1DPathCorrection.java @@ -342,14 +342,14 @@ private Map>> removeS */ public double costFunction(Map> evidStaData, Map> dataMap, Map> distanceMap, Map stationIdxMap, FrequencyBand frequencyBand, double[] optimizationParams) { - Map> localDataMap = new HashMap<>(); - double cost = 0.0; double freq0 = Math.sqrt(frequencyBand.getLowFrequency() * frequencyBand.getHighFrequency()); - for (Entry> evidEntry : evidStaData.entrySet()) { + return evidStaData.entrySet().parallelStream().map(evidEntry -> { + double cost = 0.0; Event evid = evidEntry.getKey(); Map stationMapData = evidEntry.getValue(); + Map stationValues = new HashMap<>(); DoubleArrayList dataVec = new DoubleArrayList(stationMapData.size()); for (Entry entry : stationMapData.entrySet()) { @@ -362,23 +362,19 @@ public double costFunction(Map> evidStaD double pdat = site + spectraCalc.log10ESHcorrection(p1, p2, xcross, xtrans, del) - del * Math.PI * freq0 * efact / (q * vphase); double adjustedVal = dataMap.get(evid).get(entry.getKey()) - pdat; dataVec.add(adjustedVal); - if (!localDataMap.containsKey(evid)) { - localDataMap.put(evid, new HashMap()); - } - localDataMap.get(evid).put(entry.getKey(), adjustedVal); + stationValues.put(entry.getKey(), adjustedVal); } if (dataVec.size() > 1) { double huberDel = .5d; double median = dataVec.median(); for (Entry entry1 : stationMapData.entrySet()) { - double diff = Math.abs(localDataMap.get(evid).get(entry1.getKey()) - median); + double diff = Math.abs(stationValues.get(entry1.getKey()) - median); cost = cost + (Math.pow(huberDel, 2.0) + (Math.sqrt(1d + Math.pow(diff / huberDel, 2.0)) - 1d)); } } - } - return cost; - + return cost; + }).reduce(0.0d, Double::sum); } /** diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/ServiceConfig.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/ServiceConfig.java index 7cc1a790..67111495 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/ServiceConfig.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/ServiceConfig.java @@ -31,6 +31,9 @@ public class ServiceConfig { private static ExecutorService measurementServicePool; + @Value("${spectraTruncationEnabled:true}") + private boolean spectraTruncationEnabled; + @Value("${measurementPoolSize:10}") private int measurementPoolSize; @@ -49,4 +52,13 @@ public ExecutorService getMeasurementExecutor() { return measurementServicePool; } + public boolean isSpectraTruncationEnabled() { + return spectraTruncationEnabled; + } + + public ServiceConfig setSpectraTruncationEnabled(boolean spectraTruncationEnabled) { + this.spectraTruncationEnabled = spectraTruncationEnabled; + return this; + } + } diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SiteCalibrationServiceImpl.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SiteCalibrationServiceImpl.java index d8e81265..d6317a68 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SiteCalibrationServiceImpl.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SiteCalibrationServiceImpl.java @@ -53,36 +53,39 @@ public class SiteCalibrationServiceImpl implements SiteCalibrationService { private static final Logger log = LoggerFactory.getLogger(SiteCalibrationServiceImpl.class); - private static final double UNUSED_DISTANCE = -1d; private static final double DYNE_LOG10_ADJUSTMENT = 7d; + + private static final double ARBITRARY_LOW_STRESS_SIGMA = 0.01; private MdacCalculatorService mdac; private SiteFrequencyBandParametersService siteParamsService; private MeasuredMwsService measuredMwsService; private SpectraCalculator spectraCalc; + private ServiceConfig serviceConfig; @Autowired - public SiteCalibrationServiceImpl(MdacCalculatorService mdac, SiteFrequencyBandParametersService siteParamsService, MeasuredMwsService measuredMwsService, SpectraCalculator spectraCalc) { + public SiteCalibrationServiceImpl(ServiceConfig serviceConfig, MdacCalculatorService mdac, SiteFrequencyBandParametersService siteParamsService, MeasuredMwsService measuredMwsService, + SpectraCalculator spectraCalc) { this.mdac = mdac; this.siteParamsService = siteParamsService; this.measuredMwsService = measuredMwsService; this.spectraCalc = spectraCalc; + this.serviceConfig = serviceConfig; } @Override public List fitMws(Map> dataByFreqBand, MdacParametersFI mdacFI, Map mdacPS, Map> refMws, Map> stationFrequencyBandParameters, PICK_TYPES selectedPhase) { MdacParametersPS psRows = mdacPS.get(selectedPhase); - Map>> freqBandEvidStaMeasurementsMap = mapToEventAndStation(dataByFreqBand); + Map>> evidFreqBandStaMeasurementsMap = mapToEventAndStation(dataByFreqBand); Map, SortedMap>> weightFunctionMapByEvent = new HashMap<>(); Map> averageMapByEvent = new HashMap<>(); - for (Entry>> evidStaMap : freqBandEvidStaMeasurementsMap.entrySet()) { - FrequencyBand freqBand = evidStaMap.getKey(); - for (Entry> staMwMap : evidStaMap.getValue().entrySet()) { - Event evid = staMwMap.getKey(); - + for (Entry>> evidFreqMap : evidFreqBandStaMeasurementsMap.entrySet()) { + Event evid = evidFreqMap.getKey(); + for (Entry> freqStaMap : evidFreqMap.getValue().entrySet()) { + FrequencyBand freqBand = freqStaMap.getKey(); weightFunctionMapByEvent.put(evid, this::lowerFreqHigherWeights); - for (Entry staMwEntry : staMwMap.getValue().entrySet()) { + for (Entry staMwEntry : freqStaMap.getValue().entrySet()) { double amp = staMwEntry.getValue().getPathAndSiteCorrected(); if (amp != 0.0) { if (!averageMapByEvent.containsKey(evid)) { @@ -109,7 +112,7 @@ public Map> measureSite MdacParametersPS psRows = mdacPS.get(selectedPhase); //Input - Map>> freqBandEvidStaMeasurementsMap = mapToEventAndStation(dataByFreqBand); + Map>> evidFreqBandStaMeasurementsMap = mapToEventAndStation(dataByFreqBand); Map, SortedMap>> weightFunctionMapByEvent = new HashMap<>(); @@ -123,36 +126,53 @@ public Map> measureSite Map> siteCorrections = new HashMap<>(); //0) Determine if we have a RefMw in the dataset with a GT stress + boolean hasGtSpectra = refMws.values().stream().flatMap(refs -> refs.stream()).filter(ref -> ref.getRefApparentStressInMpa() != null).anyMatch(ref -> ref.getRefApparentStressInMpa() > 0.0); //0-1A) If yes then omit everything above the corner frequency for MDAC model only events //0-1B) Add it to the GT spectra event map //0-1C) Weight the Mw fit for that event 1.0 at all bands //0-2A) If no then weight the corner frequency highly, the low middle, and the high low //1) Generate spectra for reference events and get the site correction for each station that saw it. - for (Entry>> evidStaMap : freqBandEvidStaMeasurementsMap.entrySet()) { - FrequencyBand freqBand = evidStaMap.getKey(); - Double lowFreq = freqBand.getLowFrequency(); - Double highFreq = freqBand.getHighFrequency(); - double centerFreq = (highFreq + lowFreq) / 2; - for (Entry> staMwMap : evidStaMap.getValue().entrySet()) { - Event evid = staMwMap.getKey(); - if (refMws != null && refMws.containsKey(evid.getEventId())) { - List refMwsParams = refMws.get(evid.getEventId()); - ReferenceMwParameters refMw = refMwsParams.stream().findFirst().orElse(null); - if (refMw != null) { - double mw = refMw.getRefMw(); - MdacParametersFI mdacFiEntry = new MdacParametersFI(mdacFI); - if (refMw.getRefApparentStressInMpa() != null && refMw.getRefApparentStressInMpa() != 0.0) { - mdacFiEntry.setSigma(refMw.getRefApparentStressInMpa()); - mdacFiEntry.setPsi(0.0); - weightFunctionMapByEvent.put(evid, this::noWeights); + for (Entry>> evidFreqMap : evidFreqBandStaMeasurementsMap.entrySet()) { + Event evid = evidFreqMap.getKey(); + if (refMws != null && refMws.containsKey(evid.getEventId())) { + List refMwsParams = refMws.get(evid.getEventId()); + ReferenceMwParameters refMw = refMwsParams.stream().findFirst().orElse(null); + if (refMw != null) { + double mw = refMw.getRefMw(); + boolean evidHasSpectra = false; + MdacParametersFI mdacFiEntry = new MdacParametersFI(mdacFI); + if (refMw.getRefApparentStressInMpa() != null && refMw.getRefApparentStressInMpa() != 0.0) { + mdacFiEntry.setSigma(refMw.getRefApparentStressInMpa()); + mdacFiEntry.setPsi(0.0); + weightFunctionMapByEvent.put(evid, this::evenWeights); + evidHasSpectra = true; + } else if (serviceConfig.isSpectraTruncationEnabled() && hasGtSpectra) { + mdacFiEntry.setSigma(ARBITRARY_LOW_STRESS_SIGMA); + weightFunctionMapByEvent.put(evid, this::evenWeights); + } + + Function mdacFunc = mdac.getCalculateMdacSourceSpectraFunction(psRows, mdacFiEntry, mw); + double cornerFreq = 0.0; + if (hasGtSpectra && !evidHasSpectra) { + cornerFreq = mdac.getCornerFrequency(mdacFunc); + } + + for (Entry> freqStaMap : evidFreqMap.getValue().entrySet()) { + FrequencyBand freqBand = freqStaMap.getKey(); + Double lowFreq = freqBand.getLowFrequency(); + Double highFreq = freqBand.getHighFrequency(); + + if (serviceConfig.isSpectraTruncationEnabled() && cornerFreq > 0.0 && highFreq > cornerFreq) { + continue; } - double[] refSpectra = mdac.calculateMdacSourceSpectra(psRows, mdacFiEntry, centerFreq, mw, UNUSED_DISTANCE); + double centerFreq = (highFreq + lowFreq) / 2; - for (Entry staMwEntry : staMwMap.getValue().entrySet()) { + double[] refSpectra = mdacFunc.apply(centerFreq); - double amp = staMwEntry.getValue().getPathCorrected(); + for (Entry staMwEntry : freqStaMap.getValue().entrySet()) { + double amp = staMwEntry.getValue().getPathCorrected(); if (!staFreqBandSiteCorrectionMapReferenceEvents.containsKey(staMwEntry.getKey())) { staFreqBandSiteCorrectionMapReferenceEvents.put(staMwEntry.getKey(), new HashMap()); } @@ -168,16 +188,16 @@ public Map> measureSite } } } - weightFunctionMapByEvent.putIfAbsent(evid, this::lowerFreqHigherWeights); } + weightFunctionMapByEvent.putIfAbsent(evid, this::lowerFreqHigherWeights); } //2) For every station with a site correction measured apply it to every other event and get average site term for every frequency band - for (Entry>> evidStaMap : freqBandEvidStaMeasurementsMap.entrySet()) { - FrequencyBand freqBand = evidStaMap.getKey(); - for (Entry> evidStaEntry : evidStaMap.getValue().entrySet()) { - Event evid = evidStaEntry.getKey(); - for (Entry staMwEntry : evidStaEntry.getValue().entrySet()) { + for (Entry>> evidFreqMap : evidFreqBandStaMeasurementsMap.entrySet()) { + Event evid = evidFreqMap.getKey(); + for (Entry> freqStaMap : evidFreqMap.getValue().entrySet()) { + FrequencyBand freqBand = freqStaMap.getKey(); + for (Entry staMwEntry : freqStaMap.getValue().entrySet()) { if (staFreqBandSiteCorrectionMapReferenceEvents.containsKey(staMwEntry.getKey()) && staFreqBandSiteCorrectionMapReferenceEvents.get(staMwEntry.getKey()).containsKey(freqBand)) { double amp = staMwEntry.getValue().getPathCorrected(); if (!averageMapByEvent.containsKey(evid)) { @@ -195,12 +215,12 @@ public Map> measureSite } //3) For all measurements offset by the average site term for each station/frequency band to get the final site terms - for (Entry>> evidStaMap : freqBandEvidStaMeasurementsMap.entrySet()) { - FrequencyBand freqBand = evidStaMap.getKey(); - for (Entry> evidStaEntry : evidStaMap.getValue().entrySet()) { - Event evid = evidStaEntry.getKey(); + for (Entry>> evidFreqMap : evidFreqBandStaMeasurementsMap.entrySet()) { + Event evid = evidFreqMap.getKey(); + for (Entry> freqStaMap : evidFreqMap.getValue().entrySet()) { + FrequencyBand freqBand = freqStaMap.getKey(); if (averageMapByEvent.containsKey(evid) && averageMapByEvent.get(evid).containsKey(freqBand)) { - for (Entry staMwEntry : evidStaEntry.getValue().entrySet()) { + for (Entry staMwEntry : freqStaMap.getValue().entrySet()) { double amp = staMwEntry.getValue().getPathCorrected(); if (!staFreqBandSiteCorrectionMapAverage.containsKey(staMwEntry.getKey())) { staFreqBandSiteCorrectionMapAverage.put(staMwEntry.getKey(), new HashMap()); @@ -208,15 +228,9 @@ public Map> measureSite if (!staFreqBandSiteCorrectionMapAverage.get(staMwEntry.getKey()).containsKey(freqBand)) { staFreqBandSiteCorrectionMapAverage.get(staMwEntry.getKey()).put(freqBand, new SummaryStatistics()); } - //We want to keep the reference events as-is - if (staFreqBandSiteCorrectionMapReferenceEvents.containsKey(staMwEntry.getKey()) - && staFreqBandSiteCorrectionMapReferenceEvents.get(staMwEntry.getKey()).containsKey(freqBand)) { - staFreqBandSiteCorrectionMapAverage.get(staMwEntry.getKey()).put(freqBand, staFreqBandSiteCorrectionMapReferenceEvents.get(staMwEntry.getKey()).get(freqBand)); - } else { - double refAmp = averageMapByEvent.get(evid).get(freqBand).getMean(); - double ampDiff = refAmp - amp; - staFreqBandSiteCorrectionMapAverage.get(staMwEntry.getKey()).get(freqBand).addValue(ampDiff); - } + double refAmp = averageMapByEvent.get(evid).get(freqBand).getMean(); + double ampDiff = refAmp - amp; + staFreqBandSiteCorrectionMapAverage.get(staMwEntry.getKey()).get(freqBand).addValue(ampDiff); } } } @@ -224,11 +238,11 @@ public Map> measureSite //4) Re-average the events using the new site corrections averageMapByEvent.clear(); - for (Entry>> evidStaMap : freqBandEvidStaMeasurementsMap.entrySet()) { - FrequencyBand freqBand = evidStaMap.getKey(); - for (Entry> evidStaEntry : evidStaMap.getValue().entrySet()) { - Event evid = evidStaEntry.getKey(); - for (Entry staMwEntry : evidStaEntry.getValue().entrySet()) { + for (Entry>> evidFreqMap : evidFreqBandStaMeasurementsMap.entrySet()) { + Event evid = evidFreqMap.getKey(); + for (Entry> freqStaMap : evidFreqMap.getValue().entrySet()) { + FrequencyBand freqBand = freqStaMap.getKey(); + for (Entry staMwEntry : freqStaMap.getValue().entrySet()) { if (staFreqBandSiteCorrectionMapAverage.containsKey(staMwEntry.getKey()) && staFreqBandSiteCorrectionMapAverage.get(staMwEntry.getKey()).containsKey(freqBand)) { double amp = staMwEntry.getValue().getPathCorrected(); if (!averageMapByEvent.containsKey(evid)) { @@ -281,7 +295,7 @@ private void overwriteSiteParams(Map staMap.values().stream()).collect(Collectors.toList())); } - private SortedMap noWeights(Map frequencies) { + private SortedMap evenWeights(Map frequencies) { SortedMap weightMap = new TreeMap<>(); for (Double frequency : frequencies.keySet()) { weightMap.put(frequency, 1d); @@ -299,24 +313,22 @@ private SortedMap lowerFreqHigherWeights(Map fre return weightMap; } - public static Map>> mapToEventAndStation(Map> dataByFreqBand) { - - Map>> data = new HashMap<>(); + public static Map>> mapToEventAndStation(Map> dataByFreqBand) { + Map>> data = new HashMap<>(); if (dataByFreqBand != null) { - for (Entry> entries : dataByFreqBand.entrySet()) { - if (!data.containsKey(entries.getKey())) { - data.put(entries.getKey(), new HashMap<>()); - } - Map> eventMap = data.get(entries.getKey()); for (SpectraMeasurement entry : entries.getValue()) { if (WaveformUtils.isValidWaveform(entry.getWaveform())) { Event event = entry.getWaveform().getEvent(); Station station = entry.getWaveform().getStream().getStation(); - if (!eventMap.containsKey(event)) { - eventMap.put(event, new HashMap<>()); + if (!data.containsKey(event)) { + data.put(event, new HashMap<>()); + } + Map> bandMap = data.get(event); + if (!bandMap.containsKey(entries.getKey())) { + bandMap.put(entries.getKey(), new HashMap<>()); } - Map stationMap = eventMap.get(event); + Map stationMap = bandMap.get(entries.getKey()); stationMap.put(station, entry); } else { log.debug("No valid waveform for {}", entry); @@ -362,4 +374,13 @@ public SiteCalibrationServiceImpl setSpectraCalc(SpectraCalculator spectraCalc) this.spectraCalc = spectraCalc; return this; } + + public ServiceConfig getServiceConfig() { + return serviceConfig; + } + + public SiteCalibrationServiceImpl setServiceConfig(ServiceConfig serviceConfig) { + this.serviceConfig = serviceConfig; + return this; + } } diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SpectraMeasurementServiceImpl.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SpectraMeasurementServiceImpl.java index e42193cb..9d1c5715 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SpectraMeasurementServiceImpl.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SpectraMeasurementServiceImpl.java @@ -14,6 +14,7 @@ */ package gov.llnl.gnem.apps.coda.calibration.service.impl; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -38,6 +39,7 @@ import gov.llnl.gnem.apps.coda.common.model.domain.Station; import gov.llnl.gnem.apps.coda.common.model.domain.SyntheticCoda; import gov.llnl.gnem.apps.coda.common.model.util.PICK_TYPES; +import gov.llnl.gnem.apps.coda.common.model.util.SPECTRA_TYPES; @Service @Transactional @@ -118,7 +120,7 @@ public List measureSpectra(List generatedSynt } @Override - public Spectra computeSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase) { + public Spectra computeReferenceSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase) { Spectra refSpectra = new Spectra(); ReferenceMwParameters refEvent = referenceEventRepo.findOneByEventId(eventId); if (refEvent != null) { @@ -128,12 +130,16 @@ public Spectra computeSpectraForEventId(String eventId, List freq } @Override - public Spectra getFitSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase) { - Spectra fitSpectra = new Spectra(); + public List getFitSpectraForEventId(String eventId, List frequencyBands, PICK_TYPES selectedPhase) { + List spectra = new ArrayList<>(); MeasuredMwParameters event = measuredEventRepo.findOneByEventId(eventId); if (event != null) { - fitSpectra = spectraCalc.computeFitSpectra(event, frequencyBands, selectedPhase); + spectra.add(spectraCalc.computeFitSpectra(event, frequencyBands, selectedPhase)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw1Max(), event.getApparentStress1Max(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ1)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw1Min(), event.getApparentStress1Min(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ1)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw2Max(), event.getApparentStress2Max(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ2)); + spectra.add(spectraCalc.computeSpecificSpectra(event.getMw2Min(), event.getApparentStress2Min(), frequencyBands, selectedPhase, SPECTRA_TYPES.UQ2)); } - return fitSpectra; + return spectra; } } diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SyntheticCodaGenerationServiceImpl.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SyntheticCodaGenerationServiceImpl.java index 5d431cac..bd155d05 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SyntheticCodaGenerationServiceImpl.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/SyntheticCodaGenerationServiceImpl.java @@ -74,9 +74,6 @@ private SyntheticCoda createSyntheticFromWaveform(Waveform sourceWaveform, Share double vr = syntheticCodaModel.getDistanceFunction(model.getVelocity0(), model.getVelocity1(), model.getVelocity2(), distance); double gr = syntheticCodaModel.getDistanceFunction(model.getGamma0(), model.getGamma1(), model.getGamma2(), distance); - // setting dt = 1.0 - not the SACfile's header.delta - double dt = 1.; - // note distance/vr is a singularity point - start at t = dt TimeT eventTime = new TimeT(event.getOriginTime()); TimeT codastart = eventTime; @@ -93,7 +90,9 @@ private SyntheticCoda createSyntheticFromWaveform(Waveform sourceWaveform, Share try { seis.cut(codastart, endTime); - seis.interpolate(1 / dt); + if (seis.getSamprate() > 1.0) { + seis.interpolate(1.0); + } int npts = seis.getNsamp(); @@ -103,7 +102,7 @@ private SyntheticCoda createSyntheticFromWaveform(Waveform sourceWaveform, Share for (int ii = 0; ii < Ac.length; ii++) { // t is relative to the phase start time - note t=0 is a // singularity point - start at t = dt - double t = (ii + 1) * dt; + double t = (ii + 1.0) / seis.getSamprate(); Ac[ii] = syntheticCodaModel.getSyntheticPointAtTime(gr, br, t); } diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java index 15aabb31..c8c3c892 100755 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CalibrationCurveFitter.java @@ -41,9 +41,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import gov.llnl.gnem.apps.coda.calibration.model.domain.ShapeFitterConstraints; import gov.llnl.gnem.apps.coda.calibration.model.domain.EnvelopeFit; import gov.llnl.gnem.apps.coda.calibration.model.domain.PeakVelocityMeasurement; +import gov.llnl.gnem.apps.coda.calibration.model.domain.ShapeFitterConstraints; import gov.llnl.gnem.apps.coda.calibration.model.domain.ShapeMeasurement; import gov.llnl.gnem.apps.coda.common.model.domain.FrequencyBand; import gov.llnl.gnem.apps.coda.common.model.domain.SharedFrequencyBandParameters; @@ -55,7 +55,7 @@ public class CalibrationCurveFitter { private Logger log = LoggerFactory.getLogger(CalibrationCurveFitter.class); - public EnvelopeFit fitCodaCMAES(final float[] segment, ShapeFitterConstraints constraints) { + public EnvelopeFit fitCodaCMAES(final float[] segment, final double sampleRate, ShapeFitterConstraints constraints) { double minInt = constraints.getMinIntercept(); double maxInt = constraints.getMaxIntercept(); double minGamma = constraints.getMinGamma(); @@ -67,7 +67,7 @@ public EnvelopeFit fitCodaCMAES(final float[] segment, ShapeFitterConstraints co SimpleRegression regression = new SimpleRegression(); for (int j = 0; j < segment.length; j++) { - regression.addData(j + 1, segment[j]); + regression.addData((j / sampleRate) + 1, segment[j]); } double startIntercept = regression.getIntercept(); double startBeta = regression.getSlope(); @@ -78,7 +78,7 @@ public EnvelopeFit fitCodaCMAES(final float[] segment, ShapeFitterConstraints co double beta = point[2]; double sum = 0.0; for (int j = 0; j < segment.length; j++) { - double t = j + 1.0; + double t = (j / sampleRate) + 1.0; double actual = segment[j]; double predicted = intercept - (gamma * Math.log10(t)) + (beta * t); sum = lossFunction(sum, predicted, actual); @@ -91,14 +91,16 @@ public EnvelopeFit fitCodaCMAES(final float[] segment, ShapeFitterConstraints co if (Double.isNaN(startIntercept)) { startIntercept = ThreadLocalRandom.current().nextDouble(minInt, maxInt); startBeta = minBeta; - } else if (startBeta > maxBeta || startBeta < minBeta) { + } else if (startBeta > maxBeta) { + startBeta = maxBeta; + } else if (startBeta < minBeta) { startBeta = minBeta; } PointValuePair bestResult = optimizeCMAES( prediction, new InitialGuess(new double[] { startIntercept, minGamma, startBeta }), - new CMAESOptimizer.Sigma(new double[] { 0.5, 0.05, 0.05 }), + new CMAESOptimizer.Sigma(new double[] { (maxInt - minInt) / 2.0, (maxGamma - minGamma) / 2.0, (maxBeta - minBeta) / 2.0 }), convergenceChecker, 50, new SimpleBounds(new double[] { -Double.MAX_VALUE, minGamma, minBeta }, new double[] { Double.MAX_VALUE, maxGamma, maxBeta })); @@ -107,7 +109,7 @@ public EnvelopeFit fitCodaCMAES(final float[] segment, ShapeFitterConstraints co fit.setIntercept(curve[0]); fit.setGamma(curve[1]); fit.setBeta(curve[2]); - fit.setError(bestResult.getValue()); + fit.setError(bestResult.getValue() / segment.length); return fit; } diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CodaSNREndTimePicker.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CodaSNREndTimePicker.java index 3afb653e..fbbede94 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CodaSNREndTimePicker.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/CodaSNREndTimePicker.java @@ -46,8 +46,8 @@ public double getEndTime(float[] waveform, double sampleRate, double startTimeEp private Double getSnrEndPick(final float[] waveform, final double sampleRate, int startOffset, final double minLengthSec, final double maxLengthSec, final double minimumSnr, final double noise, final int windowSize) { - int obsWindow = (int) (windowSize * sampleRate); - int spikeSamples = (int) ((windowSize / 4) * sampleRate); + int obsWindow = (int) (windowSize / sampleRate); + int spikeSamples = (int) ((windowSize / 4) / sampleRate); DescriptiveStatistics obs = new DescriptiveStatistics(obsWindow); DescriptiveStatistics spike = new DescriptiveStatistics(spikeSamples); SimpleRegression spikeReg = new SimpleRegression(); diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MaxVelocityCalculator.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MaxVelocityCalculator.java index d9e8051b..c4fe5a6e 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MaxVelocityCalculator.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MaxVelocityCalculator.java @@ -47,11 +47,11 @@ public MaxVelocityCalculator(VelocityConfiguration velConf, WaveformToTimeSeries public Stream computeMaximumVelocity(List waveforms) { return computeMaximumVelocity( waveforms, - velConf.getGroupVelocity1InKmsGtDistance(), - velConf.getGroupVelocity2InKmsGtDistance(), - velConf.getGroupVelocity1InKmsLtDistance(), - velConf.getGroupVelocity2InKmsLtDistance(), - velConf.getDistanceThresholdInKm()); + velConf.getGroupVelocity1InKmsGtDistance(), + velConf.getGroupVelocity2InKmsGtDistance(), + velConf.getGroupVelocity1InKmsLtDistance(), + velConf.getGroupVelocity2InKmsLtDistance(), + velConf.getDistanceThresholdInKm()); } public Stream computeMaximumVelocity(List waveforms, VelocityConfiguration velocityConfiguration) { @@ -94,11 +94,6 @@ private Stream computeMaximumVelocity(List wa // cut the coda window portion of the seismograms waveform.cut(starttime, endtime); - double min_length = 25.; - if (endtime.subtract(starttime).getEpochTime() < min_length) { - log.debug("Coda window length too short: {}", endtime.subtract(starttime).getEpochTime()); - } - // peakS[0] time in seconds for // reference peakS[1] max amplitude double[] peakS = WaveformUtils.getMaxTime(waveform, origintime); diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculator.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculator.java index 26275ec7..1122a57a 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculator.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculator.java @@ -40,8 +40,8 @@ public class MdacCalculator { public static final double LOG10_OF_E = Math.log10(Math.E); public static final double DYNE_CM_TO_NEWTON_M = 1e-7; - public static final double MPA_TO_PA = 1.0e6; - public static final double PA_TO_MPA = 1e6; + public static final double MPA_TO_PA = 1e6; + public static final int ANGULAR_CORNER_FREQ_IDX = 3; // Event specific variables double sigmaA; // Apparent stress - derived from Sigma and M0 @@ -62,7 +62,7 @@ public class MdacCalculator { private double m0_ref; public MdacCalculator(double sigma, double M0ref, double Psi, double Zeta, double AlphaS, double BetaS, double RadpatP, double RadpatS, double M0) { - sigmaA = getApparentStress(PA_TO_MPA * sigma, M0, M0ref, Psi); + sigmaA = getApparentStress(MPA_TO_PA * sigma, M0, M0ref, Psi); wcvels = getCornerFrequencies(Zeta, AlphaS, BetaS, RadpatP, RadpatS, sigmaA, M0); this.zeta = Zeta; this.alphaS = AlphaS; @@ -94,7 +94,7 @@ public double[] calculateMdacSourceSpectra(double freq, double M0) { double data = M0 / wwc; double veld = data * w; - return new double[] { logAmp, data, veld }; + return new double[] { logAmp, data, veld, wc }; } /** @@ -106,7 +106,8 @@ public double[] calculateMdacSourceSpectra(double freq, double M0) { * @param frequency * the desired frequency in Hz * @param m0 - * the seismic Moment in (?) * @param sigma + * the seismic Moment in (?) + * @param sigma * @param psi * @param phase * {@link PICK_TYPES} phase to calculate the spectra for @@ -200,7 +201,7 @@ private double calculateMomentRateSpectra(double frequency, double wcorner, doub * of the source region velocity, AlphaS or BetaS from getCornerFrequencies. * wc - the corner frequency wcp or wcs (see getCornerFrequencies). */ - public void initializePhaseSpecificVariables(double distance, MdacParametersPS mdacPs, MdacParametersFI mdacFi, double M0) { + public void initializePhaseSpecificVariables(MdacParametersPS mdacPs, MdacParametersFI mdacFi, double M0) { String phase = mdacPs.getPhase(); if (PICK_TYPES.PN.getPhase().equals(phase) || PICK_TYPES.PG.getPhase().equals(phase)) { @@ -227,7 +228,7 @@ public void initializePhaseSpecificVariables(double distance, MdacParametersPS m * power of the source region velocity (cs5) and the receiver region * velocity (cr) */ - double getF(double radiationpattern, double rhos, double rhor, double cs5, double cr) { + private double getF(double radiationpattern, double rhos, double rhor, double cs5, double cr) { // from Aki and Richards (1980) // use this with getS0 --> S0 = F * M0 return radiationpattern / (4.0 * Math.PI * Math.pow(rhos * rhor * cs5 * cr, 0.5)); @@ -238,7 +239,7 @@ public void initializePhaseSpecificVariables(double distance, MdacParametersPS m * given the Moment (M0) and the source moment - corner frequency scaling * parameter (F) obtained from the getF() routine. */ - double getLogS0(double F, double M0) { + private double getLogS0(double F, double M0) { double S0 = F * M0; return Math.log10(S0); } @@ -248,7 +249,7 @@ public void initializePhaseSpecificVariables(double distance, MdacParametersPS m * source-receiver distance and the critical distance for the specific phase * note units of distance, distcrit are meters. */ - double getLogGeometricalSpreading(double distance, double distcrit, double eta) { + private double getLogGeometricalSpreading(double distance, double distcrit, double eta) { double Gr; if (distance < distcrit) { Gr = 1. / distance; @@ -262,7 +263,7 @@ public void initializePhaseSpecificVariables(double distance, MdacParametersPS m * Returns the apparent stress given the reference stress (sigma) and * Moment(M0) at reference moment (M0ref) and with scaling exponent Psi */ - double getApparentStress(double Sigma, double M0, double M0ref, double Psi) { + private double getApparentStress(double Sigma, double M0, double M0ref, double Psi) { double ratio = M0 / M0ref; return Sigma * Math.pow(ratio, Psi); } @@ -272,7 +273,7 @@ public void initializePhaseSpecificVariables(double distance, MdacParametersPS m * given the s-r distance, attenuation at 1-Hz (Q0) and phase velocity (U0) * this is used to obtain a Q(f) term in the MDAC calculation */ - double getLogQfi(double distance, double Q0, double U0) { + private double getLogQfi(double distance, double Q0, double U0) { // See equation A6 (Rodgers and Walter, 2002) // Q(f) = exp( -pi* f^(1-gamma) * distance / U0 *Q0) return (distance * Math.PI * LOG10_OF_E) / (Q0 * U0); @@ -283,7 +284,7 @@ public void initializePhaseSpecificVariables(double distance, MdacParametersPS m * returns 4-element array containing the result {{wcp, alphas^5},{wcs, * betas^5}} */ - double[][] getCornerFrequencies(double zeta, double alphas, double betas, double radpatp, double radpats, double sigmaA, double M0) { + private double[][] getCornerFrequencies(double zeta, double alphas, double betas, double radpatp, double radpats, double sigmaA, double M0) { double z3 = Math.pow(zeta, 3); double a5 = Math.pow(alphas, 5); double b5 = Math.pow(betas, 5); @@ -305,4 +306,12 @@ public static double mwToM0(double Mw) { public static double mwInDyne(double testMw) { return (DYNE_CM_TO_NEWTON_M * Math.pow(10, 1.5 * (testMw + 10.73))) / DYNE_CM_TO_NEWTON_M; } + + public double apparentStressFromMwFc(Double mw, Double fc) { + double K = calculateK(zeta, alphaS, betaS, radpatP, radpatS); + double M0 = mwToM0(mw); + double wfc3 = Math.pow(Math.PI * 2.0 * fc, 3.0); + double appStress = ((wfc3 * M0) / K) / MPA_TO_PA; + return appStress; + } } diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculatorService.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculatorService.java index 5ec59d98..26f78257 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculatorService.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/MdacCalculatorService.java @@ -14,6 +14,8 @@ */ package gov.llnl.gnem.apps.coda.calibration.service.impl.processing; +import java.util.function.Function; + import org.springframework.stereotype.Service; import gov.llnl.gnem.apps.coda.calibration.model.domain.MdacParametersFI; @@ -31,16 +33,14 @@ public class MdacCalculatorService { * Phase specific parameters for the MDAC2 model * @param fiEntry * Independent parameters for the MDAC2 model - * @param frequency - * Frequency to compute the MDAC2 source spectra for * @param Mw * Magnitude of expected event * @param distance * In kilometers. May be set to < 0, in which case it is not used * in computing the source spectra - * @return logAmp, M0/wwc, w*M0/wwc + * @return Function that computes logAmp, M0/wwc, w*M0/wwc given a frequency */ - public double[] calculateMdacSourceSpectra(MdacParametersPS psEntry, MdacParametersFI fiEntry, double frequency, double Mw, double distanceInKm) { + public Function getCalculateMdacSourceSpectraFunction(MdacParametersPS psEntry, MdacParametersFI fiEntry, double Mw) { Double M0 = getM0(Mw); MdacCalculator mdc = new MdacCalculator(fiEntry.getSigma(), fiEntry.getM0ref(), @@ -51,8 +51,8 @@ public double[] calculateMdacSourceSpectra(MdacParametersPS psEntry, MdacParamet fiEntry.getRadPatP(), fiEntry.getRadPatS(), M0); - mdc.initializePhaseSpecificVariables(distanceInKm, psEntry, fiEntry, M0); - return mdc.calculateMdacSourceSpectra(frequency, M0); + mdc.initializePhaseSpecificVariables(psEntry, fiEntry, M0); + return frequency -> mdc.calculateMdacSourceSpectra(frequency, M0); } /** @@ -60,24 +60,20 @@ public double[] calculateMdacSourceSpectra(MdacParametersPS psEntry, MdacParamet * Phase specific parameters for the MDAC2 model * @param fiEntry * Independent parameters for the MDAC2 model - * @param frequency - * Frequency to compute the MDAC2 source spectra for * @param Mw * Magnitude of expected event * @param phase * The phase to use for the MDAC calculation. Should be one of * the phases in {@link PICK_TYPES}. - * @param stress + * @param sigma * Apparent stress in MPA to use. May be null to use the MDAC * parameters instead. Otherwise Psi=0 and Sigma=stress - * @return logAmp In Dyne-CM + * @return Function that computes logAmp In Dyne-CM given a frequency */ - public double calculateMdacAmplitudeForMw(MdacParametersPS psEntry, MdacParametersFI fiEntry, double Mw, double frequency, PICK_TYPES phase, Double stress) { + public Function getCalculateMdacAmplitudeForMwFunction(MdacParametersPS psEntry, MdacParametersFI fiEntry, double Mw, PICK_TYPES phase, Double sigma) { // M0 in N-m units double M0 = MdacCalculator.DYNE_CM_TO_NEWTON_M * Math.pow(10, 1.5 * (Mw + 10.73)); - - double mdacM0 = Double.NEGATIVE_INFINITY; - double distance = -1; + Function mdacFunction; MdacCalculator mdc = new MdacCalculator(fiEntry.getSigma(), fiEntry.getM0ref(), @@ -88,16 +84,15 @@ public double calculateMdacAmplitudeForMw(MdacParametersPS psEntry, MdacParamete fiEntry.getRadPatP(), fiEntry.getRadPatS(), M0); - mdc.initializePhaseSpecificVariables(distance, psEntry, fiEntry, M0); + mdc.initializePhaseSpecificVariables(psEntry, fiEntry, M0); - //FIXME: Use the calibrations phase for this! - if (stress != null) { - mdacM0 = mdc.calculateMomentRateSpectra(frequency, M0, stress, 0.0, phase); + if (sigma != null) { + mdacFunction = frequency -> mdc.calculateMomentRateSpectra(frequency, M0, sigma, 0.0, phase); } else { - mdacM0 = mdc.calculateMomentRateSpectra(frequency, M0, fiEntry.getSigma(), fiEntry.getPsi(), phase); + mdacFunction = frequency -> mdc.calculateMomentRateSpectra(frequency, M0, fiEntry.getSigma(), fiEntry.getPsi(), phase); } - return Math.log10(mdacM0) + 7; + return frequency -> Math.log10(mdacFunction.apply(frequency)) + 7; } /** @@ -105,17 +100,15 @@ public double calculateMdacAmplitudeForMw(MdacParametersPS psEntry, MdacParamete * Phase specific parameters for the MDAC2 model * @param fiEntry * Independent parameters for the MDAC2 model - * @param frequency - * Frequency to compute the MDAC2 source spectra for * @param Mw * Magnitude of expected event * @param phase * The phase to use for the MDAC calculation. Should be one of * the phases in {@link PICK_TYPES}. - * @return logAmp In Dyne-CM + * @return Function that computes logAmp In Dyne-CM given a frequency */ - public double calculateMdacAmplitudeForMw(MdacParametersPS psRows, MdacParametersFI mdacFiEntry, double refMw, double centerFreq, PICK_TYPES phase) { - return calculateMdacAmplitudeForMw(psRows, mdacFiEntry, refMw, centerFreq, phase, null); + public Function getCalculateMdacAmplitudeForMwFunction(MdacParametersPS psRows, MdacParametersFI mdacFiEntry, double refMw, PICK_TYPES phase) { + return getCalculateMdacAmplitudeForMwFunction(psRows, mdacFiEntry, refMw, phase, null); } /** @@ -132,4 +125,8 @@ public double getM0(double Mw) { public double getMwInDyne(double testMw) { return MdacCalculator.mwInDyne(testMw); } + + public double getCornerFrequency(Function mdacFunc) { + return mdacFunc.apply(Double.valueOf(1.0))[MdacCalculator.ANGULAR_CORNER_FREQ_IDX] / (Math.PI * 2.0); + } } diff --git a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/ShapeCalculator.java b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/ShapeCalculator.java index 92eeadf3..86884aac 100644 --- a/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/ShapeCalculator.java +++ b/calibration-service/calibration-service-impl/src/main/java/gov/llnl/gnem/apps/coda/calibration/service/impl/processing/ShapeCalculator.java @@ -27,9 +27,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import gov.llnl.gnem.apps.coda.calibration.model.domain.ShapeFitterConstraints; import gov.llnl.gnem.apps.coda.calibration.model.domain.EnvelopeFit; import gov.llnl.gnem.apps.coda.calibration.model.domain.PeakVelocityMeasurement; +import gov.llnl.gnem.apps.coda.calibration.model.domain.ShapeFitterConstraints; import gov.llnl.gnem.apps.coda.calibration.model.domain.ShapeMeasurement; import gov.llnl.gnem.apps.coda.common.model.domain.FrequencyBand; import gov.llnl.gnem.apps.coda.common.model.domain.SharedFrequencyBandParameters; @@ -107,8 +107,14 @@ public List fitShapelineToMeasuredEnvelopes(Collection 1.0) { + synthSeis.interpolate(1.0); + } + if (constraints != null && constraints.getFittingPointCount() > 0 && synthSeis.getNsamp() > constraints.getFittingPointCount()) { + double samprate = (constraints.getFittingPointCount() / (double) synthSeis.getNsamp()) * synthSeis.getSamprate(); + synthSeis.interpolate(samprate); + } if (frequencyBandParameter.getMinLength() > 0 && synthSeis.getLengthInSeconds() < frequencyBandParameter.getMinLength()) { log.trace( @@ -126,7 +132,7 @@ public List fitShapelineToMeasuredEnvelopes(Collection> mwFittingComparator = (o1, o2) -> { + int compare = Double.compare(o1.getLeft(), o2.getLeft()); + if (compare == 0) { + compare = Double.compare(o1.getMiddle(), o2.getMiddle()); + if (compare == 0) { + compare = Double.compare(o2.getRight(), o1.getRight()); + } + } + return compare; + }; @Autowired public SpectraCalculator(WaveformToTimeSeriesConverter converter, SyntheticCodaModel syntheticCodaModel, MdacCalculatorService mdacService, MdacParametersFiService mdacFiService, @@ -287,10 +319,8 @@ private boolean cutSeismograms(TimeSeries a, TimeSeries b, TimeT startTime, Time b.cut(startTime, endTime); // These might be off by some small number of samples due to - // precision - // errors during cut and SeriesMath will throw an ArrayBounds if - // they - // don't match exactly so we need to double check! + // precision errors during cut and SeriesMath will throw an ArrayBounds if + // they don't match exactly so we need to double check! if (a.getNsamp() != b.getNsamp()) { TimeT start = a.getTime(); @@ -301,8 +331,7 @@ private boolean cutSeismograms(TimeSeries a, TimeSeries b, TimeT startTime, Time TimeT endB = b.getEndtime(); // choose the latest start time and the earliest end time for - // the - // cut window + // the cut window if (startA.lt(startB)) { start = startB; } @@ -405,50 +434,32 @@ public double log10ESHcorrection(double s1, double s2, double xcross, double xtr } public Spectra computeReferenceSpectra(ReferenceMwParameters refEvent, List bands, PICK_TYPES selectedPhase) { + return computeSpecificSpectra(refEvent.getRefMw(), refEvent.getRefApparentStressInMpa(), bands, selectedPhase, SPECTRA_TYPES.REF); + } + + public Spectra computeFitSpectra(MeasuredMwParameters event, Collection bands, PICK_TYPES selectedPhase) { + return computeSpecificSpectra(event.getMw(), event.getApparentStressInMpa(), bands, selectedPhase, SPECTRA_TYPES.FIT); + } + + public Spectra computeSpecificSpectra(Double mw, Double apparentStress, Collection bands, PICK_TYPES selectedPhase, SPECTRA_TYPES type) { MdacParametersFI mdacFiEntry = mdacFiService.findFirst(); MdacParametersPS psRows = mdacPsService.findMatchingPhase(selectedPhase.getPhase()); List xyPoints = new ArrayList<>(); - for (FrequencyBand band : bands) { - double centerFreq = band.getLowFrequency() + (band.getHighFrequency() - band.getLowFrequency()) / 2.; - double logFreq = Math.log10(centerFreq); - if (refEvent.getRefApparentStressInMpa() != null && refEvent.getRefApparentStressInMpa() > 0.0) { - // If we know an apparent stress in MPA for this reference event we want - // to use that stress so we set Psi == 0.0 to use Sigma - mdacFiEntry.setSigma(refEvent.getRefApparentStressInMpa()); - mdacFiEntry.setPsi(0.0); - } - - double amplitude = mdacService.calculateMdacAmplitudeForMw(psRows, mdacFiEntry, refEvent.getRefMw(), centerFreq, selectedPhase); - - if (amplitude > 0) { - Point2D.Double point = new Point2D.Double(logFreq, amplitude); - xyPoints.add(point); - } + if (apparentStress != null && apparentStress > 0.0) { + mdacFiEntry.setSigma(apparentStress); + mdacFiEntry.setPsi(0.0); } - Collections.sort(xyPoints, (p1, p2) -> Double.compare(p1.getX(), p2.getX())); - return new Spectra(SPECTRA_TYPES.REF, xyPoints, refEvent.getRefMw(), refEvent.getRefApparentStressInMpa()); - } + Function mdacFunction = mdacService.getCalculateMdacAmplitudeForMwFunction(psRows, mdacFiEntry, mw, selectedPhase); - public Spectra computeFitSpectra(MeasuredMwParameters event, Collection bands, PICK_TYPES selectedPhase) { - - MdacParametersFI mdacFiEntry = mdacFiService.findFirst(); - MdacParametersPS psRows = mdacPsService.findMatchingPhase(selectedPhase.getPhase()); - - List xyPoints = new ArrayList<>(); for (FrequencyBand band : bands) { double centerFreq = band.getLowFrequency() + (band.getHighFrequency() - band.getLowFrequency()) / 2.; double logFreq = Math.log10(centerFreq); - if (event.getApparentStressInMpa() != null && event.getApparentStressInMpa() > 0.0) { - mdacFiEntry.setSigma(event.getApparentStressInMpa()); - mdacFiEntry.setPsi(0.0); - } - - double amplitude = mdacService.calculateMdacAmplitudeForMw(psRows, mdacFiEntry, event.getMw(), centerFreq, selectedPhase); + double amplitude = mdacFunction.apply(centerFreq); if (amplitude > 0) { Point2D.Double point = new Point2D.Double(logFreq, amplitude); @@ -456,7 +467,7 @@ public Spectra computeFitSpectra(MeasuredMwParameters event, Collection Double.compare(p1.getX(), p2.getX())); - return new Spectra(SPECTRA_TYPES.FIT, xyPoints, event.getMw(), event.getApparentStressInMpa()); + return new Spectra(type, xyPoints, mw, apparentStress); } /** @@ -467,28 +478,36 @@ public Spectra computeFitSpectra(MeasuredMwParameters event, Collection measureMws(final Map> evidMap, Map, SortedMap>> eventWeights, final PICK_TYPES selectedPhase, MdacParametersPS mdacPs, MdacParametersFI mdacFi) { - List measuredMws = new ArrayList<>(evidMap.entrySet().size()); - for (Entry> entry : evidMap.entrySet()) { + return evidMap.entrySet().parallelStream().map(entry -> { Map measurements = entry.getValue(); double[] MoMw = fitMw(entry.getKey(), measurements, selectedPhase, mdacFi, mdacPs, eventWeights.get(entry.getKey())); if (MoMw == null) { log.warn("MoMw calculation returned null value"); - continue; + return null; } - measuredMws.add( - new MeasuredMwParameters().setEventId(entry.getKey().getEventId()) - .setDataCount((int) MoMw[DATA_COUNT]) - .setMw(MoMw[MW_FIT]) - .setMeanMw(MoMw[MW_MEAN]) - .setMwSd(MoMw[MW_SD]) - .setApparentStressInMpa(MoMw[STRESS]) - .setMeanApparentStressInMpa(MoMw[MPA_MEAN]) - .setStressSd(MoMw[MPA_SD]) - .setMisfit(MoMw[RMS_FIT]) - .setMeanMisfit(MoMw[FIT_MEAN]) - .setMisfitSd(MoMw[FIT_SD])); - } - return measuredMws; + return new MeasuredMwParameters().setEventId(entry.getKey().getEventId()) + .setDataCount((int) MoMw[DATA_COUNT]) + .setMw(MoMw[MW_FIT]) + .setMeanMw(MoMw[MW_MEAN]) + .setMwSd(MoMw[MW_SD]) + .setMw1Min(MoMw[MW_1_MIN]) + .setMw1Max(MoMw[MW_1_MAX]) + .setMw2Min(MoMw[MW_2_MIN]) + .setMw2Max(MoMw[MW_2_MAX]) + .setApparentStressInMpa(MoMw[APP_STRESS]) + .setMeanApparentStressInMpa(MoMw[APP_STRESS_MEAN]) + .setApparentStressSd(MoMw[APP_STRESS_SD]) + .setApparentStress1Min(MoMw[APP_1_MIN]) + .setApparentStress1Max(MoMw[APP_1_MAX]) + .setApparentStress2Min(MoMw[APP_2_MIN]) + .setApparentStress2Max(MoMw[APP_2_MAX]) + .setMisfit(MoMw[RMS_FIT]) + .setMeanMisfit(MoMw[FIT_MEAN]) + .setMisfitSd(MoMw[FIT_SD]) + .setCornerFrequency(MoMw[CORNER_FREQ]) + .setCornerFrequencySd(MoMw[CORNER_FREQ_SD]) + .setIterations((int) MoMw[ITR_COUNT]); + }).filter(Objects::nonNull).collect(Collectors.toList()); } /** @@ -507,9 +526,10 @@ public List measureMws(final Map measurements, final PICK_TYPES phase, final MdacParametersFI mdacFi, final MdacParametersPS mdacPs, Function, SortedMap> weightFunction) { - double[] result = new double[11]; + double[] result = new double[PARAM_COUNT]; final SortedMap frequencyBands = new TreeMap<>(); + final SortedSet> optimizerMeasurements = Collections.synchronizedSortedSet(new TreeSet<>(mwFittingComparator)); long dataCount = 0l; for (Entry meas : measurements.entrySet()) { @@ -524,12 +544,7 @@ public double[] fitMw(Event event, final Map m } final Map weightMap = weightFunction.apply(frequencyBands); - //TODO: Thinking about ways to flatten spectra fit for missing low frequencies. - //TODO: Devise uncertainty quantification on site fit. Distribution of misfit vs mws evaluated maybe? - // Would need to add the extremes for the mw/mpa range to the misfit dist to get a good estimate of the range. - // Misfit is log normal probably; should check. - - final SynchronizedMultivariateSummaryStatistics stats = new SynchronizedMultivariateSummaryStatistics(3, false); + final SynchronizedMultivariateSummaryStatistics stats = new SynchronizedMultivariateSummaryStatistics(4, false); MultivariateFunction mdacFunction = new MultivariateFunction() { @Override @@ -537,7 +552,9 @@ public double value(double[] point) { double testMw = point[0]; double testSigma = point[1]; - HashMap dataMap = new HashMap<>(); + Map dataMap = new HashMap<>(); + + Function mdacFunc = mdacService.getCalculateMdacAmplitudeForMwFunction(mdacPs, mdacFi, testMw, phase, testSigma); for (Entry meas : measurements.entrySet()) { double logAmplitude = meas.getValue().getMean(); @@ -545,19 +562,19 @@ public double value(double[] point) { double highFreq = meas.getKey().getHighFrequency(); double centerFreq = (highFreq + lowFreq) / 2.0; - if (mdacPs != null) { - // Note this is in dyne-cm to match Kevin - double log10mdacM0 = mdacService.calculateMdacAmplitudeForMw(mdacPs, mdacFi, testMw, centerFreq, phase, testSigma); + // Note this is in dyne-cm to match Kevin + double log10mdacM0 = mdacFunc.apply(centerFreq); - if (logAmplitude > 0.) { - double[] coda_vs_mdac = new double[] { logAmplitude, log10mdacM0 }; - dataMap.put(centerFreq, coda_vs_mdac); - } + if (logAmplitude > 0.) { + double[] coda_vs_mdac = new double[] { logAmplitude, log10mdacM0 }; + dataMap.put(centerFreq, coda_vs_mdac); } } double fit = WCVRMSD(weightMap, dataMap); - stats.addValue(new double[] { testMw, testSigma, fit }); + double corner = mdacService.getCornerFrequency(mdacService.getCalculateMdacSourceSpectraFunction(mdacPs, mdacFi.setPsi(0.0).setSigma(testSigma), testMw)); + stats.addValue(new double[] { testMw, testSigma, fit, corner }); + optimizerMeasurements.add(new ImmutableTriple<>(fit, testMw, testSigma)); return fit; } }; @@ -566,17 +583,10 @@ public double value(double[] point) { CMAESOptimizer optimizer = new CMAESOptimizer(1000000, 0, true, 0, 10, new MersenneTwister(), false, convergenceChecker); int iterations = iterationCutoff; try { - PointValuePair optimizerResult = optimizer.optimize( - new MaxEval(1000000), - new ObjectiveFunction(mdacFunction), - GoalType.MINIMIZE, - new SimpleBounds(new double[] { minMW, minMPA }, new double[] { maxMW, maxMPA }), - new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minMW, maxMW), ThreadLocalRandom.current().nextDouble(minMPA, maxMPA) }), - new CMAESOptimizer.Sigma(new double[] { 0.5, 1.0 }), - new CMAESOptimizer.PopulationSize(50)); + PointValuePair optimizerResult = runOptimizer(mdacFunction, optimizer); // converted back into dyne-cm to match Kevin's format - double testMw = optimizerResult.getPoint()[0]; + double testMw = optimizerResult.getPoint()[MW]; // log10M0 result[LOG10_M0] = Math.log10(mdacService.getMwInDyne(testMw)); result[MW_FIT] = testMw; // best Mw fit @@ -585,50 +595,125 @@ public double value(double[] point) { // this is the rmsfit measurement result[RMS_FIT] = optimizerResult.getValue(); // this is the stress - result[STRESS] = optimizerResult.getPoint()[1]; + result[APP_STRESS] = optimizerResult.getPoint()[MPA]; iterations = optimizer.getIterations(); } catch (TooManyEvaluationsException | TooManyIterationsException e) { log.warn("Failed to converge while attempting to fit an Mw to this event {}, falling back to a grid search.", event); } if (iterations >= iterationCutoff) { - double best = Double.MAX_VALUE; - double mwVal = 0; - double stressVal = 0; + double best = result[RMS_FIT] != 0.0 ? result[RMS_FIT] : Double.MAX_VALUE; for (double mw = minMW; mw < maxMW; mw = mw + ((maxMW - minMW) / 100.)) { - for (double stress = minMPA; stress < maxMPA; stress = stress + ((maxMPA - minMPA) / 100.)) { + for (double stress = minApparentStress; stress < maxApparentStress; stress = stress + ((maxApparentStress - minApparentStress) / 100.)) { double res = mdacFunction.value(new double[] { mw, stress }); - stats.addValue(new double[] { mw, stress, res }); + double corner = mdacService.getCornerFrequency(mdacService.getCalculateMdacSourceSpectraFunction(mdacPs, mdacFi.setPsi(0.0).setSigma(stress), mw)); + stats.addValue(new double[] { mw, stress, res, corner }); + optimizerMeasurements.add(new ImmutableTriple<>(res, mw, stress)); if (res < best) { best = res; - mwVal = mw; - stressVal = stress; + result[MW_FIT] = mw; + result[APP_STRESS] = stress; } + iterations++; } } - result[LOG10_M0] = Math.log10(mdacService.getMwInDyne(mwVal)); - result[MW_FIT] = mwVal; + result[LOG10_M0] = Math.log10(mdacService.getMwInDyne(result[MW_FIT])); result[RMS_FIT] = best; - result[STRESS] = stressVal; } RealMatrix C = stats.getCovariance(); - result[MW_MEAN] = stats.getMean()[MW]; + + double[] mean = stats.getMean(); + result[MW_MEAN] = mean[MW]; result[MW_SD] = Math.sqrt(C.getEntry(MW, MW)); - result[MPA_MEAN] = stats.getMean()[MPA]; - result[MPA_SD] = Math.sqrt(C.getEntry(MPA, MPA)); - result[FIT_MEAN] = stats.getMean()[FIT]; + result[APP_STRESS_MEAN] = mean[MPA]; + result[APP_STRESS_SD] = Math.sqrt(C.getEntry(MPA, MPA)); + result[FIT_MEAN] = mean[FIT]; result[FIT_SD] = Math.sqrt(C.getEntry(FIT, FIT)); + result[CORNER_FREQ_SD] = Math.sqrt(C.getEntry(CORNER, CORNER)); + + //This is kinda wonky mathmatically but at least it roughly scales with N so until I can get a stats person to eyeball this it'll have to do. + double SE = Math.sqrt(C.getEntry(FIT, FIT) / (stats.getN() - 2.0)); + double f1 = result[RMS_FIT] + SE; + double f2 = f1 + (2.0 * SE); + + double mw1min = Double.POSITIVE_INFINITY; + double mw1max = Double.NEGATIVE_INFINITY; + double mw2min = Double.POSITIVE_INFINITY; + double mw2max = Double.NEGATIVE_INFINITY; + + double as1min = Double.POSITIVE_INFINITY; + double as1max = Double.NEGATIVE_INFINITY; + double as2min = Double.POSITIVE_INFINITY; + double as2max = Double.NEGATIVE_INFINITY; + + for (Triple meas : optimizerMeasurements) { + Double fit = meas.getLeft(); + Double mw = meas.getMiddle(); + Double apparentStress = meas.getRight(); + + if (fit < f1) { + if (mw < mw1min) { + mw1min = mw; + as1min = apparentStress; + } + if (mw > mw1max) { + mw1max = mw; + as1max = apparentStress; + } + } + if (fit < f2) { + if (mw < mw2min) { + mw2min = mw; + as2min = apparentStress; + } + if (mw > mw2max) { + mw2max = mw; + as2max = apparentStress; + } + } else { + break; + } + } + + result[MW_1_MIN] = mw1min; + result[MW_1_MAX] = mw1max; + result[MW_2_MIN] = mw2min; + result[MW_2_MAX] = mw2max; + if (reportStressBoundsInUQ) { + result[APP_1_MIN] = as1min; + result[APP_1_MAX] = as1max; + result[APP_2_MIN] = as2min; + result[APP_2_MAX] = as2max; + } else { + result[APP_1_MIN] = result[APP_STRESS]; + result[APP_1_MAX] = result[APP_STRESS]; + result[APP_2_MIN] = result[APP_STRESS]; + result[APP_2_MAX] = result[APP_STRESS]; + } + result[CORNER_FREQ] = mdacService.getCornerFrequency(mdacService.getCalculateMdacSourceSpectraFunction(mdacPs, mdacFi.setPsi(0.0).setSigma(result[APP_STRESS]), result[MW_FIT])); + result[ITR_COUNT] = iterations; return result; } + private PointValuePair runOptimizer(MultivariateFunction mdacFunction, CMAESOptimizer optimizer) { + return optimizer.optimize( + new MaxEval(1000000), + new ObjectiveFunction(mdacFunction), + GoalType.MINIMIZE, + new SimpleBounds(new double[] { minMW, minApparentStress }, new double[] { maxMW, maxApparentStress }), + new InitialGuess(new double[] { ThreadLocalRandom.current().nextDouble(minMW, maxMW), ThreadLocalRandom.current().nextDouble(minApparentStress, maxApparentStress) }), + new CMAESOptimizer.Sigma(new double[] { 0.5, 1.0 }), + new CMAESOptimizer.PopulationSize(50)); + } + /** * @param weightMap * @param dataMap * @return */ - protected static double WCVRMSD(Map weightMap, HashMap dataMap) { + protected static double WCVRMSD(Map weightMap, Map dataMap) { for (Entry entry : dataMap.entrySet()) { if (weightMap.containsKey(entry.getKey())) { double[] val = entry.getValue(); @@ -640,4 +725,4 @@ protected static double WCVRMSD(Map weightMap, HashMap new double[] { 1.0, 1.0, 1.0, 1.0 }); + Mockito.when(mdac.getCalculateMdacAmplitudeForMwFunction(Mockito.any(), Mockito.any(), Mockito.anyDouble(), Mockito.any(), Mockito.anyDouble())).thenReturn(f -> Double.valueOf(1.0d)); SpectraCalculator spectraCalc = new SpectraCalculator(converter, syntheticCodaModel, mdac, mdacFiService, mdacPsService, velConf); siteCalibrationServiceImpl.setSpectraCalc(spectraCalc); + siteCalibrationServiceImpl.setServiceConfig(new ServiceConfig()); } @AfterEach diff --git a/calibration-service/pom.xml b/calibration-service/pom.xml index f4503963..8a9ab7a9 100644 --- a/calibration-service/pom.xml +++ b/calibration-service/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/calibration-standalone/pom.xml b/calibration-standalone/pom.xml index 61aa3c1e..5c4a923f 100644 --- a/calibration-standalone/pom.xml +++ b/calibration-standalone/pom.xml @@ -6,7 +6,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 4.0.0 @@ -79,7 +79,7 @@ - org.springframework.boot.loader.JarLauncher + org.springframework.boot.loader.PropertiesLauncher gov.llnl.gnem.apps.coda.calibration.standalone.CodaCalibrationStandalone @@ -145,10 +145,6 @@ spring-boot-configuration-processor true - - org.glassfish.tyrus.bundles - tyrus-standalone-client - org.apache.commons commons-compress diff --git a/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/CalibrationLocalClient.java b/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/CalibrationLocalClient.java index 6ccdb4ad..7a0efeba 100644 --- a/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/CalibrationLocalClient.java +++ b/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/CalibrationLocalClient.java @@ -51,7 +51,7 @@ public Mono runCalibration(Boolean autoPickingEnabled) { @Override public Mono makeMwMeasurements(Boolean autoPickingEnabled) { try { - return Mono.just(service.makeMwMeasurements(autoPickingEnabled).get(100l, TimeUnit.SECONDS).getResultPayload().get()); + return Mono.just(service.makeMwMeasurements(autoPickingEnabled, Boolean.TRUE).get(1l, TimeUnit.DAYS).getResultPayload().get()); } catch (InterruptedException | ExecutionException | TimeoutException e) { log.error(e.getMessage(), e); return Mono.empty(); @@ -61,7 +61,7 @@ public Mono makeMwMeasurements(Boolean autoPickingEnabl @Override public Mono makeMwMeasurements(Boolean autoPickingEnabled, List eventIds) { try { - return Mono.just(service.makeMwMeasurements(autoPickingEnabled, new HashSet<>(eventIds)).get(100l, TimeUnit.SECONDS).getResultPayload().get()); + return Mono.just(service.makeMwMeasurements(autoPickingEnabled, Boolean.TRUE, new HashSet<>(eventIds)).get(1l, TimeUnit.DAYS).getResultPayload().get()); } catch (InterruptedException | ExecutionException | TimeoutException e) { log.error(e.getMessage(), e); return Mono.empty(); diff --git a/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/SpectraLocalClient.java b/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/SpectraLocalClient.java index 7f8f3e15..166230b8 100644 --- a/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/SpectraLocalClient.java +++ b/calibration-standalone/src/main/java/gov/llnl/gnem/apps/coda/calibration/standalone/data/client/SpectraLocalClient.java @@ -14,6 +14,8 @@ */ package gov.llnl.gnem.apps.coda.calibration.standalone.data.client; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; @@ -54,13 +56,15 @@ public Flux getMeasuredSpectraMetadata() { @Override public Mono getReferenceSpectra(String eventId) { - return Mono.just(Optional.ofNullable(service.computeSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG)).orElseGet(() -> new Spectra())) + //FIXME: Use the calibrations phase for this! + return Mono.just(Optional.ofNullable(service.computeReferenceSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG)).orElseGet(() -> new Spectra())) .onErrorReturn(new Spectra()); } @Override - public Mono getFitSpectra(String eventId) { - return Mono.just(Optional.ofNullable(service.getFitSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG)).orElseGet(() -> new Spectra())) - .onErrorReturn(new Spectra()); + public Mono> getFitSpectra(String eventId) { + //FIXME: Use the calibrations phase for this! + return Mono.just(Optional.ofNullable(service.getFitSpectraForEventId(eventId, sharedParamsService.getFrequencyBands(), PICK_TYPES.LG)).orElseGet(ArrayList::new)) + .onErrorReturn(new ArrayList<>()); } } diff --git a/calibration-standalone/src/main/resources/application.properties b/calibration-standalone/src/main/resources/application.properties index 077181dd..55026e39 100644 --- a/calibration-standalone/src/main/resources/application.properties +++ b/calibration-standalone/src/main/resources/application.properties @@ -32,4 +32,5 @@ app.height=1200 app.width=800 app.baseTitle=Coda Calibration logging.level.org.springframework.web.socket.*=off -logging.level.org.springframework.web.reactive.function.client.*=off \ No newline at end of file +logging.level.org.springframework.web.reactive.function.client.*=off +spring.codec.max-in-memory-size=-1 \ No newline at end of file diff --git a/common-gui/pom.xml b/common-gui/pom.xml index 57123635..616cb294 100644 --- a/common-gui/pom.xml +++ b/common-gui/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 gov.llnl.gnem.apps.coda.common diff --git a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java index c48c00d8..a0f47840 100644 --- a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java +++ b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacLoader.java @@ -139,7 +139,11 @@ public Result convertSacFileToWaveform(File file) { channel = new Channel(chan); } catch (Exception ex) { return exceptionalResult( - new LightweightIllegalStateException(String.format("Error parsing (%s): SAC header variable KCMPNM (%s) does not meet FDSN requirements!", fileName, chan), ex)); + new LightweightIllegalStateException(String.format( + "Error parsing (%s): SAC header variable KCMPNM (%s) does not meet FDSN requirements! [%s]", + fileName, + chan, + ex.getLocalizedMessage()), ex)); } Date beginTime = Optional.ofNullable(header.getBeginTime().getMilliseconds()).map(time -> Date.from(Instant.ofEpochMilli(time))).orElse(null); @@ -352,7 +356,7 @@ public String getOrCreateEvid(Waveform waveform) { if (waveform.getEvent() != null && waveform.getEvent().getEventId() != null) { evid = waveform.getEvent().getEventId(); } - + if (evid == null || evid == "0") { evid = String.valueOf(createJDateMinuteResolutionFromEpoch(time)); } @@ -371,18 +375,17 @@ public String getOrCreateEvid(SACHeader header) { time = relTime.getEpochTime(); } } - + if (header.kevnm != null && header.kevnm.matches("[0-9]*")) { evid = header.kevnm.trim(); - } - else if (header.nevid > 0) { + } else if (header.nevid > 0) { evid = String.valueOf(header.nevid); } - + if (evid == null || evid == "0") { evid = String.valueOf(createJDateMinuteResolutionFromEpoch(time)); } - + return evid; } diff --git a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/CodaWebClientBuilder.java b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/CodaWebClientBuilder.java index 3fa2cc51..575439cc 100644 --- a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/CodaWebClientBuilder.java +++ b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/CodaWebClientBuilder.java @@ -186,6 +186,8 @@ public void close() { if (strategies != null) { builder.exchangeStrategies(strategies); } + // builder.exchangeStrategies(configurer -> configurer.codecs(codecs -> codecs.defaultCodecs().maxInMemorySize(-1))); + return builder.build(); } } \ No newline at end of file diff --git a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/EventBusStompSessionHandler.java b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/EventBusStompSessionHandler.java index 80c0a978..78bf4ac0 100644 --- a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/EventBusStompSessionHandler.java +++ b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/data/client/EventBusStompSessionHandler.java @@ -15,6 +15,7 @@ package gov.llnl.gnem.apps.coda.common.gui.data.client; import java.lang.reflect.Type; +import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +46,7 @@ public EventBusStompSessionHandler(EventBus bus) { @Override public Type getPayloadType(StompHeaders headers) { Type c; - if (headers.containsKey(MESSAGE_HEADERS.PAYLOAD_CLASSNAME)) { + if (headers.containsKey(MESSAGE_HEADERS.PAYLOAD_CLASSNAME) && headers.getFirst(MESSAGE_HEADERS.PAYLOAD_CLASSNAME).toLowerCase(Locale.ENGLISH).startsWith("gov.llnl.gnem.apps.coda")) { try { c = Class.forName(headers.getFirst(MESSAGE_HEADERS.PAYLOAD_CLASSNAME)); } catch (ReflectiveOperationException e) { diff --git a/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/util/SnapshotUtils.java b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/util/SnapshotUtils.java new file mode 100644 index 00000000..85ce5b1d --- /dev/null +++ b/common-gui/src/main/java/gov/llnl/gnem/apps/coda/common/gui/util/SnapshotUtils.java @@ -0,0 +1,86 @@ +/* +* Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.common.gui.util; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.time.Instant; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import javax.imageio.ImageIO; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gov.llnl.gnem.apps.coda.common.model.domain.Pair; +import javafx.embed.swing.SwingFXUtils; +import javafx.scene.Node; +import javafx.scene.SnapshotParameters; +import javafx.scene.image.Image; +import javafx.scene.transform.Transform; + +public class SnapshotUtils { + private static final Logger log = LoggerFactory.getLogger(SnapshotUtils.class); + + public static Image snapshot(final Node node) { + SnapshotParameters snapshotParams = new SnapshotParameters(); + snapshotParams.setTransform(Transform.scale(8.0, 8.0)); + return node.snapshot(snapshotParams, null); + } + + public static void writePng(BufferedImage image, String filename) { + try { + ImageIO.write(image, "png", new File(filename)); + log.trace("Wrote image to: {}", filename); + } catch (IOException ex) { + log.warn(ex.getMessage(), ex); + } catch (NullPointerException ex) { + log.warn("Null pointer writing image {} to file {} : {}", image, filename, ex.getMessage(), ex); + } + } + + public static void writePng(final Node node, String filename) { + Image snapshot = snapshot(node); + CompletableFuture.runAsync(() -> writePng(SwingFXUtils.fromFXImage(snapshot, null), filename)); + } + + public static void writePng(File folder, Pair nameAndNode) { + writePng(folder, Collections.singletonList(nameAndNode), null); + } + + public static void writePng(File folder, Pair nameAndNode, String id) { + writePng(folder, Collections.singletonList(nameAndNode), id); + } + + public static void writePng(File folder, List> namesAndNodes, String id) { + String timestamp; + if (id != null) { + timestamp = id; + } else { + timestamp = getTimestampWithLeadingSeparator(); + } + if (folder != null && namesAndNodes != null && !namesAndNodes.isEmpty()) { + namesAndNodes.stream().forEach(nameAndNode -> writePng(nameAndNode.getRight(), folder.getAbsolutePath() + File.separator + nameAndNode.getLeft() + timestamp + ".png")); + } + } + + public static String getTimestampWithLeadingSeparator() { + return "_" + Instant.now().toEpochMilli(); + } + +} diff --git a/common-gui/src/test/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporterTest.java b/common-gui/src/test/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporterTest.java index b3dd4919..a6b41992 100644 --- a/common-gui/src/test/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporterTest.java +++ b/common-gui/src/test/java/gov/llnl/gnem/apps/coda/common/gui/converters/sac/SacExporterTest.java @@ -66,7 +66,7 @@ private static Collection testParamSet() throws Exception { .setSampleRate(.0) .setBeginTime(Date.from(Instant.now())) .setEndTime(Date.from(Instant.now())) - .setStream(new Stream().setChannelName("STACK").setStation(new Station().setStationName("RNG"))) + .setStream(new Stream().setChannelName(Stream.TYPE_STACK).setStation(new Station().setStationName("RNG"))) .setEvent(new Event().setEventId("12345").setOriginTime(Date.from(Instant.now()))), "RNG_STACK_12345_0.0_0.0_VEL_.ENV", waveformValidAssertions() }); return params; diff --git a/common-service/common-application/pom.xml b/common-service/common-application/pom.xml index d3d7b9ae..7fa79079 100644 --- a/common-service/common-application/pom.xml +++ b/common-service/common-application/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/common-service/common-model/pom.xml b/common-service/common-model/pom.xml index af0cb62a..17aa43f2 100644 --- a/common-service/common-model/pom.xml +++ b/common-service/common-model/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/util/SPECTRA_TYPES.java b/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/util/SPECTRA_TYPES.java index 37e3f4b6..218e7293 100644 --- a/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/util/SPECTRA_TYPES.java +++ b/common-service/common-model/src/main/java/gov/llnl/gnem/apps/coda/common/model/util/SPECTRA_TYPES.java @@ -15,5 +15,5 @@ package gov.llnl.gnem.apps.coda.common.model.util; public enum SPECTRA_TYPES { - REF, FIT, UNK + REF, FIT, UQ1, UQ2, UNK } diff --git a/common-service/common-repository/pom.xml b/common-service/common-repository/pom.xml index c406cb88..f413fc12 100644 --- a/common-service/common-repository/pom.xml +++ b/common-service/common-repository/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/common-service/common-service-api/pom.xml b/common-service/common-service-api/pom.xml index c483e5f1..92e9f7c2 100644 --- a/common-service/common-service-api/pom.xml +++ b/common-service/common-service-api/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/common-service/common-service-impl/pom.xml b/common-service/common-service-impl/pom.xml index 8ac4ff4f..f900f582 100644 --- a/common-service/common-service-impl/pom.xml +++ b/common-service/common-service-impl/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.common common-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/common-service/pom.xml b/common-service/pom.xml index 4806014a..f1d7bccb 100644 --- a/common-service/pom.xml +++ b/common-service/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 gov.llnl.gnem.apps.coda.common diff --git a/envelope-gui/pom.xml b/envelope-gui/pom.xml index 6814c872..c1884e28 100644 --- a/envelope-gui/pom.xml +++ b/envelope-gui/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 envelope-gui diff --git a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/AppProperties.java b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/AppProperties.java index c642dd6b..a41f28b0 100644 --- a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/AppProperties.java +++ b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/AppProperties.java @@ -15,7 +15,9 @@ package gov.llnl.gnem.apps.coda.envelope.gui; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +@Component @ConfigurationProperties("envelope-app") public class AppProperties { diff --git a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/EnvelopeParamsController.java b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/EnvelopeParamsController.java index 6a6a81d2..00e1df8b 100644 --- a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/EnvelopeParamsController.java +++ b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/EnvelopeParamsController.java @@ -166,7 +166,6 @@ public void generateTable() { List oldBands = new ArrayList<>(bands); bands.clear(); bands.addAll(tableGenerator.generateTable(minFreq, maxFreq, overlap, spacing)); - config.setFrequencyBandConfiguration(bands); postJob().doOnError(er -> { diff --git a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java index 091fb69c..e898739c 100644 --- a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java +++ b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/controllers/WaveformLoadingController.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicLong; @@ -193,8 +194,15 @@ public void stackEnvelopes(TreeMap> evidStaFreqMap) { Waveform rawWaveform = result.getResultPayload().get(); rawWaveform.setLowFrequency(stackInfo.getLowFrequency()); rawWaveform.setHighFrequency(stackInfo.getHighFrequency()); - if (rawWaveform != null && rawWaveform.hasData() && rawWaveform.getSegmentLength() > 0 && rawWaveform.getStream() != null && rawWaveform.getStream().getStation() != null) { - waveformsByFreqAndSta.computeIfAbsent(entry.getKey() + " " + rawWaveform.getStream().getStation().hashCode(), k -> new ArrayList<>()).add(rawWaveform); + if (rawWaveform != null + && rawWaveform.hasData() + && rawWaveform.getSegmentLength() > 0 + && rawWaveform.getStream() != null + && rawWaveform.getStream().getStation() != null + && rawWaveform.getStream().getChannelName() != null) { + if (!gov.llnl.gnem.apps.coda.common.model.domain.Stream.TYPE_STACK.equalsIgnoreCase(rawWaveform.getStream().getChannelName())) { + waveformsByFreqAndSta.computeIfAbsent(entry.getKey() + " " + rawWaveform.getStream().getStation().hashCode(), k -> new ArrayList<>()).add(rawWaveform); + } } else { log.warn("No data or bad station specification for waveform {}.", rawWaveform); } @@ -206,7 +214,7 @@ public void stackEnvelopes(TreeMap> evidStaFreqMap) { } } - List stackedWaveforms = waveformsByFreqAndSta.entrySet().stream().map(e -> stackEnvelopes(e.getValue())).collect(Collectors.toList()); + List stackedWaveforms = waveformsByFreqAndSta.entrySet().stream().map(e -> stackEnvelopes(e.getValue())).filter(Objects::nonNull).collect(Collectors.toList()); // TODO: Export envelopes and stacks to separate dirs for (Waveform stackedWaveform : stackedWaveforms) { @@ -237,13 +245,13 @@ private Waveform stackEnvelopes(List waves) { base = waves.get(0); TimeSeries seis = convertToTimeSeries(base); - float[] seisData = seis.getData(); for (int i = 1; i < waves.size(); i++) { TimeSeries seis2 = convertToTimeSeries(waves.get(i)); seis = seis.add(seis2); } seis.MultiplyScalar(1d / waves.size()); + float[] seisData = seis.getData(); double[] data = new double[seisData.length]; for (int j = 0; j < data.length; ++j) { data[j] = seisData[j]; @@ -257,7 +265,7 @@ private Waveform stackEnvelopes(List waves) { base.setBeginTime(seis.getTime().getDate()); base.setEndTime(seis.getEndtime().getDate()); if (base.getStream() != null) { - base.getStream().setChannelName("STACK"); + base.getStream().setChannelName(gov.llnl.gnem.apps.coda.common.model.domain.Stream.TYPE_STACK); } } catch (Exception e) { log.info(e.getMessage(), e); @@ -290,7 +298,7 @@ public Path getExportPath(Waveform w) { } if (w == null) { - throw new IllegalStateException("Unable to export waveform,waveform was null"); + throw new IllegalStateException("Unable to export waveform, waveform was null"); } String station = Optional.ofNullable(w.getStream()).map(stream -> stream.getStation()).map(sta -> sta.getStationName()).orElse(""); diff --git a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeParamsWebClient.java b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeParamsWebClient.java index e3c0c93d..640b0f51 100644 --- a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeParamsWebClient.java +++ b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeParamsWebClient.java @@ -42,7 +42,7 @@ public EnvelopeParamsWebClient(WebClient client) { @Override public Mono postEnvelopeJobConfiguration(EnvelopeJobConfiguration config) { - return client.post().uri("/envelopes/job-configuration/update").contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).syncBody(config).exchange().doOnSuccess(resp -> { + return client.post().uri("/envelopes/job-configuration/update").contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).bodyValue(config).exchange().doOnSuccess(resp -> { if (!HttpStatus.OK.equals(resp.statusCode())) { throw new LightweightIllegalStateException(resp.toString()); } diff --git a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeWebClient.java b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeWebClient.java index 403a92c5..cc1e00f3 100644 --- a/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeWebClient.java +++ b/envelope-gui/src/main/java/gov/llnl/gnem/apps/coda/envelope/gui/data/client/EnvelopeWebClient.java @@ -65,7 +65,7 @@ public Flux postEnvelopes(Long sessionId, List segments, Env .uri("/envelopes/create/batch/" + sessionId) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) - .syncBody(new EnvelopeJob().setData(segments).setJobConfig(job)) + .bodyValue(new EnvelopeJob().setData(segments).setJobConfig(job)) .exchange() .flatMapMany(resp -> { Mono>> respMono = resp.bodyToMono(POST_ENV_RETURN_TYPE); @@ -85,7 +85,7 @@ public Flux getEnvelopesMatchingAllFields(Waveform segment) { .uri("/waveforms/query/all") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) - .syncBody(segment) + .bodyValue(segment) .exchange() .flatMapMany(response -> response.bodyToFlux(Waveform.class)) .onErrorReturn(new Waveform()); diff --git a/envelope-gui/src/main/resources/application.properties b/envelope-gui/src/main/resources/application.properties index 7e8a4241..3817cdbf 100644 --- a/envelope-gui/src/main/resources/application.properties +++ b/envelope-gui/src/main/resources/application.properties @@ -2,4 +2,5 @@ spring.output.ansi.enabled=ALWAYS logging.level.org.springframework.web.socket.*=off logging.level.org.springframework.web.reactive.function.client.*=off logging.level.org.springframework.web.socket.sockjs.client.SockJsClient=off -webclient.basePath=localhost:53922 \ No newline at end of file +webclient.basePath=localhost:53922 +spring.codec.max-in-memory-size=-1 \ No newline at end of file diff --git a/envelope-service/envelope-application/pom.xml b/envelope-service/envelope-application/pom.xml index a7569604..22d81c00 100644 --- a/envelope-service/envelope-application/pom.xml +++ b/envelope-service/envelope-application/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.7.1 + 1.0.8 4.0.0 @@ -93,30 +93,6 @@ org.springframework.boot spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-tomcat - - - - - org.springframework.boot - spring-boot-starter-jetty - - - org.eclipse.jetty - jetty-server - - - - - org.eclipse.jetty - jetty-alpn-conscrypt-server - - - org.eclipse.jetty.http2 - http2-server gov.llnl.gnem.apps.coda.common diff --git a/envelope-service/envelope-model/pom.xml b/envelope-service/envelope-model/pom.xml index b71efe85..da4ef786 100644 --- a/envelope-service/envelope-model/pom.xml +++ b/envelope-service/envelope-model/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/envelope-service/envelope-repository/pom.xml b/envelope-service/envelope-repository/pom.xml index fe37053e..ce741e3e 100644 --- a/envelope-service/envelope-repository/pom.xml +++ b/envelope-service/envelope-repository/pom.xml @@ -4,7 +4,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/envelope-service/envelope-service-api/pom.xml b/envelope-service/envelope-service-api/pom.xml index 8934d43f..a82df631 100644 --- a/envelope-service/envelope-service-api/pom.xml +++ b/envelope-service/envelope-service-api/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/envelope-service/envelope-service-impl/pom.xml b/envelope-service/envelope-service-impl/pom.xml index 67104b52..6936d67e 100644 --- a/envelope-service/envelope-service-impl/pom.xml +++ b/envelope-service/envelope-service-impl/pom.xml @@ -6,7 +6,7 @@ gov.llnl.gnem.apps.coda.envelope envelope-service - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java index 632b1678..df366a3a 100644 --- a/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java +++ b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeCreationServiceImpl.java @@ -116,10 +116,7 @@ private Waveform createEnvelopeForBand(Waveform wave, EnvelopeBandParameters ban //Note these mutate the series double maxNeededRate = sampRate; if (maxNeededRate > bandConfig.getHighFrequency() * 2.0) { - maxNeededRate = (int) (bandConfig.getHighFrequency() * 2.0 + 0.5); - } - if (maxNeededRate < 1.0) { - maxNeededRate = 1.0; + maxNeededRate = bandConfig.getHighFrequency() * 2.0; } if (maxNeededRate < bandConfig.getInterpolation()) { maxNeededRate = bandConfig.getInterpolation(); @@ -144,7 +141,9 @@ private Waveform createEnvelopeForBand(Waveform wave, EnvelopeBandParameters ban double trimlength = 2 * smoothing / seis.getSamprate(); seis.cut(seis.getTime().add(trimlength), seis.getEndtime().add(-1 * trimlength)); - seis.interpolate(maxNeededRate); + if (maxNeededRate != sampRate) { + seis.interpolate(maxNeededRate); + } seisWave.setSampleRate(seis.getSamprate()); seisWave.setSegment(WaveformUtils.floatsToDoubles(seis.getData())); diff --git a/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java index a62d4803..42377395 100644 --- a/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java +++ b/envelope-service/envelope-service-impl/src/main/java/gov/llnl/gnem/apps/coda/envelope/service/impl/EnvelopeStacker.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import gov.llnl.gnem.apps.coda.common.model.domain.Stream; import gov.llnl.gnem.apps.coda.common.model.domain.Waveform; import gov.llnl.gnem.apps.coda.common.service.util.WaveformToTimeSeriesConverter; import gov.llnl.gnem.apps.coda.common.service.util.WaveformUtils; @@ -77,7 +78,7 @@ public Waveform stackEnvelopes(List waves) { base.setBeginTime(seis.getTime().getDate()); base.setEndTime(seis.getEndtime().getDate()); if (base.getStream() != null) { - base.getStream().setChannelName("STACK"); + base.getStream().setChannelName(Stream.TYPE_STACK); } } catch (Exception e) { log.info(e.getMessage(), e); diff --git a/envelope-service/pom.xml b/envelope-service/pom.xml index 7d295e5e..2d171212 100644 --- a/envelope-service/pom.xml +++ b/envelope-service/pom.xml @@ -5,7 +5,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 gov.llnl.gnem.apps.coda.envelope diff --git a/envelope-standalone/pom.xml b/envelope-standalone/pom.xml index d3a24f08..35fdf263 100644 --- a/envelope-standalone/pom.xml +++ b/envelope-standalone/pom.xml @@ -6,7 +6,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 4.0.0 diff --git a/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/EnvelopeStandalone.java b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/EnvelopeStandalone.java index a074becc..b30f1d79 100644 --- a/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/EnvelopeStandalone.java +++ b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/EnvelopeStandalone.java @@ -72,7 +72,7 @@ public static synchronized void main(String[] args) { } launch(EnvelopeStandalone.class, args); } catch (Exception e) { - log.error("Exception at CodaCalibrationStandalone::main", e); + log.error("Exception at EnvelopeStandalone::main", e); Platform.exit(); System.exit(1); } diff --git a/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java index 47c76b5f..e638351c 100644 --- a/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java +++ b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeLocalClient.java @@ -36,11 +36,11 @@ @Primary public class EnvelopeLocalClient implements EnvelopeClient { + private static final Logger log = LoggerFactory.getLogger(EnvelopeLocalClient.class); + private EnvelopeCreationService service; private WaveformService waveformService; - private static final Logger log = LoggerFactory.getLogger(EnvelopeLocalClient.class); - @Autowired public EnvelopeLocalClient(EnvelopeCreationService service, WaveformService waveformService) { this.service = service; diff --git a/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeParamsLocalClient.java b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeParamsLocalClient.java new file mode 100644 index 00000000..3595229a --- /dev/null +++ b/envelope-standalone/src/main/java/gov/llnl/gnem/apps/coda/envelope/standalone/data/client/EnvelopeParamsLocalClient.java @@ -0,0 +1,54 @@ +/* +* Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.envelope.standalone.data.client; + +import java.util.ArrayList; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +import gov.llnl.gnem.apps.coda.envelope.gui.data.api.EnvelopeParamsClient; +import gov.llnl.gnem.apps.coda.envelope.model.domain.EnvelopeJobConfiguration; +import gov.llnl.gnem.apps.coda.envelope.service.api.EnvelopeParamsService; +import reactor.core.publisher.Mono; + +@Component +@Primary +public class EnvelopeParamsLocalClient implements EnvelopeParamsClient { + + private static final Logger log = LoggerFactory.getLogger(EnvelopeParamsLocalClient.class); + + private EnvelopeParamsService service; + + @Autowired + public EnvelopeParamsLocalClient(EnvelopeParamsService service) { + this.service = service; + } + + @Override + public Mono postEnvelopeJobConfiguration(EnvelopeJobConfiguration config) { + service.setConfiguration(new EnvelopeJobConfiguration(new ArrayList<>(config.getFrequencyBandConfiguration()))); + return Mono.empty(); + } + + @Override + public Mono getEnvelopeJobConfiguration() { + return Mono.just(service.getConfiguration()); + } + +} diff --git a/envelope-standalone/src/main/resources/application.properties b/envelope-standalone/src/main/resources/application.properties index a98022ee..ffb17749 100644 --- a/envelope-standalone/src/main/resources/application.properties +++ b/envelope-standalone/src/main/resources/application.properties @@ -32,4 +32,5 @@ logging.level.org.springframework.web.reactive.function.client.*=off logging.level.org.springframework.web.socket.sockjs.client.SockJsClient=off envelope-app.height=1200 envelope-app.width=800 -envelope-app.baseTitle=Envelope Tool \ No newline at end of file +envelope-app.baseTitle=Envelope Tool +spring.codec.max-in-memory-size=-1 \ No newline at end of file diff --git a/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb b/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb index 1c759e1c..d415e2cf 100644 --- a/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb +++ b/example-notebooks/measure-mws/Mw-From-FDSN-waveforms.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -33,7 +33,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -94,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -113,20 +113,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "1 Event(s) in Catalog:\n", - "2007-10-31T03:04:54.810000Z | +37.434, -121.774 | 5.45 Mw | manual\n" - ] - } - ], + "outputs": [], "source": [ "starttime = UTCDateTime(\"2007/10/31\")\n", "endtime = UTCDateTime(\"2007/11/01\")\n", @@ -137,31 +128,11 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "12 Trace(s) in Stream:\n", - "BK.BRK..BHE | 2007-10-31T02:56:34.835645Z - 2007-10-31T03:54:54.810645Z | 40.0 Hz, 140000 samples\n", - "BK.BRK..BHN | 2007-10-31T02:56:34.835645Z - 2007-10-31T03:54:54.810645Z | 40.0 Hz, 140000 samples\n", - "BK.BKS..BHE | 2007-10-31T02:56:34.823338Z - 2007-10-31T03:54:54.798338Z | 40.0 Hz, 140000 samples\n", - "BK.BKS..BHN | 2007-10-31T02:56:34.823338Z - 2007-10-31T03:54:54.798338Z | 40.0 Hz, 140000 samples\n", - "BK.CVS..BHE | 2007-10-31T02:56:34.837466Z - 2007-10-31T03:54:54.812466Z | 40.0 Hz, 140000 samples\n", - "BK.CVS..BHN | 2007-10-31T02:56:34.837465Z - 2007-10-31T03:54:54.812465Z | 40.0 Hz, 140000 samples\n", - "BK.MHC..BHE | 2007-10-31T02:56:34.835642Z - 2007-10-31T03:54:54.810642Z | 40.0 Hz, 140000 samples\n", - "BK.MHC..BHN | 2007-10-31T02:56:34.835642Z - 2007-10-31T03:54:54.810642Z | 40.0 Hz, 140000 samples\n", - "BK.SAO..BHE | 2007-10-31T02:56:34.823144Z - 2007-10-31T03:54:54.798144Z | 40.0 Hz, 140000 samples\n", - "BK.SAO..BHN | 2007-10-31T02:56:34.823144Z - 2007-10-31T03:54:54.798144Z | 40.0 Hz, 140000 samples\n", - "BK.WENL..BHE | 2007-10-31T02:56:34.827466Z - 2007-10-31T03:54:54.802466Z | 40.0 Hz, 140000 samples\n", - "BK.WENL..BHN | 2007-10-31T02:56:34.827466Z - 2007-10-31T03:54:54.802466Z | 40.0 Hz, 140000 samples\n" - ] - } - ], + "outputs": [], "source": [ "st = None\n", "\n", @@ -175,6 +146,17 @@ " ste = client.get_waveforms(\"BK\", sta, \"*\", \"BHE\", t - 500, t + 3000, attach_response=True)\n", " stn = client.get_waveforms(\"BK\", sta, \"*\", \"BHN\", t - 500, t + 3000, attach_response=True)\n", "\n", + " #Instrument correction may be required\n", + " pre_filt = (0.005, 0.006, 30.0, 35.0) \n", + " ste.remove_response(output='VEL', pre_filt=pre_filt)\n", + " stn.remove_response(output='VEL', pre_filt=pre_filt)\n", + "\n", + " #ObsPy defaults to m/s for VEL and we want NM/s\n", + " for trace in ste.traces:\n", + " trace.data = trace.data * 1e+9\n", + " for trace in stn.traces:\n", + " trace.data = trace.data * 1e+9 \n", + " \n", " if (st == None):\n", " st = ste + stn\n", " else:\n", @@ -192,7 +174,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -210,7 +192,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -226,18 +208,9 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "200\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "with open(\"norcal_bands.json\") as bands:\n", " norcal_bands = json.load(bands)\n", @@ -255,17 +228,9 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "200\n" - ] - } - ], + "outputs": [], "source": [ "with open(\"norcal_site.json\") as corrections:\n", " norcal_corrections = json.load(corrections)\n", @@ -282,19 +247,11 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "200\n" - ] - } - ], + "outputs": [], "source": [ "r = requests.post('https://127.0.0.1:53922/api/v1/envelopes/create/batch-stacks-only/10000', json=waveData, verify=False)\n", "print(r.status_code)\n", @@ -310,40 +267,28 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "200\n" - ] - } - ], + "outputs": [], "source": [ - "r = requests.post('https://127.0.0.1:53921/api/v1/measurement/measure-mws-from-stacks/true', json=envData, verify=False)\n", + "request = {\n", + " \"stacks\": envData,\n", + " \"autopickingEnabled\":\"true\"\n", + "}\n", + "r = requests.post('https://127.0.0.1:53921/api/v1/measurement/measure-mws', json=request, verify=False, timeout=10000)\n", "print(r.status_code)\n", "eventMws = json.loads(r.text)" ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'eventId': '40204628', 'mw': 5.405543565488166, 'refMw': None, 'apparentStressInMpa': 0.5181555647743256, 'refApparentStressInMpa': None, 'dataCount': 60, 'latitude': 37.4325, 'longitude': -121.7756667, 'datetime': '2007-10-31T03:04:54.820Z'}\n" - ] - } - ], + "outputs": [], "source": [ - "print(list(eventMws[\"measuredMwDetails\"].values())[0])" + "print(eventMws[\"measuredMwDetails\"][\"40204628\"])" ] }, { @@ -355,26 +300,15 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "x1 = [cat.events[0].magnitudes[0].mag]\n", - "x2 = [list(eventMws[\"measuredMwDetails\"].values())[0][\"mw\"]]\n", + "x2 = []\n", + "for evs in eventMws[\"measuredMwDetails\"]:\n", + " x2.append(eventMws[\"measuredMwDetails\"][evs][\"mw\"]) \n", "scale = 200.0\n", "\n", "ax.scatter(x1,x1, c='blue', s=scale, label='NC Mw',\n", @@ -413,7 +347,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.6" } }, "nbformat": 4, diff --git a/externals/pom.xml b/externals/pom.xml index 721cee82..f7ebe404 100644 --- a/externals/pom.xml +++ b/externals/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 externals diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/JPlotContainer.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/JPlotContainer.java index bc95c70f..b96893fd 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/JPlotContainer.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/JPlotContainer.java @@ -353,7 +353,7 @@ public void exportSVG(File file) throws UnsupportedEncodingException, FileNotFou // Set the properties that will make for the best quality SVG // Copied from: JPlotContainer.printCurrentPlot method // Only putting method call here and not in other exportSvg calls because all roads lead here in the end - // Logic to set these properties is now centralized in JPlotContainer so all apps immediatly see advtange + // Logic to set these properties is now centralized in JPlotContainer so all apps immediately see advantage setupSvgExportSettings(); SVGGraphics2D svgGenerator = renderSVG(createDocument()); diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Cross.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Cross.java index d2d3d2ac..3aba8de7 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Cross.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Cross.java @@ -112,7 +112,7 @@ public void PaintSymbol(Graphics g, int x, int y, int h) { cross.lineTo(x + h6, y + h2); cross.lineTo(x - h6, y + h2); cross.lineTo(x, y); - cross.lineTo(x - h2, y + h6); + cross.closePath(); g2d.setColor(getFillColor()); g2d.fill(cross); diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Diamond.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Diamond.java index c2a0cea7..9a5667d0 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Diamond.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Diamond.java @@ -95,7 +95,7 @@ public void PaintSymbol(Graphics g, int x, int y, int h) { diamond.lineTo(x + h2, y); diamond.lineTo(x, y + h2); diamond.lineTo(x - h2, y); - diamond.lineTo(x, y - h2); + diamond.closePath(); g2d.setColor(getFillColor()); g2d.fill(diamond); g2d.setColor(_EdgeColor); diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Hexagon.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Hexagon.java index 72efa57d..159c1c0c 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Hexagon.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Hexagon.java @@ -104,6 +104,7 @@ public void PaintSymbol(Graphics g, int x, int y, int h) { Yvalue = (float) (y - radius * Math.cos(theta)); hex.lineTo(Xvalue, Yvalue); } + hex.closePath(); g2d.setColor(getFillColor()); g2d.fill(hex); diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Plus.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Plus.java index 3825efab..00da3e0b 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Plus.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Plus.java @@ -107,7 +107,7 @@ public void PaintSymbol(Graphics g, int x, int y, int h) { plus.lineTo(x - h15, y + h2); plus.lineTo(x - h15, y + h15); plus.lineTo(x - h2, y + h15); - plus.lineTo(x - h2, y - h15); + plus.closePath(); g2d.setColor(getFillColor()); g2d.fill(plus); diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Star5.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Star5.java index 5d50c9b0..7936b3c2 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Star5.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/Star5.java @@ -104,6 +104,7 @@ public void PaintSymbol(Graphics g, int x, int y, int h) { float Yvalue = (float) (y - radius * Math.cos(theta)); plus.lineTo(Xvalue, Yvalue); } + plus.closePath(); g2d.setColor(getFillColor()); g2d.fill(plus); diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleDn.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleDn.java index 9f73d314..01a5566a 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleDn.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleDn.java @@ -95,7 +95,7 @@ public void PaintSymbol(Graphics g, int x, int y, int h) { triangle.moveTo(x - h2, y - h2); triangle.lineTo(x + h2, y - h2); triangle.lineTo(x, y + h2); - triangle.lineTo(x - h2, y - h2); + triangle.closePath(); g2d.setColor(getFillColor()); g2d.fill(triangle); g2d.setColor(_EdgeColor); diff --git a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleUp.java b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleUp.java index 68340ee7..6f3696f1 100755 --- a/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleUp.java +++ b/externals/src/main/java/llnl/gnem/core/gui/plotting/plotobject/TriangleUp.java @@ -95,7 +95,7 @@ public void PaintSymbol(Graphics g, int x, int y, int h) { triangle.moveTo(x, y - h2); triangle.lineTo(x + h2, y + h2); triangle.lineTo(x - h2, y + h2); - triangle.lineTo(x, y - h2); + triangle.closePath(); g2d.setColor(getFillColor()); g2d.fill(triangle); g2d.setColor(edgeRenderColor); diff --git a/externals/src/main/java/llnl/gnem/core/util/MathFunctions/FitnessCriteria.java b/externals/src/main/java/llnl/gnem/core/util/MathFunctions/FitnessCriteria.java index e5c3eb7a..559f3e0a 100755 --- a/externals/src/main/java/llnl/gnem/core/util/MathFunctions/FitnessCriteria.java +++ b/externals/src/main/java/llnl/gnem/core/util/MathFunctions/FitnessCriteria.java @@ -16,7 +16,7 @@ package llnl.gnem.core.util.MathFunctions; import java.util.Collection; -import java.util.HashMap; +import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -236,15 +236,15 @@ public static Double CVRMSD(double[] data, double[] reference) { * The Coefficient of Variation of the Root-Mean-Square Difference (aka * CV(RMSD) or CV(RMSE)) * - * @param dataHashMap - * a HashMap of data and reference values to be compared note - * that the HashMap key is not used in the RMS fit procedure + * @param dataMap + * a Map of data and reference values to be compared note that + * the Map key is not used in the RMS fit procedure * @return the single float valued measure of the RMS fit */ - public static Double CVRMSD(HashMap dataHashMap) { + public static Double CVRMSD(Map dataMap) { double result = Double.MAX_VALUE; - Collection values = dataHashMap.values(); + Collection values = dataMap.values(); Object[] object = values.toArray(); diff --git a/mapping/pom.xml b/mapping/pom.xml index ab8423eb..a97c5917 100644 --- a/mapping/pom.xml +++ b/mapping/pom.xml @@ -7,7 +7,7 @@ gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 gov.llnl.gnem.apps.coda.common diff --git a/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMap.java b/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMap.java index e7cf8505..423a1aa8 100644 --- a/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMap.java +++ b/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMap.java @@ -26,6 +26,10 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gov.llnl.gnem.apps.coda.common.mapping.api.GeoBox; import gov.llnl.gnem.apps.coda.common.mapping.api.GeoShape; import gov.llnl.gnem.apps.coda.common.mapping.api.Icon; import gov.llnl.gnem.apps.coda.common.mapping.api.Icon.IconStyles; @@ -45,6 +49,8 @@ public class LeafletMap { + private static final Logger log = LoggerFactory.getLogger(LeafletMap.class); + private WebView webView; private ObservableSet icons = FXCollections.synchronizedObservableSet(FXCollections.observableSet(new HashSet<>())); private ObservableSet shapes = FXCollections.observableSet(new HashSet<>()); @@ -323,18 +329,14 @@ private String createJsIconRepresentation(Icon icon) { switch (icon.getType()) { case TRIANGLE_UP: - sb.append("marker = L.marker(["); + sb.append("marker = L.shapeMarker(["); sb.append(icon.getLocation().getLatitude()); sb.append(','); sb.append(icon.getLocation().getLongitude()); - sb.append("], {icon: L.icon({"); - sb.append("iconUrl: "); + sb.append("], {"); + sb.append("shape: 'triangle-up',"); sb.append(getTriangleStyle(icon.getStyle())); - sb.append("iconSize: [12, 12],"); - sb.append("iconAnchor: [6, 6],"); - sb.append("popupAnchor: [-3, -3]"); - sb.append("})"); - sb.append(getTriangleStyleZIndex(icon.getStyle())); + sb.append("radius: 4"); sb.append("})"); break; case CIRCLE: @@ -376,35 +378,18 @@ private String addCallbacks(String id, String name) { + "\" === mouseoverIconId) { mouseoverIconId = null; }})"; } - private String getTriangleStyleZIndex(IconStyles style) { - String jsonStyle; - switch (style) { - case FOCUSED: - jsonStyle = ", zIndexOffset: 800000, interactive: false"; - break; - case BACKGROUND: - jsonStyle = ", zIndexOffset: -800000"; - break; - case DEFAULT: - default: - jsonStyle = ""; - break; - } - return jsonStyle; - } - private String getTriangleStyle(IconStyles style) { String jsonStyle; switch (style) { case FOCUSED: - jsonStyle = "'images/triangle-up-focused.png',"; + jsonStyle = "color: 'white', fillColor: 'white', opacity: 1, fillOpacity: 1, lineJoin: 'mitre', pane: 'important-event-pane', interactive: false,"; break; case BACKGROUND: - jsonStyle = "'images/triangle-up-background.png',"; + jsonStyle = "color: 'gray', fillColor: 'gray', opacity: 1, fillOpacity: 1, lineJoin: 'mitre',"; break; case DEFAULT: default: - jsonStyle = "'images/triangle-up.png',"; + jsonStyle = "color: 'yellow', fillColor: 'yellow', opacity: 1, fillOpacity: 1, lineJoin: 'mitre',"; break; } return jsonStyle; @@ -426,4 +411,43 @@ private String getCircleStyle(IconStyles style) { } return jsonStyle; } + + public WebView getWebView() { + return webView; + } + + public String getSvgLayer() { + return (String) webView.getEngine().executeScript("getSvgLayer();"); + } + + public void fitToBounds(GeoBox bounds) { + webView.getEngine().executeScript("fitBounds([[" + bounds.getMinX() + "," + bounds.getMinY() + "], [" + bounds.getMaxX() + "," + bounds.getMaxY() + "]]);"); + } + + public GeoBox getMapBounds() { + GeoBox bounds; + try { + Double mapXne = (Double) webView.getEngine().executeScript("getMapBoundXne();"); + Double mapYne = (Double) webView.getEngine().executeScript("getMapBoundYne();"); + Double mapXsw = (Double) webView.getEngine().executeScript("getMapBoundXsw();"); + Double mapYsw = (Double) webView.getEngine().executeScript("getMapBoundYsw();"); + bounds = new GeoBox(mapXne, mapYne, mapXsw, mapYsw); + } catch (ClassCastException | NullPointerException e) { + bounds = null; + log.debug("Problem attempting to get bounds from map : {}", e.getLocalizedMessage(), e); + } + return bounds; + } + + public void setShowOverlay(boolean showOverlay) { + if (showOverlay) { + webView.getEngine().executeScript("showOverlay();"); + } else { + webView.getEngine().executeScript("hideOverlay();"); + } + } + + public Boolean hasVisibleTileLayers() { + return (Boolean) webView.getEngine().executeScript("hasVisibleTiles();"); + } } diff --git a/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMapController.java b/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMapController.java index 1efd5bcf..869ef1b3 100644 --- a/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMapController.java +++ b/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/LeafletMapController.java @@ -14,10 +14,19 @@ */ package gov.llnl.gnem.apps.coda.common.mapping; +import java.awt.image.BufferedImage; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.Instant; import java.util.Collection; +import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import javax.imageio.ImageIO; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -29,11 +38,22 @@ import gov.llnl.gnem.apps.coda.common.mapping.api.GeoShape; import gov.llnl.gnem.apps.coda.common.mapping.api.Icon; import javafx.application.Platform; +import javafx.embed.swing.SwingFXUtils; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; +import javafx.scene.SnapshotParameters; +import javafx.scene.control.Button; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.ScrollPane; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.layout.StackPane; +import javafx.scene.transform.Transform; +import javafx.scene.web.WebView; +import javafx.stage.DirectoryChooser; import javafx.stage.Stage; import javafx.stage.StageStyle; @@ -41,14 +61,22 @@ @Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON) public class LeafletMapController implements GeoMap { + private static final Logger log = LoggerFactory.getLogger(LeafletMapController.class); + @FXML private StackPane view; + @FXML + private ScrollPane viewScroll; + + @FXML + private Button snapshotButton; + private LeafletMap mapImpl; private Stage stage; - private static final Logger log = LoggerFactory.getLogger(LeafletMapController.class); + private DirectoryChooser screenshotFolderChooser = new DirectoryChooser(); private LeafletMapController(@Autowired(required = false) MapProperties mapProps) { Platform.runLater(() -> { @@ -64,6 +92,13 @@ private LeafletMapController(@Autowired(required = false) MapProperties mapProps if (mapProps != null) { mapProps.getLayers().forEach(mapImpl::addLayer); } + + ImageView label = new ImageView(); + label.setImage(new Image(getClass().getResource("/fxml/snapshot-icon.png").toExternalForm())); + label.setFitWidth(16); + label.setFitHeight(16); + snapshotButton.setGraphic(label); + snapshotButton.setContentDisplay(ContentDisplay.CENTER); } catch (IOException e) { throw new IllegalStateException(e); } @@ -189,4 +224,80 @@ public String toString() { return builder.toString(); } + @FXML + private void showSnapshotDialog(ActionEvent e) { + File folder = screenshotFolderChooser.showDialog(view.getScene().getWindow()); + try { + if (folder != null && folder.exists() && folder.isDirectory() && folder.canWrite()) { + screenshotFolderChooser.setInitialDirectory(folder); + exportSnapshot(folder); + } + } catch (SecurityException ex) { + log.warn("Exception trying to write screenshots to folder {} : {}", folder, ex.getLocalizedMessage(), ex); + } + } + + private void exportSnapshot(File folder) { + String mapId = folder.getAbsolutePath() + File.separator + "Map_" + Instant.now().toEpochMilli(); + try { + String svg = mapImpl.getSvgLayer(); + if (svg != null && !svg.trim().isEmpty()) { + Files.write(Paths.get(mapId + ".svg"), svg.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + } catch (IOException e) { + log.warn("Unable to write map svg due to file exception {}", e.getLocalizedMessage(), e); + } + writePng(mapId + ".png"); + } + + private Image snapshot(final WebView node) { + SnapshotParameters params = new SnapshotParameters(); + params.setTransform(Transform.scale(4.0, 4.0)); + Image snapshot = node.snapshot(params, null); + return snapshot; + } + + private void writePng(BufferedImage image, String filename) { + try { + ImageIO.write(image, "png", new File(filename)); + log.trace("Wrote image to: {}", filename); + } catch (IOException ex) { + log.warn(ex.getMessage(), ex); + } catch (NullPointerException ex) { + log.warn("Null pointer writing image {} to file {} : {}", image, filename, ex.getMessage(), ex); + } + } + + private void writePng(String filename) { + mapImpl.setShowOverlay(false); + CompletableFuture.runAsync(() -> { + //This is really dumb and subject to nasty race conditions but, as of writing (Jan 2020), I can find no good way to get a notification from the JavaFX renderer that it actually executed a render pass. + //There is a pre/post pulse callback on Scene proposed but is not available in Java 8 and we need to maintain compatibility for the moment. + //Thus we have to do this song and dance to have a reasonable assumption a pulse has happened on the FX thread. + //We could try to register and start/stop an AnimationTimer to know for sure but that's considerably more complicated and the fail state for this is "has visible buttons in the screenshot" so eh. + try { + Thread.sleep(200l); + } catch (InterruptedException e) { + //Nop + } + Platform.runLater(() -> { + try { + Boolean visibleLayers = mapImpl.hasVisibleTileLayers(); + if (visibleLayers) { + Image snapshot = snapshot(mapImpl.getWebView()); + CompletableFuture.runAsync(() -> { + if (visibleLayers) { + writePng(SwingFXUtils.fromFXImage(snapshot, null), filename); + } + Platform.runLater(() -> mapImpl.setShowOverlay(true)); + }); + } else { + mapImpl.setShowOverlay(true); + } + } catch (RuntimeException e) { + mapImpl.setShowOverlay(true); + } + }); + }); + } } diff --git a/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/api/GeoBox.java b/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/api/GeoBox.java new file mode 100644 index 00000000..cb91192d --- /dev/null +++ b/mapping/src/main/java/gov/llnl/gnem/apps/coda/common/mapping/api/GeoBox.java @@ -0,0 +1,66 @@ +/* +* Copyright (c) 2020, Lawrence Livermore National Security, LLC. Produced at the Lawrence Livermore National Laboratory +* CODE-743439. +* All rights reserved. +* This file is part of CCT. For details, see https://github.com/LLNL/coda-calibration-tool. +* +* Licensed under the Apache License, Version 2.0 (the “Licensee”); you may not use this file except in compliance with the License. You may obtain a copy of the License at: +* http://www.apache.org/licenses/LICENSE-2.0 +* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and limitations under the license. +* +* This work was performed under the auspices of the U.S. Department of Energy +* by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344. +*/ +package gov.llnl.gnem.apps.coda.common.mapping.api; + +public class GeoBox { + + private Double minX; + private Double minY; + private Double maxX; + private Double maxY; + + public GeoBox(Double minX, Double minY, Double maxX, Double maxY) { + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + + public Double getMinX() { + return minX; + } + + public GeoBox setMinX(Double minX) { + this.minX = minX; + return this; + } + + public Double getMinY() { + return minY; + } + + public GeoBox setMinY(Double minY) { + this.minY = minY; + return this; + } + + public Double getMaxX() { + return maxX; + } + + public GeoBox setMaxX(Double maxX) { + this.maxX = maxX; + return this; + } + + public Double getMaxY() { + return maxY; + } + + public GeoBox setMaxY(Double maxY) { + this.maxY = maxY; + return this; + } +} diff --git a/mapping/src/main/resources/fxml/MapView.fxml b/mapping/src/main/resources/fxml/MapView.fxml index 8f04dfb7..ca0a19bf 100644 --- a/mapping/src/main/resources/fxml/MapView.fxml +++ b/mapping/src/main/resources/fxml/MapView.fxml @@ -1,11 +1,30 @@ + + + + - - + - + + + +
+ + + + + +
+
+ +
diff --git a/mapping/src/main/resources/fxml/snapshot-icon.png b/mapping/src/main/resources/fxml/snapshot-icon.png new file mode 100755 index 00000000..8a86004b Binary files /dev/null and b/mapping/src/main/resources/fxml/snapshot-icon.png differ diff --git a/mapping/src/main/resources/leaflet/images/triangle-up-background.png b/mapping/src/main/resources/leaflet/images/triangle-up-background.png deleted file mode 100644 index 569db594..00000000 Binary files a/mapping/src/main/resources/leaflet/images/triangle-up-background.png and /dev/null differ diff --git a/mapping/src/main/resources/leaflet/images/triangle-up-focused.png b/mapping/src/main/resources/leaflet/images/triangle-up-focused.png deleted file mode 100644 index 030bd17b..00000000 Binary files a/mapping/src/main/resources/leaflet/images/triangle-up-focused.png and /dev/null differ diff --git a/mapping/src/main/resources/leaflet/images/triangle-up.png b/mapping/src/main/resources/leaflet/images/triangle-up.png deleted file mode 100644 index 5d8aa86e..00000000 Binary files a/mapping/src/main/resources/leaflet/images/triangle-up.png and /dev/null differ diff --git a/mapping/src/main/resources/leaflet/leaflet-svg-shape-markers.min.js b/mapping/src/main/resources/leaflet/leaflet-svg-shape-markers.min.js new file mode 100644 index 00000000..121f5593 --- /dev/null +++ b/mapping/src/main/resources/leaflet/leaflet-svg-shape-markers.min.js @@ -0,0 +1 @@ +L.SVG.include({_updateShape:function(t){var i=t._point,s=t._radius,e=t.options.shape;if("diamond"===e){var n="M"+(i.x-Math.sqrt(2)*s)+" "+i.y+" L "+i.x+" "+(i.y-Math.sqrt(2)*s)+" L"+(i.x+Math.sqrt(2)*s)+" "+i.y+" L"+i.x+" "+(i.y+Math.sqrt(2)*s)+" L"+(i.x-Math.sqrt(2)*s)+" "+i.y;this._setPath(t,n)}if("square"===e){n="M"+(i.x-s)+" "+(i.y-s)+" L "+(i.x+s)+" "+(i.y-s)+" L"+(i.x+s)+" "+(i.y+s)+" L"+(i.x-s)+" "+(i.y+s)+" L"+(i.x-s)+" "+(i.y-s);this._setPath(t,n)}if("triangle"===e||"triangle-up"===e){n="M"+(i.x-1.3*s)+" "+(i.y+.75*s)+" L"+i.x+" "+(i.y-1.5*s)+" L"+(i.x+1.3*s)+" "+(i.y+.75*s)+" Z";this._setPath(t,n)}if("triangle-down"===e){n="M"+(i.x-1.3*s)+" "+(i.y-.75*s)+" L"+i.x+" "+(i.y+1.5*s)+" L"+(i.x+1.3*s)+" "+(i.y-.75*s)+" Z";this._setPath(t,n)}if("circle"===e&&this._updateCircle(t),"x"===e){s/=2;n="M"+(i.x+s)+","+(i.y+s)+"L"+(i.x-s)+","+(i.y-s)+"M"+(i.x-s)+","+(i.y+s)+"L"+(i.x+s)+","+(i.y-s);this._setPath(t,n)}}}),L.ShapeMarker=L.Path.extend({options:{fill:!0,shape:"triangle",radius:10},initialize:function(t,i){L.setOptions(this,i),this._latlng=L.latLng(t),this._radius=this.options.radius},setLatLng:function(t){return this._latlng=L.latLng(t),this.redraw(),this.fire("move",{latlng:this._latlng})},getLatLng:function(){return this._latlng},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius},setStyle:function(t){var i=t&&t.radius||this._radius;return L.Path.prototype.setStyle.call(this,t),this.setRadius(i),this},_project:function(){this._point=this._map.latLngToLayerPoint(this._latlng),this._updateBounds()},_updateBounds:function(){var t=this._radius,i=this._radiusY||t,s=this._clickTolerance(),e=[t+s,i+s];this._pxBounds=new L.Bounds(this._point.subtract(e),this._point.add(e))},_update:function(){this._map&&this._updatePath()},_updatePath:function(){this._renderer._updateShape(this)},_empty:function(){return this._size&&!this._renderer._bounds.intersects(this._pxBounds)},toGeoJSON:function(){return L.GeoJSON.getFeature(this,{type:"Point",coordinates:L.GeoJSON.latLngToCoords(this.getLatLng())})}}),L.shapeMarker=function(t,i){return new L.ShapeMarker(t,i)}; diff --git a/mapping/src/main/resources/leaflet/leaflet.html b/mapping/src/main/resources/leaflet/leaflet.html index 58ac2d02..643d5f23 100644 --- a/mapping/src/main/resources/leaflet/leaflet.html +++ b/mapping/src/main/resources/leaflet/leaflet.html @@ -4,16 +4,34 @@ + - -
+ +
diff --git a/pom.xml b/pom.xml index 857e005a..100b66da 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 gov.llnl.gnem.apps.coda.calibration coda-calibration - 1.0.7.1 + 1.0.8 coda-calibration pom @@ -74,33 +74,33 @@ 1.8 3.8.1 3.1.1 - 3.0.0-M3 + 3.0.0-M4 3.2.1 3.2.0 - 13 + 15-ea+1 2.4.0-b180830.0359 1.2.0.RELEASE - 2.2.0.RELEASE - Dysprosium-SR1 + 2.2.4.RELEASE + Dysprosium-SR4 2.2.1 - 1.11 + 1.12 1.3.04 1.50 - 10.0.0 + 10.1.0 1.15 1.19 3.6.1 - 28.1-jre + 28.2-jre 2.3.1 - 5.2.1.RELEASE - 5.5.2 - 3.1.0 + 5.2.3.RELEASE + 5.6.0 + 3.2.4 1.3.2