diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFilesGroundMotions.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFilesGroundMotions.java new file mode 100644 index 0000000000000000000000000000000000000000..3fcaf21746928f2d89b8fb7fc894e91499fea82d --- /dev/null +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFilesGroundMotions.java @@ -0,0 +1,77 @@ +package gov.usgs.earthquake.nshmp.netcdf; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.netcdf.reader.NetcdfUtils; +import gov.usgs.earthquake.nshmp.netcdf.reader.NetcdfUtils.Key; + +/** + * Read in and parse all AASHTO NetCDF files for a particular year. + * + * @author U.S. Geological Survey + */ +public class NetcdfDataFilesGroundMotions extends NetcdfDataFiles<NetcdfGroundMotions> { + + private int aashtoYear; + + public NetcdfDataFilesGroundMotions(Path netcdfPath, int aashtoYear) { + super(netcdfPath, NetcdfDataType.GROUND_MOTIONS); + this.aashtoYear = aashtoYear; + addAll(readFiles(netcdfPath, dataType())); + } + + /** + * Returns the aastho year of the NetCDF files. + */ + public int aashtoYear() { + return aashtoYear; + } + + /** + * Returns the {@link NetcdfGroundMotions} for a given site. + * + * @param location The site to get the NetCDF data + */ + public NetcdfGroundMotions netcdf(Location location) { + return stream() + .filter(netcdf -> netcdf.netcdfData().contains(location)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException( + String.format( + "Longitude %s and latitude %s not found in data sets.", + location.longitude, + location.latitude))); + } + + @Override + protected List<NetcdfGroundMotions> readFiles(Path netcdfPath, NetcdfDataType dataType) { + try { + List<NetcdfGroundMotions> data = walkFiles(netcdfPath, dataType) + .map(NetcdfGroundMotions::new) + .collect(Collectors.toList()); + + if (data.isEmpty()) { + throw new FileNotFoundException( + String.format("Failed to find AASHTO %s NetCDF files", aashtoYear())); + } + + return List.copyOf(data); + } catch (IOException e) { + throw new RuntimeException("Failed to read NetCDF directory " + netcdfPath, e); + } + } + + @Override + protected Stream<Path> walkFiles(Path netcdfPath, NetcdfDataType dataType) throws IOException { + return super.walkFiles(netcdfPath, dataType) + .filter(file -> NetcdfUtils.readAttribute( + Key.YEAR, + file).getNumericValue().intValue() == aashtoYear()); + } +} diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfGroundMotions.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfGroundMotions.java index 3a9c943b3b74a5f31ff24cbf626dcb54537f0359..d20109927ac0ea27d6dce4be0dc72e08d6579191 100644 --- a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfGroundMotions.java +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfGroundMotions.java @@ -9,7 +9,7 @@ import gov.usgs.earthquake.nshmp.data.XySequence; import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; import gov.usgs.earthquake.nshmp.netcdf.data.BoundingData; -import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfData; +import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfDataGroundMotions; import gov.usgs.earthquake.nshmp.netcdf.data.StaticData; import gov.usgs.earthquake.nshmp.netcdf.reader.BoundingReaderGroundMotions; import gov.usgs.earthquake.nshmp.netcdf.reader.ReaderGroundMotions; @@ -33,8 +33,8 @@ public class NetcdfGroundMotions extends Netcdf<XySequence> { } @Override - public NetcdfData netcdfData() { - return netcdfData; + public NetcdfDataGroundMotions netcdfData() { + return (NetcdfDataGroundMotions) netcdfData; } @Override @@ -56,7 +56,7 @@ public class NetcdfGroundMotions extends Netcdf<XySequence> { var group = ncd.getRootGroup(); return new ReaderGroundMotions(group); } catch (IOException e) { - throw new RuntimeException("Could not read Netcdf file [" + netcdfPath + " ]"); + throw new RuntimeException("Could not read Netcdf file [" + netcdfPath + "]"); } } } diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfDataGroundMotions.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfDataGroundMotions.java new file mode 100644 index 0000000000000000000000000000000000000000..4d45dd2a3d9de063ff946db52976855de8c48e27 --- /dev/null +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfDataGroundMotions.java @@ -0,0 +1,29 @@ +package gov.usgs.earthquake.nshmp.netcdf.data; + +import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.geo.Region; +import gov.usgs.earthquake.nshmp.geo.Regions; + +/** + * NetCDF data container for ground motions (AASHTO) data. + * + * @author U.S. Geological Survey + */ +public class NetcdfDataGroundMotions extends NetcdfData { + + private final Region region; + + public NetcdfDataGroundMotions(NetcdfData netcdfData) { + super(NetcdfData.Builder.copyOf(netcdfData)); + region = Regions.createRectangular("Region", minimumBounds(), maximumBounds()); + } + + /** + * Whether a location is contained in the bounds. + * + * @param location The location to test + */ + public boolean contains(Location location) { + return region.contains(location); + } +} diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderGroundMotions.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderGroundMotions.java index f45da139d717469be4fc52160892913a4d392736..716c3da4686aeb78d8a1f55318ce40484b599ebb 100644 --- a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderGroundMotions.java +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderGroundMotions.java @@ -2,6 +2,8 @@ package gov.usgs.earthquake.nshmp.netcdf.reader; import java.io.IOException; +import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfData; +import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfDataGroundMotions; import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfShape; import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfShape.IndexKey; import gov.usgs.earthquake.nshmp.netcdf.reader.NetcdfUtils.Key; @@ -29,4 +31,10 @@ public class ReaderGroundMotions extends Reader { .add(IndexKey.SITE_CLASS, vData.findDimensionIndex(Key.SITE_CLASS)) .build(); } + + @Override + NetcdfDataGroundMotions readData(Group targetGroup) throws IOException { + NetcdfData netcdfData = super.readData(targetGroup); + return new NetcdfDataGroundMotions(netcdfData); + } } diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java index bb5bc19870dadd7c7fb864fcc397ef05489106fd..6c204a6e4d628ee365812abd79e3b6a433899d77 100644 --- a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java @@ -3,7 +3,7 @@ package gov.usgs.earthquake.nshmp.netcdf.www; import java.nio.file.Path; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; -import gov.usgs.earthquake.nshmp.netcdf.NetcdfGroundMotions; +import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesGroundMotions; import gov.usgs.earthquake.nshmp.netcdf.www.Metadata.ServiceResponseMetadata; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataSiteClass; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; @@ -43,9 +43,12 @@ public class NetcdfController { @Inject private NshmpMicronautServlet servlet; - @Value("${nshmp-ws-static.netcdf-file}") + @Value("${nshmp-ws-static.netcdf-path}") Path netcdfPath; + @Value("${nshmp-ws-static.aashto-year}") + int aashtoYear; + NetcdfServiceGroundMotions service; /** @@ -53,8 +56,8 @@ public class NetcdfController { */ @EventListener void startup(StartupEvent event) { - var netcdf = new NetcdfGroundMotions(netcdfPath); - service = new NetcdfServiceGroundMotions(netcdf); + var netcdfDataFiles = new NetcdfDataFilesGroundMotions(netcdfPath, aashtoYear); + service = new NetcdfServiceGroundMotions(netcdfDataFiles); } /** diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceGroundMotions.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceGroundMotions.java index e5ddcae46b8b3fa7c96fc3f7fb12ce30ecb06804..3ac06ee66e8e4892f20c5a6af90c003e2d4ccff2 100644 --- a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceGroundMotions.java +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceGroundMotions.java @@ -5,10 +5,12 @@ import java.util.stream.Collectors; import gov.usgs.earthquake.nshmp.data.XySequence; import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesGroundMotions; import gov.usgs.earthquake.nshmp.netcdf.NetcdfGroundMotions; import gov.usgs.earthquake.nshmp.netcdf.NetcdfVersion; import gov.usgs.earthquake.nshmp.netcdf.data.StaticData; import gov.usgs.earthquake.nshmp.netcdf.www.Metadata.ServiceResponseMetadata; +import gov.usgs.earthquake.nshmp.netcdf.www.NetcdfService.SourceModel; import gov.usgs.earthquake.nshmp.netcdf.www.NetcdfWsUtils.Key; import gov.usgs.earthquake.nshmp.netcdf.www.Query.Service; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestData; @@ -26,21 +28,22 @@ import io.micronaut.http.HttpRequest; * * @author U.S. Geological Survey */ -public class NetcdfServiceGroundMotions extends NetcdfService<Query> { +public class NetcdfServiceGroundMotions extends NetcdfService<List<SourceModel>, Query> { static final String SERVICE_DESCRIPTION = "Get static ground motions from a NetCDF file"; static final String X_LABEL = "Period (s)"; static final String Y_LABEL = "Spectral Acceleration (g)"; - public NetcdfServiceGroundMotions(NetcdfGroundMotions netcdf) { - super(netcdf); + public NetcdfServiceGroundMotions(NetcdfDataFilesGroundMotions netcdfDataFiles) { + super(netcdfDataFiles); } @Override - ResponseBody<String, Metadata<Query>> getMetadataResponse(HttpRequest<?> request) { - var metadata = new Metadata<Query>(request, this, SERVICE_DESCRIPTION); + ResponseBody<String, Metadata<List<SourceModel>, Query>> getMetadataResponse( + HttpRequest<?> request) { + var metadata = new Metadata<List<SourceModel>, Query>(request, this, SERVICE_DESCRIPTION); - return ResponseBody.<String, Metadata<Query>> usage() + return ResponseBody.<String, Metadata<List<SourceModel>, Query>> usage() .metadata(new ResponseMetadata(NetcdfVersion.appVersions())) .name(getServiceName()) .request(NetcdfWsUtils.getRequestUrl(request)) @@ -49,19 +52,24 @@ public class NetcdfServiceGroundMotions extends NetcdfService<Query> { .build(); } + List<SourceModel> getSourceModels() { + return netcdfDataFiles().stream() + .map(SourceModel::new) + .collect(Collectors.toList()); + } + @Override String getServiceName() { - return getSourceModel().name; + return String.format("AASHTO-%d Web Services", netcdfDataFiles().aashtoYear()); } - @Override - SourceModel getSourceModel() { - return new SourceModel(netcdf()); + NetcdfGroundMotions netcdf(Location location) { + return netcdfDataFiles().netcdf(location); } @Override - NetcdfGroundMotions netcdf() { - return (NetcdfGroundMotions) netcdf; + NetcdfDataFilesGroundMotions netcdfDataFiles() { + return (NetcdfDataFilesGroundMotions) netcdfDataFiles; } @Override @@ -82,14 +90,13 @@ public class NetcdfServiceGroundMotions extends NetcdfService<Query> { } } - @Override ResponseBody<RequestDataSiteClass, ResponseData<ServiceResponseMetadata>> processCurvesSiteClass( RequestDataSiteClass request, String url) { WsUtils.checkValue(Key.LATITUDE, request.latitude); WsUtils.checkValue(Key.LONGITUDE, request.longitude); WsUtils.checkValue(Key.SITE_CLASS, request.siteClass); - var curves = netcdf().staticData(request.site, request.siteClass); + var curves = netcdf(request.site).staticData(request.site, request.siteClass); var responseData = toResponseData(request, curves); return ResponseBody.<RequestDataSiteClass, ResponseData<ServiceResponseMetadata>> success() @@ -101,13 +108,12 @@ public class NetcdfServiceGroundMotions extends NetcdfService<Query> { .build(); } - @Override ResponseBody<RequestData, List<ResponseData<ServiceResponseMetadata>>> processCurves( RequestData request, String url) { WsUtils.checkValue(Key.LATITUDE, request.latitude); WsUtils.checkValue(Key.LONGITUDE, request.longitude); - var curves = netcdf().staticData(request.site); + var curves = netcdf(request.site).staticData(request.site); var responseData = toList(request, curves); return ResponseBody.<RequestData, List<ResponseData<ServiceResponseMetadata>>> success() diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java index 5bd776c0040d70de43e8e21763d8533b1170b53d..727142bac7070531ce86f38c96a1f6046f8adef5 100644 --- a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java @@ -1,9 +1,8 @@ package gov.usgs.earthquake.nshmp.netcdf.www; -import java.io.IOException; import java.nio.file.Path; -import gov.usgs.earthquake.nshmp.netcdf.NetcdfGroundMotions; +import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesGroundMotions; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; import io.micronaut.context.annotation.Value; @@ -34,74 +33,30 @@ public class SwaggerController { @Inject private NshmpMicronautServlet servlet; - @Value("${nshmp-ws-static.netcdf-file}") + @Value("${nshmp-ws-static.netcdf-path}") Path netcdfPath; - NetcdfGroundMotions netcdf; + @Value("${nshmp-ws-static.aashto-year}") + int aashtoYear; + + NetcdfServiceGroundMotions service; /** * Read in data type and return the appropriate service to use. */ @EventListener void startup(StartupEvent event) { - netcdf = new NetcdfGroundMotions(netcdfPath); + var netcdfDataFiles = new NetcdfDataFilesGroundMotions(netcdfPath, aashtoYear); + service = new NetcdfServiceGroundMotions(netcdfDataFiles); } @Get(produces = MediaType.TEXT_EVENT_STREAM) public HttpResponse<String> doGet(HttpRequest<?> request) { try { - var openApi = NetcdfWsUtils.getOpenApi( - request, - netcdf.netcdfData(), - servicePatternSection(request)); - return HttpResponse.ok(Yaml.pretty(openApi)); + SwaggerGroundMotions swagger = new SwaggerGroundMotions(request, service); + return HttpResponse.ok(Yaml.pretty(swagger.updateOpenApi())); } catch (Exception e) { return NetcdfWsUtils.handleError(e, "Swagger", request.getUri().getPath()); } } - - private static String servicePatternSection(HttpRequest<?> request) - throws IOException { - var url = NetcdfWsUtils.getRequestUrl(request); - url = url.endsWith("/swagger") ? url.replace("/swagger", "") : url; - - return new StringBuilder() - .append( - "<details>\n" + - "<summary>Service Call Patterns</summary>\n") - .append( - "### Query Pattern\n" + - - "The query based service call is in the form of:\n" + - - "```text\n" + - url + "/spectra?longitude={number}&latitude={number}\n" + - url + "/spectra?longitude={number}&latitude={number}&siteClass={string}\n" + - "````\n" + - - "Example:\n" + - "```text\n" + - url + "/spectra?longitude=-118&latitude=34\n" + - url + "/spectra?longitude=-118&latitude=34&siteClass=BC\n" + - "```\n") - .append( - "### Slash Pattern\n" + - - "The slash based service call is in the form of:\n" + - - "```text\n" + - url + "/spectra/{longitude}/{latitude}\n" + - url + "/spectra/{longitude}/{latitude}/{siteClass}\n" + - "```\n" + - - "Example:\n" + - - "```text\n" + - url + "/spectra/-118/34\n" + - url + "/spectra/-118/34/BC\n" + - "```\n") - .append("</details>") - .toString(); - } - } diff --git a/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerGroundMotions.java b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerGroundMotions.java new file mode 100644 index 0000000000000000000000000000000000000000..ae1bf7db22fff06c624e1b46a8cacdfa0aa9db35 --- /dev/null +++ b/src/aashto/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerGroundMotions.java @@ -0,0 +1,94 @@ +package gov.usgs.earthquake.nshmp.netcdf.www; + +import gov.usgs.earthquake.nshmp.netcdf.data.ScienceBaseMetadata; + +import io.micronaut.http.HttpRequest; + +/** + * Swagger page updates for ground motion services. + * + * @author U.S. Geological Survey + */ +class SwaggerGroundMotions extends Swagger<NetcdfServiceGroundMotions> { + + SwaggerGroundMotions(HttpRequest<?> request, NetcdfServiceGroundMotions service) { + super(request, service); + } + + @Override + String description() { + StringBuilder builder = new StringBuilder() + .append(serviceInfo()) + .append("\n## " + descriptionHeader() + "\n"); + + service.netcdfDataFiles() + .forEach(netcdf -> { + ScienceBaseMetadata metadata = netcdf.netcdfData().scienceBaseMetadata(); + builder + .append("### " + metadata.label + "\n") + .append(metadata.description + "\n") + .append(parameterSection(netcdf.netcdfData()) + "\n") + .append(scienceBaseSection(metadata) + "\n"); + }); + + return builder.toString(); + } + + @Override + String descriptionHeader() { + return String.format("AASHTO-%d Data Sets", service.netcdfDataFiles().aashtoYear()); + } + + @Override + String serviceInfo() { + return String.format(String.join("", + "Get risk-targeted design response spectra for the %d ", + "editions of American Association of State Highway and Transportation ", + "Officials (AASHTO) bridge design specifications."), + service.netcdfDataFiles().aashtoYear()); + } + + @Override + String servicePatternSection() { + var url = NetcdfWsUtils.getRequestUrl(request); + url = url.endsWith("/swagger") ? url.replace("/swagger", "") : url; + + return new StringBuilder() + .append( + "<details>\n" + + "<summary>Service Call Patterns</summary>\n") + .append( + "### Query Pattern\n" + + + "The query based service call is in the form of:\n" + + + "```text\n" + + url + "/spectra?longitude={number}&latitude={number}\n" + + url + "/spectra?longitude={number}&latitude={number}&siteClass={string}\n" + + "````\n" + + + "Example:\n" + + "```text\n" + + url + "/spectra?longitude=-118&latitude=34\n" + + url + "/spectra?longitude=-118&latitude=34&siteClass=BC\n" + + "```\n") + .append( + "### Slash Pattern\n" + + + "The slash based service call is in the form of:\n" + + + "```text\n" + + url + "/spectra/{longitude}/{latitude}\n" + + url + "/spectra/{longitude}/{latitude}/{siteClass}\n" + + "```\n" + + + "Example:\n" + + + "```text\n" + + url + "/spectra/-118/34\n" + + url + "/spectra/-118/34/BC\n" + + "```\n") + .append("</details>") + .toString(); + } +} diff --git a/src/aashto/src/main/resources/aashto-example.nc b/src/aashto/src/main/resources/aashto-example.nc index 799953a05255d3a6a4918ec11f36372a552a49c7..125a3d19c9b36ed44667ee08d97c8603dc040611 100644 Binary files a/src/aashto/src/main/resources/aashto-example.nc and b/src/aashto/src/main/resources/aashto-example.nc differ diff --git a/src/aashto/src/main/resources/application.yml b/src/aashto/src/main/resources/application.yml index 70d77ecea5826d7592f8c02b83a0eac4b028e1da..2b1921f1539347e1075f8905bce8e8ec1ab430b4 100644 --- a/src/aashto/src/main/resources/application.yml +++ b/src/aashto/src/main/resources/application.yml @@ -16,4 +16,7 @@ micronaut: logger-name: http nshmp-ws-static: - netcdf-file: ${netcdf:src/main/resources/aashto-example.nc} + # AASHTO year to filter NetCDF files by + aashto-year: ${year:2023} + # Path to directory holding NetCDF files + netcdf-path: ${netcdf:src/main/resources} diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFilesHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFilesHazardCurves.java new file mode 100644 index 0000000000000000000000000000000000000000..c429b83ced424369a79c7afcb1cf62254e1054b4 --- /dev/null +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFilesHazardCurves.java @@ -0,0 +1,49 @@ +package gov.usgs.earthquake.nshmp.netcdf; + +import java.nio.file.Path; +import java.util.List; +import java.util.Set; + +import gov.usgs.earthquake.nshmp.gmm.Imt; + +/** + * Read in and parse a single hazard NetCDF data file. + * + * @author U.S. Geological Survey + */ +public class NetcdfDataFilesHazardCurves extends NetcdfDataFiles<NetcdfHazardCurves> { + + public NetcdfDataFilesHazardCurves(Path netcdfPath) { + super(netcdfPath, NetcdfDataType.HAZARD_CURVES); + addAll(readFiles(netcdfPath, dataType())); + } + + /** + * Returns the set of IMTs. + */ + public Set<Imt> imts() { + return Set.copyOf(netcdf().netcdfData().imts()); + } + + /** + * Returns the Netcdf object. + */ + public NetcdfHazardCurves netcdf() { + return stream() + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Data set not found")); + } + + @Override + protected List<NetcdfHazardCurves> readFiles(Path netcdfPath, NetcdfDataType dataType) { + NetcdfDataType fileDataType = NetcdfDataType.getDataType((netcdfPath)); + + if (!fileDataType.equals(dataType)) { + throw new RuntimeException( + String.format("Data type %s of file must be of type %s", fileDataType, dataType)); + } ; + + NetcdfHazardCurves data = new NetcdfHazardCurves(netcdfPath); + return List.of(data); + } +} diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfHazardCurves.java index 4fedc864f22f334c6599266d233e791a589b3003..3f6aa549e01d79fbc318a2777dd24340c7328023 100644 --- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfHazardCurves.java +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfHazardCurves.java @@ -56,7 +56,7 @@ public class NetcdfHazardCurves extends Netcdf<StaticDataHazardCurves> { var group = ncd.getRootGroup(); return new ReaderHazardCurves(group); } catch (IOException e) { - throw new RuntimeException("Could not read Netcdf file [" + netcdfPath + " ]"); + throw new RuntimeException("Could not read Netcdf file [" + netcdfPath + "]"); } } } diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfDataHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfDataHazardCurves.java index af9df24c6ad72820f09624a47ffae0dd24f9c11d..e144ae5476e0a74c35c0af382651830fdc5c8f8d 100644 --- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfDataHazardCurves.java +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfDataHazardCurves.java @@ -2,11 +2,9 @@ package gov.usgs.earthquake.nshmp.netcdf.data; import static com.google.common.base.Preconditions.checkState; -import java.util.List; import java.util.Map; import gov.usgs.earthquake.nshmp.gmm.Imt; -import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; /** * NetCDF data for hazard curves. @@ -17,9 +15,10 @@ public class NetcdfDataHazardCurves extends NetcdfData { private final Map<Imt, double[]> imls; - NetcdfDataHazardCurves(Builder builder) { - super(builder); - imls = builder.imls; + public NetcdfDataHazardCurves(NetcdfData netcdfData, Map<Imt, double[]> imls) { + super(NetcdfData.Builder.copyOf(netcdfData)); + checkState(!imls.isEmpty(), "Must add imls"); + this.imls = imls; } /** @@ -39,63 +38,4 @@ public class NetcdfDataHazardCurves extends NetcdfData { public static Builder builder() { return new Builder(); } - - public static class Builder extends NetcdfData.Builder { - Map<Imt, double[]> imls; - - Builder() { - super(); - } - - public Builder imls(Map<Imt, double[]> imls) { - this.imls = imls; - return this; - } - - @Override - public Builder imts(List<Imt> imts) { - super.imts(imts); - return this; - } - - @Override - public Builder latitudes(double[] latitudes) { - super.latitudes(latitudes); - return this; - } - - @Override - public Builder longitudes(double[] longitudes) { - super.longitudes(longitudes); - return this; - } - - @Override - public Builder scienceBaseMetadata(ScienceBaseMetadata scienceBaseMetadata) { - super.scienceBaseMetadata(scienceBaseMetadata); - return this; - } - - @Override - public Builder siteClasses(List<NehrpSiteClass> siteClasses) { - super.siteClasses(siteClasses); - return this; - } - - @Override - public Builder vs30Map(Map<NehrpSiteClass, Double> vs30Map) { - super.vs30Map(vs30Map); - return this; - } - - public NetcdfDataHazardCurves build() { - checkBuildState(); - return new NetcdfDataHazardCurves(this); - } - - void checkBuildState() { - super.checkBuildState(); - checkState(!imls.isEmpty(), "Must add imls"); - } - } } diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderHazardCurves.java index b90c086be3d7e1cbe9756a2c69b2d58885daea6c..f8531d56ae5533bae0695d3d657f2569f8305b8a 100644 --- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderHazardCurves.java +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/ReaderHazardCurves.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import gov.usgs.earthquake.nshmp.gmm.Imt; +import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfData; import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfDataHazardCurves; import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfShape; import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfShape.IndexKey; @@ -36,21 +37,13 @@ public class ReaderHazardCurves extends Reader { @Override NetcdfDataHazardCurves readData(Group targetGroup) throws IOException { - var coords = super.readData(targetGroup); + NetcdfData netcdfData = super.readData(targetGroup); var vImls = targetGroup.findVariableLocal(Key.IMLS); // get map of IMLs - var imls = mapImls(vImls, coords.imts()); + var imls = mapImls(vImls, netcdfData.imts()); - return NetcdfDataHazardCurves.builder() - .imls(imls) - .imts(coords.imts()) - .latitudes(coords.latitudes()) - .longitudes(coords.longitudes()) - .scienceBaseMetadata(coords.scienceBaseMetadata()) - .siteClasses(coords.siteClasses()) - .vs30Map(coords.vs30Map()) - .build(); + return new NetcdfDataHazardCurves(netcdfData, imls); } @Override diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/HazardMetadata.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/HazardMetadata.java new file mode 100644 index 0000000000000000000000000000000000000000..fe7c8b3683284fed828871d46ff834019ce32ee0 --- /dev/null +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/HazardMetadata.java @@ -0,0 +1,33 @@ +package gov.usgs.earthquake.nshmp.netcdf.www; + +import gov.usgs.earthquake.nshmp.gmm.Imt; +import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; +import gov.usgs.earthquake.nshmp.netcdf.www.Metadata.ServiceResponseMetadata; +import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestData; +import gov.usgs.earthquake.nshmp.netcdf.www.RequestHazardCurves.HazardRequestDataImt; + +/** + * Hazard metadata. + * + * @author U.S. Geological Survey + */ +public class HazardMetadata { + + static class HazardResponseMetadata extends ServiceResponseMetadata { + public final Imt imt; + + HazardResponseMetadata( + NehrpSiteClass siteClass, + Imt imt, + String xLabel, + String yLabel) { + super(siteClass, xLabel, yLabel); + this.imt = imt; + } + + @Override + public HazardRequestDataImt toRequestMetadata(RequestData requestData) { + return new HazardRequestDataImt(requestData.site, siteClass, imt, requestData.format); + } + } +} diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java index f14d8449a2aa35055936926c95369321a10154d7..4e1a688ead05ae7f864cfa4c981a7ad41204a2cb 100644 --- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfController.java @@ -5,10 +5,10 @@ import java.util.List; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; -import gov.usgs.earthquake.nshmp.netcdf.NetcdfHazardCurves; -import gov.usgs.earthquake.nshmp.netcdf.www.Metadata.HazardResponseMetadata; -import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataImt; +import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesHazardCurves; +import gov.usgs.earthquake.nshmp.netcdf.www.HazardMetadata.HazardResponseMetadata; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataSiteClass; +import gov.usgs.earthquake.nshmp.netcdf.www.RequestHazardCurves.HazardRequestDataImt; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; import gov.usgs.earthquake.nshmp.www.ResponseBody; @@ -32,8 +32,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; /** - * Micronaut controller for getting static hazards or ground motions from a - * NetCDF file. + * Micronaut controller for getting static hazards from a NetCDF file. * * @see NetcdfService * @@ -46,7 +45,10 @@ public class NetcdfController { @Inject private NshmpMicronautServlet servlet; - @Value("${nshmp-ws-static.netcdf-file}") + /** + * The path to a hazard NetCDF file + */ + @Value("${nshmp-ws-static.netcdf-path}") Path netcdfPath; NetcdfServiceHazardCurves service; @@ -56,8 +58,8 @@ public class NetcdfController { */ @EventListener void startup(StartupEvent event) { - var netcdfHazard = new NetcdfHazardCurves(netcdfPath); - service = new NetcdfServiceHazardCurves(netcdfHazard); + var netcdfDataFiles = new NetcdfDataFilesHazardCurves(netcdfPath); + service = new NetcdfServiceHazardCurves(netcdfDataFiles); } /** @@ -174,7 +176,8 @@ public class NetcdfController { "...")) }) - @Get(uri = "/{longitude}/{latitude}/{siteClass}{?format}", produces = MediaType.APPLICATION_JSON) + @Get(uri = "/{longitude}/{latitude}/{siteClass}{?format}", + produces = MediaType.APPLICATION_JSON) public HttpResponse<String> doGetSlashBySite( HttpRequest<?> request, @Schema(required = true) @PathVariable Double latitude, @@ -249,6 +252,7 @@ public class NetcdfController { * GET method to return a static curve using URL query. * * @param request The HTTP request + * @param nshm The NSHM to query * @param longitude The longitude of the site * @param latitude Latitude of the site * @param siteClass The site class (optional) @@ -298,7 +302,8 @@ public class NetcdfController { "...")) }) - @Get(uri = "{?longitude,latitude,siteClass,imt,format}", produces = MediaType.APPLICATION_JSON) + @Get(uri = "{?longitude,latitude,siteClass,imt,format}", + produces = MediaType.APPLICATION_JSON) public HttpResponse<String> doGet( HttpRequest<?> request, @Schema(required = true) @QueryValue @Nullable Double longitude, @@ -314,7 +319,7 @@ public class NetcdfController { // For Swagger schema private static class ResponseByImt extends - ResponseBody<RequestDataImt, ResponseData<HazardResponseMetadata>> {} + ResponseBody<HazardRequestDataImt, ResponseData<HazardResponseMetadata>> {} // For Swagger schema private static class ResponseBySiteClass diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java index 831ccfc27c2c2ef6ee691fd0fe6891191bf463fe..6984e675bb8bf2b8232500c46e977c2a94c723e7 100644 --- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfServiceHazardCurves.java @@ -6,16 +6,18 @@ import java.util.stream.Collectors; import gov.usgs.earthquake.nshmp.data.XySequence; import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.gmm.Imt; +import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesHazardCurves; import gov.usgs.earthquake.nshmp.netcdf.NetcdfHazardCurves; import gov.usgs.earthquake.nshmp.netcdf.NetcdfVersion; import gov.usgs.earthquake.nshmp.netcdf.data.StaticData; import gov.usgs.earthquake.nshmp.netcdf.data.StaticDataHazardCurves; -import gov.usgs.earthquake.nshmp.netcdf.www.Metadata.HazardResponseMetadata; +import gov.usgs.earthquake.nshmp.netcdf.www.HazardMetadata.HazardResponseMetadata; +import gov.usgs.earthquake.nshmp.netcdf.www.NetcdfService.SourceModel; import gov.usgs.earthquake.nshmp.netcdf.www.NetcdfWsUtils.Key; import gov.usgs.earthquake.nshmp.netcdf.www.Query.Service; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestData; -import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataImt; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataSiteClass; +import gov.usgs.earthquake.nshmp.netcdf.www.RequestHazardCurves.HazardRequestDataImt; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ResponseMetadata; import gov.usgs.earthquake.nshmp.www.WsUtils; @@ -29,20 +31,22 @@ import io.micronaut.http.HttpRequest; * * @author U.S. Geological Survey */ -public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { +public class NetcdfServiceHazardCurves extends + NetcdfService<NetcdfServiceHazardCurves.HazardSourceModel, HazardQuery> { static final String SERVICE_DESCRIPTION = "Get static hazard curves from a NetCDF file"; static final String X_LABEL = "Ground Motion (g)"; static final String Y_LABEL = "Annual Frequency of Exceedence"; - public NetcdfServiceHazardCurves(NetcdfHazardCurves netcdf) { - super(netcdf); + public NetcdfServiceHazardCurves(NetcdfDataFilesHazardCurves netcdfDataFiles) { + super(netcdfDataFiles); } @Override - ResponseBody<String, Metadata<HazardQuery>> getMetadataResponse(HttpRequest<?> request) { - var metadata = new Metadata<HazardQuery>(request, this, SERVICE_DESCRIPTION); - return ResponseBody.<String, Metadata<HazardQuery>> usage() + ResponseBody<String, Metadata<HazardSourceModel, HazardQuery>> getMetadataResponse( + HttpRequest<?> request) { + var metadata = new Metadata<HazardSourceModel, HazardQuery>(request, this, SERVICE_DESCRIPTION); + return ResponseBody.<String, Metadata<HazardSourceModel, HazardQuery>> usage() .metadata(new ResponseMetadata(NetcdfVersion.appVersions())) .name(getServiceName()) .request(NetcdfWsUtils.getRequestUrl(request)) @@ -64,20 +68,29 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { @Override String getServiceName() { - return getSourceModel().name; + return netcdfDataFiles().netcdf().netcdfData().scienceBaseMetadata().label; } @Override - SourceModel getSourceModel() { - return new HazardSourceModel(); + HazardSourceModel getSourceModels() { + return new HazardSourceModel(netcdfDataFiles().netcdf()); } - @Override + /** + * Returns the {@link NetcdfHazardCurves} associated with a given {@link Nshm + * NSHM}. + * + * @param nshm The NSHM to get the NetCDF data + */ NetcdfHazardCurves netcdf() { - return (NetcdfHazardCurves) netcdf; + return netcdfDataFiles().netcdf(); } @Override + NetcdfDataFilesHazardCurves netcdfDataFiles() { + return (NetcdfDataFilesHazardCurves) netcdfDataFiles; + } + ResponseBody<RequestData, List<List<ResponseData<HazardResponseMetadata>>>> processCurves( RequestData request, String url) { @@ -86,7 +99,8 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { var curves = netcdf().staticData(request.site); var curvesAsList = toList(request, curves); - return ResponseBody.<RequestData, List<List<ResponseData<HazardResponseMetadata>>>> success() + return ResponseBody + .<RequestData, List<List<ResponseData<HazardResponseMetadata>>>> success() .metadata(new ResponseMetadata(NetcdfVersion.appVersions())) .name(getServiceName()) .request(request) @@ -95,7 +109,6 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { .build(); } - @Override ResponseBody<RequestDataSiteClass, List<ResponseData<HazardResponseMetadata>>> processCurvesSiteClass( RequestDataSiteClass request, String url) { @@ -105,7 +118,8 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { var curves = netcdf().staticData(request.site, request.siteClass); var curvesAsList = toList(request, curves); - return ResponseBody.<RequestDataSiteClass, List<ResponseData<HazardResponseMetadata>>> success() + return ResponseBody + .<RequestDataSiteClass, List<ResponseData<HazardResponseMetadata>>> success() .metadata(new ResponseMetadata(NetcdfVersion.appVersions())) .name(getServiceName()) .request(request) @@ -115,7 +129,7 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { } ResponseBody<RequestDataSiteClass, ResponseData<HazardResponseMetadata>> processCurvesImt( - RequestDataImt request, + HazardRequestDataImt request, String url) { WsUtils.checkValue(Key.LATITUDE, request.latitude); WsUtils.checkValue(Key.LONGITUDE, request.longitude); @@ -143,10 +157,12 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { var requestData = new RequestData(site, query.format); return toResponseFromListOfList(processCurves(requestData, url)); case CURVES_BY_SITE_CLASS: - var requestDataSiteClass = new RequestDataSiteClass(site, query.siteClass, query.format); + var requestDataSiteClass = + new RequestDataSiteClass(site, query.siteClass, query.format); return toResponseFromList(processCurvesSiteClass(requestDataSiteClass, url)); case CURVES_BY_IMT: - var requestDataImt = new RequestDataImt(site, query.siteClass, query.imt, query.format); + var requestDataImt = + new HazardRequestDataImt(site, query.siteClass, query.imt, query.format); return toResponse(processCurvesImt(requestDataImt, url)); default: throw new RuntimeException("Netcdf service [" + service + "] not found"); @@ -184,7 +200,8 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { return curves.entrySet().stream() .map(entry -> { var request = - new RequestDataSiteClass(requestData.site, entry.getKey(), requestData.format); + new RequestDataSiteClass(requestData.site, entry.getKey(), + requestData.format); return toList(request, entry.getValue()); }) .collect(Collectors.toList()); @@ -199,12 +216,12 @@ public class NetcdfServiceHazardCurves extends NetcdfService<HazardQuery> { return new ResponseData<>(metadata, curves); } - class HazardSourceModel extends SourceModel { + static class HazardSourceModel extends SourceModel { public final List<Imt> imts; - HazardSourceModel() { - super(netcdf()); - imts = netcdf().netcdfData().imts().stream().sorted().collect(Collectors.toList()); + HazardSourceModel(NetcdfHazardCurves netcdf) { + super(netcdf); + imts = netcdf.netcdfData().imts().stream().sorted().collect(Collectors.toList()); } } } diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/RequestHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/RequestHazardCurves.java new file mode 100644 index 0000000000000000000000000000000000000000..1332d2cbd156dc224a655a78856b75bd9035c321 --- /dev/null +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/RequestHazardCurves.java @@ -0,0 +1,41 @@ +package gov.usgs.earthquake.nshmp.netcdf.www; + +import java.util.List; + +import gov.usgs.earthquake.nshmp.Text; +import gov.usgs.earthquake.nshmp.Text.Delimiter; +import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.gmm.Imt; +import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; +import gov.usgs.earthquake.nshmp.netcdf.reader.NetcdfUtils.Key; +import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataSiteClass; + +/** + * Request data for hazard services. + * + * @author U.S. Geological Survey + */ +public class RequestHazardCurves { + + /** + * Request data with site class and imt + */ + static class HazardRequestDataImt extends RequestDataSiteClass { + public Imt imt; + + HazardRequestDataImt( + Location site, + NehrpSiteClass siteClass, + Imt imt, + ResponseFormat format) { + super(site, siteClass, format); + this.imt = imt; + } + + @Override + public String toCsv() { + return String.format("%s,%s", + super.toCsv(), Text.join(List.of(Key.IMT.toString(), imt.name()), Delimiter.COMMA)); + } + } +} diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java index ac31adec41786e82aabe251c57e2f0fa945abfc6..40b1f2714a7b2360df33b50ab22241ba65a90987 100644 --- a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerController.java @@ -1,11 +1,9 @@ package gov.usgs.earthquake.nshmp.netcdf.www; -import java.io.IOException; import java.nio.file.Path; -import gov.usgs.earthquake.nshmp.netcdf.NetcdfHazardCurves; +import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFilesHazardCurves; import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; -import gov.usgs.earthquake.nshmp.www.SwaggerUtils; import io.micronaut.context.annotation.Value; import io.micronaut.context.event.StartupEvent; @@ -35,79 +33,27 @@ public class SwaggerController { @Inject private NshmpMicronautServlet servlet; - @Value("${nshmp-ws-static.netcdf-file}") + @Value("${nshmp-ws-static.netcdf-path}") Path netcdfPath; - NetcdfHazardCurves netcdf; + NetcdfServiceHazardCurves service; /** * Read in data type and return the appropriate service to use. */ @EventListener void startup(StartupEvent event) { - netcdf = new NetcdfHazardCurves(netcdfPath); + var netcdfDataFiles = new NetcdfDataFilesHazardCurves(netcdfPath); + service = new NetcdfServiceHazardCurves(netcdfDataFiles); } @Get(produces = MediaType.TEXT_EVENT_STREAM) public HttpResponse<String> doGet(HttpRequest<?> request) { try { - var openApi = NetcdfWsUtils.getOpenApi( - request, - netcdf.netcdfData(), - servicePatternSection(request)); - SwaggerUtils.imtSchema(openApi.getComponents().getSchemas(), netcdf.netcdfData().imts()); - return HttpResponse.ok(Yaml.pretty(openApi)); + SwaggerHazardCurves swagger = new SwaggerHazardCurves(request, service); + return HttpResponse.ok(Yaml.pretty(swagger.updateOpenApi())); } catch (Exception e) { return NetcdfWsUtils.handleError(e, "Swagger", request.getUri().getPath()); } } - - private static String servicePatternSection(HttpRequest<?> request) - throws IOException { - var url = NetcdfWsUtils.getRequestUrl(request); - url = url.endsWith("/swagger") ? url.replace("/swagger", "") : url; - - return new StringBuilder() - .append( - "<details>\n" + - "<summary>Service Call Patterns</summary>\n") - .append( - "### Query Pattern\n" + - - "The query based service call is in the form of:\n" + - - "```text\n" + - url + "/hazard?longitude={number}&latitude={number}\n" + - url + "/hazard?longitude={number}&latitude={number}&siteClass={string}\n" + - url + - "/hazard?longitude={number}&latitude={number}&siteClass={string}&imt={string}\n" + - "````\n" + - - "Example:\n" + - "```text\n" + - url + "/hazard?longitude=-118&latitude=34\n" + - url + "/hazard?longitude=-118&latitude=34&siteClass=BC\n" + - url + "/hazard?longitude=-118&latitude=34&siteClass=BC&imt=PGA\n" + - "```\n") - .append( - "### Slash Pattern\n" + - - "The slash based service call is in the form of:\n" + - - "```text\n" + - url + "/hazard/{longitude}/{latitude}\n" + - url + "/hazard/{longitude}/{latitude}/{siteClass}\n" + - url + "/hazard/{longitude}/{latitude}/{siteClass}/{imt}\n" + - "```\n" + - - "Example:\n" + - - "```text\n" + - url + "/hazard/-118/34\n" + - url + "/hazard/-118/34/BC\n" + - url + "/hazard/-118/34/BC/PGA\n" + - "```\n") - .append("</details>") - .toString(); - } } diff --git a/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerHazardCurves.java b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerHazardCurves.java new file mode 100644 index 0000000000000000000000000000000000000000..64e880c9421b958b413f53fa8e46b2d42a45fb7f --- /dev/null +++ b/src/hazard/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/SwaggerHazardCurves.java @@ -0,0 +1,108 @@ +package gov.usgs.earthquake.nshmp.netcdf.www; + +import java.io.IOException; +import java.util.List; + +import gov.usgs.earthquake.nshmp.netcdf.data.ScienceBaseMetadata; +import gov.usgs.earthquake.nshmp.www.SwaggerUtils; + +import io.micronaut.http.HttpRequest; +import io.swagger.v3.oas.models.OpenAPI; + +/** + * Swagger page updates for hazard services. + * + * @author U.S. Geological Survey + */ +public class SwaggerHazardCurves extends Swagger<NetcdfServiceHazardCurves> { + + SwaggerHazardCurves(HttpRequest<?> request, NetcdfServiceHazardCurves service) { + super(request, service); + } + + @Override + String description() { + StringBuilder builder = new StringBuilder(); + + service.netcdfDataFiles() + .forEach(netcdf -> { + ScienceBaseMetadata metadata = netcdf.netcdfData().scienceBaseMetadata(); + builder + .append(metadata.description + "\n") + .append(parameterSection(netcdf.netcdfData()) + "\n") + .append(scienceBaseSection(metadata) + "\n"); + }); + + return builder.toString(); + } + + @Override + String descriptionHeader() { + return ""; + } + + @Override + String serviceInfo() { + return ""; + } + + @Override + String servicePatternSection() { + var url = NetcdfWsUtils.getRequestUrl(request); + url = url.endsWith("/swagger") ? url.replace("/swagger", "") : url; + + return new StringBuilder() + .append( + "<details>\n" + + "<summary>Service Call Patterns</summary>\n") + .append( + "### Query Pattern\n" + + + "The query based service call is in the form of:\n" + + + "```text\n" + + url + "/hazard?longitude={number}&latitude={number}\n" + + url + "/hazard?longitude={number}&latitude={number}&siteClass={string}\n" + + url + "/hazard" + + "?longitude={number}&latitude={number}&siteClass={string}&imt={string}\n" + + "````\n" + + + "Example:\n" + + "```text\n" + + url + "/hazard?longitude=-118&latitude=34\n" + + url + "/hazard?longitude=-118&latitude=34&siteClass=BC\n" + + url + "/hazard?longitude=-118&latitude=34&siteClass=BC&imt=PGA\n" + + "```\n") + .append( + "### Slash Pattern\n" + + + "The slash based service call is in the form of:\n" + + + "```text\n" + + url + "/hazard/{longitude}/{latitude}\n" + + url + "/hazard/{longitude}/{latitude}/{siteClass}\n" + + url + "/hazard/{longitude}/{latitude}/{siteClass}/{imt}\n" + + "```\n" + + + "Example:\n" + + + "```text\n" + + url + "/hazard/-118/34\n" + + url + "/hazard/-118/34/BC\n" + + url + "/hazard/-118/34/BC/PGA\n" + + "```\n") + .append("</details>") + .toString(); + } + + @Override + OpenAPI updateOpenApi() throws IOException { + OpenAPI openApi = super.updateOpenApi(); + + SwaggerUtils.imtSchema( + openApi.getComponents().getSchemas(), + List.copyOf(service.netcdfDataFiles().imts())); + + return openApi; + } +} diff --git a/src/hazard/src/main/resources/application.yml b/src/hazard/src/main/resources/application.yml index 006db385cd7a52c1b5aec2c514fb18b6b591dd12..eb7e65e318252797d30050f75cb61f70d7ce8688 100644 --- a/src/hazard/src/main/resources/application.yml +++ b/src/hazard/src/main/resources/application.yml @@ -16,4 +16,5 @@ micronaut: logger-name: http nshmp-ws-static: - netcdf-file: ${netcdf:src/main/resources/hazard-example.nc} + # Path to hazard NetCDF file + netcdf-path: ${netcdf:src/main/resources/hazard-example.nc} diff --git a/src/hazard/src/main/resources/hazard-example.nc b/src/hazard/src/main/resources/hazard-example.nc index ffdecae92f16cb6c34404bea8e91d41e482712d3..1969150bcb1f553bd4e701ed35ea61c4fd8a96f9 100644 Binary files a/src/hazard/src/main/resources/hazard-example.nc and b/src/hazard/src/main/resources/hazard-example.nc differ diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/Netcdf.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/Netcdf.java index 5c13df52084f3a2bcac317f429588f895edd4007..b18e4c5a77aa49d14655f0a9ce0a953a002a3538 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/Netcdf.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/Netcdf.java @@ -18,10 +18,9 @@ import gov.usgs.earthquake.nshmp.netcdf.reader.Reader; * * @author U.S. Geological Survey */ -public abstract class Netcdf<T> { +public abstract class Netcdf<T> implements Comparable<Netcdf<T>> { protected final Path netcdfPath; - protected final NetcdfDataType dataType; protected final NetcdfData netcdfData; protected NetcdfShape netcdfShape; @@ -39,7 +38,12 @@ public abstract class Netcdf<T> { throw new IllegalArgumentException("Path to Netcdf file [" + netcdfPath + "] does not exist"); } - dataType = NetcdfDataType.getDataType(netcdfPath); + Path fileName = netcdfPath.getFileName(); + + if (fileName == null || !fileName.toString().endsWith(".nc")) { + throw new IllegalArgumentException("NetCDF file not found (.nc) " + netcdfPath); + } + var reader = getNetcdfData(netcdfPath); netcdfData = reader.netcdfData(); netcdfShape = reader.netcdfShape(); @@ -52,13 +56,6 @@ public abstract class Netcdf<T> { */ public abstract BoundingData<T> boundingData(Location site); - /** - * Returns the data type. - */ - public NetcdfDataType dataType() { - return dataType; - } - /** * Returns the NetCDF data. */ @@ -93,5 +90,11 @@ public abstract class Netcdf<T> { */ public abstract T staticData(Location site, NehrpSiteClass siteClass); + @Override + public int compareTo(Netcdf<T> that) { + return this.netcdfData().scienceBaseMetadata().label + .compareTo(that.netcdfData().scienceBaseMetadata().label); + } + abstract Reader getNetcdfData(Path netcdfPath); } diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFiles.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFiles.java new file mode 100644 index 0000000000000000000000000000000000000000..18c1690d657d41f7c2695e28029a19aa3095bae0 --- /dev/null +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataFiles.java @@ -0,0 +1,76 @@ +package gov.usgs.earthquake.nshmp.netcdf; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; + +/** + * Read in all NetCDF files associated with a {@link NetcdfDataType}, holds + * {@link Netcdf} objects associated with each NetCDF file. + * + * @author U.S. Geological Survey + */ +public abstract class NetcdfDataFiles<T extends Netcdf<?>> extends ArrayList<T> { + private final Path netcdfPath; + private final NetcdfDataType dataType; + + public NetcdfDataFiles(Path netcdfPath, NetcdfDataType dataType) { + super(); + this.netcdfPath = netcdfPath; + this.dataType = dataType; + } + + /** + * Returns the path to the directory where all NetCDF files are. + */ + public Path netcdfPath() { + return netcdfPath; + } + + /** + * Returns the data type of the NetCDF files. + */ + public NetcdfDataType dataType() { + return dataType; + } + + /** + * Returns the set of NEHRP site classes for all data files. + */ + public Set<NehrpSiteClass> siteClasses() { + return stream() + .flatMap(netcdf -> netcdf.netcdfData().siteClasses().stream()) + .sorted() + .collect(Collectors.toSet()); + } + + /** + * Find NetCDF files (.nc) with specific {@link NetcdfDataType}. + * + * @param netcdfPath Path to NetCDF files + * @param dataType The data type to filter by + * @throws IOException + */ + protected Stream<Path> walkFiles(Path netcdfPath, NetcdfDataType dataType) throws IOException { + return Files.walk(netcdfPath) + .filter(file -> file.getFileName().toString().endsWith(".nc")) + .filter(file -> NetcdfDataType.getDataType(file) == dataType); + } + + /** + * Returns the list of {@link Netcdf} objects associated with a NetCDF file + * and {@link NetcdfDataType}. + * + * @param netcdfPath The path to NetCDF files + * @param dataType The data type to read in + * @throws IOException + */ + protected abstract List<T> readFiles(Path netcdfPath, NetcdfDataType dataType); +} diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataType.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataType.java index d7bb13c55cb393d6fad60b864e903487580fee39..11196fac65c5b1306db819dbdff68bac8c1bb7cf 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataType.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/NetcdfDataType.java @@ -1,12 +1,10 @@ package gov.usgs.earthquake.nshmp.netcdf; -import java.io.IOException; import java.nio.file.Path; +import gov.usgs.earthquake.nshmp.netcdf.reader.NetcdfUtils; import gov.usgs.earthquake.nshmp.netcdf.reader.NetcdfUtils.Key; -import ucar.nc2.dataset.NetcdfDatasets; - /** * Supported NetCDF data types. */ @@ -21,12 +19,7 @@ public enum NetcdfDataType { * @param netcdfPath Path to NetCDF file */ public static NetcdfDataType getDataType(Path netcdfPath) { - try (var ncd = NetcdfDatasets.openDataset(netcdfPath.toString())) { - var group = ncd.getRootGroup(); - var vDataType = group.attributes().findAttribute(Key.DATA_TYPE); - return NetcdfDataType.valueOf(vDataType.getStringValue()); - } catch (IOException e) { - throw new RuntimeException("Could not read Netcdf file [" + netcdfPath + " ]"); - } + return NetcdfDataType + .valueOf(NetcdfUtils.readAttribute(Key.DATA_TYPE, netcdfPath).getStringValue()); } } diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfData.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfData.java index 7037e9c14ac8244250af619ee528dea81b55aa8c..f113edc7ed945a72b03077e27fd789063d181a7c 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfData.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/data/NetcdfData.java @@ -118,6 +118,16 @@ public class NetcdfData { Builder() {} + public static Builder copyOf(NetcdfData netcdfData) { + return builder() + .imts(netcdfData.imts) + .latitudes(netcdfData.latitudes) + .longitudes(netcdfData.longitudes) + .scienceBaseMetadata(netcdfData.scienceBaseMetadata) + .siteClasses(netcdfData.siteClasses) + .vs30Map(netcdfData.vs30Map); + } + public Builder imts(List<Imt> imts) { this.imts = imts; return this; @@ -162,5 +172,4 @@ public class NetcdfData { checkState(!vs30Map.isEmpty(), "Must add vs30s"); } } - } diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java index 186b1465c5508df884a450b96ae955b084fa50e4..8ccccd717b07e78844f146cb2b44fc2188819ba4 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/reader/NetcdfUtils.java @@ -4,6 +4,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import java.io.IOException; +import java.nio.file.Path; import java.util.Arrays; import com.google.common.math.DoubleMath; @@ -13,7 +14,10 @@ import gov.usgs.earthquake.nshmp.data.XySequence; import gov.usgs.earthquake.nshmp.geo.LocationList; import ucar.ma2.DataType; +import ucar.nc2.Attribute; import ucar.nc2.Group; +import ucar.nc2.dataset.NetcdfDataset; +import ucar.nc2.dataset.NetcdfDatasets; public class NetcdfUtils { @@ -190,6 +194,15 @@ public class NetcdfUtils { return (String[]) get1DArray(group, key, DataType.STRING); } + public static Attribute readAttribute(String attributeKey, Path netcdfPath) { + try (NetcdfDataset ncd = NetcdfDatasets.openDataset(netcdfPath.toString())) { + Group group = ncd.getRootGroup(); + return group.attributes().findAttribute(attributeKey); + } catch (IOException e) { + throw new RuntimeException("Could not read Netcdf file [" + netcdfPath + " ]"); + } + } + public static class Key { public static final String DESCRIPTION = "description"; public static final String GRID_STEP = "gridStep"; diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Metadata.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Metadata.java index c228a0b1223ea92d2b75d1ed60b817ab0d4e7e53..a3485d039a5eff1015376e8b1f67c63f9d98ae58 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Metadata.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Metadata.java @@ -1,12 +1,7 @@ package gov.usgs.earthquake.nshmp.netcdf.www; -import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; -import gov.usgs.earthquake.nshmp.netcdf.Netcdf; -import gov.usgs.earthquake.nshmp.netcdf.data.ScienceBaseMetadata; -import gov.usgs.earthquake.nshmp.netcdf.www.NetcdfService.SourceModel; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestData; -import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataImt; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataSiteClass; import gov.usgs.earthquake.nshmp.netcdf.www.meta.DoubleParameter; @@ -17,17 +12,13 @@ import io.micronaut.http.HttpRequest; * * @author U.S. Geological Survey */ -public class Metadata<T extends Query> { +public class Metadata<S, T extends Query> { public final String description; public final String[] syntax; - public final SourceModel model; - public final DoubleParameter longitude; - public final DoubleParameter latitude; + public final S models; public final DoubleParameter vs30; - public final NetcdfMetadata netcdfMetadata; - Metadata(HttpRequest<?> request, NetcdfService<T> netcdfService, String description) { - var netcdf = netcdfService.netcdf(); + Metadata(HttpRequest<?> request, NetcdfService<S, T> netcdfService, String description) { var url = request.getUri().toString(); url = url.endsWith("/") ? url.substring(0, url.length() - 1) : url; this.description = description; @@ -38,37 +29,13 @@ public class Metadata<T extends Query> { url + "?longitude={number}&latitude={number}&siteClass={NehrpSiteClass}", }; - var min = netcdf.netcdfData().minimumBounds(); - var max = netcdf.netcdfData().maximumBounds(); + models = netcdfService.getSourceModels(); - longitude = new DoubleParameter( - "Longitude", - "°", - min.longitude, - max.longitude); - latitude = new DoubleParameter( - "Latitude", - "°", - min.latitude, - max.latitude); - model = netcdfService.getSourceModel(); vs30 = new DoubleParameter( "Vs30", "m/s", 150, 1500); - netcdfMetadata = new NetcdfMetadata(netcdf); - } - - class NetcdfMetadata { - public final String netcdfFile; - public final ScienceBaseMetadata scienceBaseMetadata; - - NetcdfMetadata(Netcdf<?> netcdf) { - var fileName = netcdf.netcdfPath().getFileName(); - netcdfFile = fileName == null ? netcdf.netcdfPath().toString() : fileName.toString(); - scienceBaseMetadata = netcdf.netcdfData().scienceBaseMetadata(); - } } interface ServiceMetadata { @@ -94,18 +61,4 @@ public class Metadata<T extends Query> { return new RequestDataSiteClass(requestData.site, siteClass, requestData.format); } } - - static class HazardResponseMetadata extends ServiceResponseMetadata { - public final Imt imt; - - HazardResponseMetadata(NehrpSiteClass siteClass, Imt imt, String xLabel, String yLabel) { - super(siteClass, xLabel, yLabel); - this.imt = imt; - } - - @Override - public RequestDataImt toRequestMetadata(RequestData requestData) { - return new RequestDataImt(requestData.site, siteClass, imt, requestData.format); - } - } } diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfService.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfService.java index 243ab9418da29a3112e98a87576af188613d6c18..9d88e3cfa083f37dae1b03ff0ffc6c271e2a248e 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfService.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfService.java @@ -12,10 +12,12 @@ import gov.usgs.earthquake.nshmp.Text; import gov.usgs.earthquake.nshmp.Text.Delimiter; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; import gov.usgs.earthquake.nshmp.netcdf.Netcdf; +import gov.usgs.earthquake.nshmp.netcdf.NetcdfDataFiles; +import gov.usgs.earthquake.nshmp.netcdf.data.ScienceBaseMetadata; import gov.usgs.earthquake.nshmp.netcdf.www.Metadata.ServiceResponseMetadata; import gov.usgs.earthquake.nshmp.netcdf.www.Query.Service; import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestData; -import gov.usgs.earthquake.nshmp.netcdf.www.Request.RequestDataSiteClass; +import gov.usgs.earthquake.nshmp.netcdf.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.ResponseBody; import io.micronaut.http.HttpRequest; @@ -28,14 +30,14 @@ import io.micronaut.http.HttpResponse; * * @author U.S. Geological Survey */ -public abstract class NetcdfService<T extends Query> { +public abstract class NetcdfService<S, T extends Query> { protected static final Logger LOGGER = Logger.getLogger(NetcdfService.class.getName()); - Netcdf<?> netcdf; + protected NetcdfDataFiles<?> netcdfDataFiles; - protected NetcdfService(Netcdf<?> netcdf) { - this.netcdf = netcdf; + protected NetcdfService(NetcdfDataFiles<?> netcdfDataFiles) { + this.netcdfDataFiles = netcdfDataFiles; } /** @@ -43,42 +45,17 @@ public abstract class NetcdfService<T extends Query> { * * @param httpRequest The HTTP request */ - abstract ResponseBody<String, Metadata<T>> getMetadataResponse(HttpRequest<?> httpRequest); + abstract ResponseBody<String, Metadata<S, T>> getMetadataResponse(HttpRequest<?> httpRequest); /** * Returns the service name */ abstract String getServiceName(); - /** - * Returns the source model - */ - abstract SourceModel getSourceModel(); - /** * Returns the netcdf object associated with the specific data type. */ - abstract Netcdf<?> netcdf(); - - /** - * Returns the static curves at a specific location. - * - * @param <T> The response type - * @param request The request data - * @param url The URL for the service call - */ - abstract <U> ResponseBody<RequestData, U> processCurves(RequestData request, String url); - - /** - * Returns the static curves at a specific location and site class. - * - * @param <T> The response type - * @param request The request data - * @param url The URL for the service call - */ - abstract <U> ResponseBody<RequestDataSiteClass, U> processCurvesSiteClass( - RequestDataSiteClass request, - String url); + abstract NetcdfDataFiles<?> netcdfDataFiles(); /** * Process the service request and returns the string response. @@ -127,6 +104,11 @@ public abstract class NetcdfService<T extends Query> { } } + /** + * Returns the source models. + */ + abstract S getSourceModels(); + <U extends ServiceResponseMetadata> String toCsvResponse( RequestData requestData, ResponseData<U> responseData) { @@ -192,13 +174,41 @@ public abstract class NetcdfService<T extends Query> { return lines.stream().collect(Collectors.joining(Text.NEWLINE)); } + static class NetcdfMetadata { + public final String netcdfFile; + public final ScienceBaseMetadata scienceBaseMetadata; + + NetcdfMetadata(Netcdf<?> netcdf) { + var fileName = netcdf.netcdfPath().getFileName(); + netcdfFile = fileName == null ? netcdf.netcdfPath().toString() : fileName.toString(); + scienceBaseMetadata = netcdf.netcdfData().scienceBaseMetadata(); + } + } + static class SourceModel { public final String name; public final Map<NehrpSiteClass, Double> siteClasses; + public final DoubleParameter longitude; + public final DoubleParameter latitude; + public final NetcdfMetadata netcdfMetadata; SourceModel(Netcdf<?> netcdf) { name = netcdf.netcdfData().scienceBaseMetadata().label; siteClasses = netcdf.netcdfData().vs30Map(); + var min = netcdf.netcdfData().minimumBounds(); + var max = netcdf.netcdfData().maximumBounds(); + + longitude = new DoubleParameter( + "Longitude", + "°", + min.longitude, + max.longitude); + latitude = new DoubleParameter( + "Latitude", + "°", + min.latitude, + max.latitude); + netcdfMetadata = new NetcdfMetadata(netcdf); } } } diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfWsUtils.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfWsUtils.java index c4233ad557e292cf3b4a1fd20eb1a9790c8ac8fb..cc9a932302868e95c509b888fa8855412a56e54c 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfWsUtils.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/NetcdfWsUtils.java @@ -3,29 +3,21 @@ package gov.usgs.earthquake.nshmp.netcdf.www; import static com.google.common.base.CaseFormat.UPPER_CAMEL; import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; -import java.io.IOException; -import java.util.Arrays; import java.util.Optional; import java.util.logging.Logger; -import java.util.stream.Collectors; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.netcdf.NetcdfVersion; -import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfData; -import gov.usgs.earthquake.nshmp.netcdf.data.ScienceBaseMetadata; import gov.usgs.earthquake.nshmp.www.ResponseBody; import gov.usgs.earthquake.nshmp.www.ResponseMetadata; -import gov.usgs.earthquake.nshmp.www.SwaggerUtils; import gov.usgs.earthquake.nshmp.www.WsUtils.EnumSerializer; import gov.usgs.earthquake.nshmp.www.WsUtils.NaNSerializer; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.parser.OpenAPIV3Parser; public class NetcdfWsUtils { static final Gson GSON; @@ -69,33 +61,6 @@ public class NetcdfWsUtils { return HttpResponse.serverError(response); } - public static OpenAPI getOpenApi( - HttpRequest<?> request, - NetcdfData netcdfData, - String serviceSection) throws IOException { - var openApi = new OpenAPIV3Parser().read("META-INF/swagger/nshmp-ws-static.yml"); - var scienceBaseMetadata = netcdfData.scienceBaseMetadata(); - SwaggerUtils.addLocationBounds(openApi, netcdfData.minimumBounds(), netcdfData.maximumBounds()); - var components = openApi.getComponents(); - var schemas = components.getSchemas(); - SwaggerUtils.siteClassSchema(schemas, netcdfData.siteClasses()); - openApi.servers(null); - - openApi.getInfo().setTitle(scienceBaseMetadata.label); - - // Update description - var description = new StringBuilder() - .append(scienceBaseMetadata.description + "\n") - .append(swaggerResponseFormatSection()) - .append(serviceSection) - .append(swaggerParameterSection(netcdfData)) - .append(swaggerScienceBaseSection(scienceBaseMetadata)) - .toString(); - openApi.getInfo().setDescription(description); - - return openApi; - } - public static enum Key { LATITUDE, LONGITUDE, @@ -107,52 +72,4 @@ public class NetcdfWsUtils { return UPPER_UNDERSCORE.to(UPPER_CAMEL, name()); } } - - private static String swaggerParameterSection(NetcdfData netcdfData) { - return new StringBuilder() - .append( - "<details>\n" + - "<summary>Parameters</summary>\n") - .append( - SwaggerUtils.locationBoundsInfo(netcdfData.minimumBounds(), netcdfData.maximumBounds(), - Optional.of("###"))) - .append(SwaggerUtils.siteClassInfo(netcdfData.siteClasses(), Optional.of("###"))) - .append("</details>") - .toString(); - } - - private static String swaggerResponseFormatSection() { - return new StringBuilder() - .append( - "<details>\n" + - "<summary>Response Format: CSV or JSON</summary>\n") - .append( - "The web service can respond in JSON or CSV format.\n" + - "<br><br>" + - "Choose the format type by adding a `format` query to the service call:" + - "`?format=JSON` or `?format=CSV`\n\n" + - "<br><br>" + - "Default: `JSON`") - .append("</details>") - .toString(); - } - - private static String swaggerScienceBaseSection(ScienceBaseMetadata scienceBaseMetadata) { - return new StringBuilder() - .append( - "<details>\n" + - "<summary>ScienceBase Information</summary>\n") - .append("Data history: " + scienceBaseMetadata.history) - .append("<br><br>") - .append( - String.format( - "Returned data are spatially interpolated from " + - "the %s grid data from ScienceBase:\n", - scienceBaseMetadata.gridStep)) - .append(Arrays.stream(scienceBaseMetadata.scienceBaseInfo) - .map(info -> String.format("- [%s](%s)", info.url, info.url)) - .collect(Collectors.joining("\n"))) - .append("</details>") - .toString(); - } } diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Request.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Request.java index eb193cbb2c40d17be105336c9a1f8610b185b5cc..af4452de0471c9bee952929e6ae75df963df0556 100644 --- a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Request.java +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Request.java @@ -5,7 +5,6 @@ import java.util.List; import gov.usgs.earthquake.nshmp.Text; import gov.usgs.earthquake.nshmp.Text.Delimiter; import gov.usgs.earthquake.nshmp.geo.Location; -import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; import gov.usgs.earthquake.nshmp.netcdf.www.NetcdfWsUtils.Key; @@ -46,24 +45,6 @@ public class Request { } } - /** - * Request data with site class and imt - */ - static class RequestDataImt extends RequestDataSiteClass { - public Imt imt; - - RequestDataImt(Location site, NehrpSiteClass siteClass, Imt imt, ResponseFormat format) { - super(site, siteClass, format); - this.imt = imt; - } - - @Override - public String toCsv() { - return String.format("%s,%s", - super.toCsv(), Text.join(List.of(Key.IMT.toString(), imt.name()), Delimiter.COMMA)); - } - } - /** * Request data with site class */ diff --git a/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Swagger.java b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Swagger.java new file mode 100644 index 0000000000000000000000000000000000000000..400e5142f06caf3d32f5e622fd09744e6cf9c6a9 --- /dev/null +++ b/src/lib/src/main/java/gov/usgs/earthquake/nshmp/netcdf/www/Swagger.java @@ -0,0 +1,157 @@ +package gov.usgs.earthquake.nshmp.netcdf.www; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import gov.usgs.earthquake.nshmp.geo.Bounds; +import gov.usgs.earthquake.nshmp.geo.Location; +import gov.usgs.earthquake.nshmp.geo.LocationList; +import gov.usgs.earthquake.nshmp.netcdf.data.NetcdfData; +import gov.usgs.earthquake.nshmp.netcdf.data.ScienceBaseMetadata; +import gov.usgs.earthquake.nshmp.www.SwaggerUtils; + +import io.micronaut.http.HttpRequest; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.parser.OpenAPIV3Parser; + +/** + * Update Swagger landing page. + * + * @author U.S. Geological Survey + */ +abstract class Swagger<T extends NetcdfService<?, ?>> { + + final HttpRequest<?> request; + final T service; + + Swagger(HttpRequest<?> request, T service) { + this.request = request; + this.service = service; + } + + /** + * Returns the main description header text. + */ + abstract String descriptionHeader(); + + /** + * Returns the service info text. + */ + abstract String serviceInfo(); + + /** + * Returns the service patterns sections + */ + abstract String servicePatternSection(); + + /** + * Creates the main swagger description section. + * + * @param netcdfDataFiles The data files + */ + abstract String description(); + + /** + * Creates the parameter section for the description. + * + * @param netcdfData The NetCDF data + */ + String parameterSection(NetcdfData netcdfData) { + return new StringBuilder() + .append( + "<details>\n" + + "<summary>Parameters</summary>\n") + .append( + SwaggerUtils.locationBoundsInfo(netcdfData.minimumBounds(), netcdfData.maximumBounds(), + Optional.of("###"))) + .append(SwaggerUtils.siteClassInfo(netcdfData.siteClasses(), Optional.of("###"))) + .append("</details>") + .toString(); + } + + /** + * Creates the response format section for the description. + */ + String responseFormatSection() { + return new StringBuilder() + .append( + "<details>\n" + + "<summary>Response Format: CSV or JSON</summary>\n") + .append( + "The web service can respond in JSON or CSV format.\n" + + "<br><br>" + + "Choose the format type by adding a `format` query to the service call:" + + "`?format=JSON` or `?format=CSV`\n\n" + + "<br><br>" + + "Default: `JSON`") + .append("</details>") + .toString(); + } + + /** + * Creates the science base section for the description. + * + * @param scienceBaseMetadata The metadata. + */ + String scienceBaseSection(ScienceBaseMetadata scienceBaseMetadata) { + return new StringBuilder() + .append( + "<details>\n" + + "<summary>ScienceBase Information</summary>\n") + .append("Data history: " + scienceBaseMetadata.history) + .append("<br><br>") + .append( + String.format( + "Returned data are spatially interpolated from " + + "the %s grid data from ScienceBase:\n", + scienceBaseMetadata.gridStep)) + .append(Arrays.stream(scienceBaseMetadata.scienceBaseInfo) + .map(info -> String.format("- [%s](%s)", info.url, info.url)) + .collect(Collectors.joining("\n"))) + .append("</details>") + .toString(); + } + + void updateLocationBounds(OpenAPI openApi) { + List<Location> locations = service.netcdfDataFiles().stream() + .map(netcdf -> netcdf.netcdfData()) + .flatMap( + netcdfData -> List.of(netcdfData.minimumBounds(), netcdfData.maximumBounds()).stream()) + .collect(Collectors.toList()); + + Bounds bounds = LocationList.copyOf(locations).bounds(); + SwaggerUtils.addLocationBounds(openApi, bounds.min, bounds.max); + } + + /** + * Update the {@link OpenAPI} documentation. + * + * @throws IOException + */ + OpenAPI updateOpenApi() throws IOException { + OpenAPI openApi = new OpenAPIV3Parser().read("META-INF/swagger/nshmp-ws-static.yml"); + updateLocationBounds(openApi); + Components components = openApi.getComponents(); + Map<String, Schema> schemas = components.getSchemas(); + SwaggerUtils.siteClassSchema(schemas, List.copyOf(service.netcdfDataFiles().siteClasses())); + openApi.servers(null); + + openApi.getInfo().setTitle(service.getServiceName()); + + // Update description + String description = new StringBuilder() + .append(description()) + .append("## Formating \n" + responseFormatSection() + "\n") + .append("## Service Patterns \n" + servicePatternSection()) + .toString(); + openApi.getInfo().setDescription(description); + + return openApi; + } +}