diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServicesUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java similarity index 88% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/ServicesUtil.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java index b2da3f0917178c67b8acb98f9da86f899e1ec652..0da1909789abb10235cbf9f1116ee2b088a819fb 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServicesUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServicesUtil.java @@ -1,4 +1,4 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -11,9 +11,6 @@ import gov.usgs.earthquake.nshmp.calc.Hazard; import gov.usgs.earthquake.nshmp.calc.HazardCalcs; import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.model.HazardModel; -import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.WsUtils; - import io.micronaut.http.HttpResponse; public class ServicesUtil { @@ -35,7 +32,8 @@ public class ServicesUtil { return HttpResponse.serverError(response); } - static Hazard calcHazard( + @Deprecated + public static Hazard calcHazard( Function<HazardModel, CalcConfig> configFunction, Function<CalcConfig, Site> siteFunction) throws InterruptedException, ExecutionException { @@ -47,12 +45,12 @@ public class ServicesUtil { } @Deprecated - static class ServiceQueryData implements ServiceQuery { + public static class ServiceQueryData implements ServiceQuery { public final Double longitude; public final Double latitude; - ServiceQueryData(Double longitude, Double latitude) { + public ServiceQueryData(Double longitude, Double latitude) { this.longitude = longitude; this.latitude = latitude; } @@ -70,7 +68,7 @@ public class ServicesUtil { } @Deprecated - static class ServiceRequestData { + public static class ServiceRequestData { public final double longitude; public final double latitude; @@ -81,7 +79,7 @@ public class ServicesUtil { } } - enum Key { + public enum Key { EDITION, REGION, MODEL, @@ -114,6 +112,7 @@ public class ServicesUtil { void checkValues(); } + @Deprecated private static CompletableFuture<Hazard> calcHazard( HazardModel model, CalcConfig config, diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServletUtil.java b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java similarity index 94% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/ServletUtil.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java index 81651afd7d44ac3d968008892b7e7d1dc03700eb..3de64116aa99b3bfacebdbc762657fc9bccd3a13 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/ServletUtil.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/ServletUtil.java @@ -1,4 +1,4 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www; import static java.lang.Runtime.getRuntime; @@ -27,9 +27,7 @@ import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.calc.ValueFormat; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.model.HazardModel; -import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.MetaUtil; - import io.micronaut.context.annotation.Value; import io.micronaut.context.event.ShutdownEvent; import io.micronaut.context.event.StartupEvent; @@ -46,10 +44,10 @@ public class ServletUtil { public static final Gson GSON; - static final ListeningExecutorService CALC_EXECUTOR; - static final ExecutorService TASK_EXECUTOR; + public static final ListeningExecutorService CALC_EXECUTOR; + public static final ExecutorService TASK_EXECUTOR; - static final int THREAD_COUNT; + public static final int THREAD_COUNT; @Value("${nshmp-haz.model-path}") private Path modelPath; @@ -73,7 +71,7 @@ public class ServletUtil { .create(); } - static HazardModel model() { + public static HazardModel model() { return HAZARD_MODEL; } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java index c2251caba5d3ed3612ed43e1aa8ca14404f7f381..6cd6d6d04a827c18eae70c9852878e5f5adf833d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/SwaggerController.java @@ -5,8 +5,6 @@ import java.util.stream.Collectors; import com.google.common.io.Resources; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.MediaType; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/HazardController.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java similarity index 63% rename from src/main/java/gov/usgs/earthquake/nshmp/www/HazardController.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java index fdc74747815374a10fb5977cf022ceb16a05319a..54c765def174142b7456525caa90fb62160be1fe 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/HazardController.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardController.java @@ -1,8 +1,7 @@ -package gov.usgs.earthquake.nshmp.www; - -import gov.usgs.earthquake.nshmp.www.services.HazardService; -import gov.usgs.earthquake.nshmp.www.services.HazardService.QueryParameters; +package gov.usgs.earthquake.nshmp.www.hazard; +import gov.usgs.earthquake.nshmp.www.NshmpMicronautServlet; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.annotation.Controller; @@ -16,9 +15,9 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.inject.Inject; /** - * Micronaut controller for probabilistic seismic hazard calculations. + * Micronaut controller for probabilistic seismic hazard calculations and + * services. * - * @see HazardService * @author U.S. Geological Survey */ @Tag( @@ -32,8 +31,7 @@ public class HazardController { @Operation( summary = "Hazard model and service metadata", - description = "Returns details of the installed model and service request parameters", - operationId = "hazard_doGetMetadata") + description = "Returns details of the installed model and service request parameters") @ApiResponse( description = "Hazard service metadata", responseCode = "200") @@ -51,31 +49,32 @@ public class HazardController { */ @Operation( summary = "Compute probabilisitic hazard at a site", - description = "Returns hazard curves computed from the installed model", - operationId = "hazard_doGetHazard") + description = "Returns hazard curves computed from the installed model") @ApiResponse( description = "Hazard curves", responseCode = "200") @Get(uri = "/{longitude}/{latitude}/{vs30}{?truncate,maxdir}") public HttpResponse<String> doGetHazard( - HttpRequest<?> request, - - @Schema(minimum = "-360", maximum = "360") @PathVariable double longitude, - - @Schema(minimum = "-90", maximum = "90") @PathVariable double latitude, - - @Schema(minimum = "150", maximum = "3000") @PathVariable int vs30, - - @QueryValue(defaultValue = "false") boolean truncate, - - @QueryValue(defaultValue = "false") boolean maxdir) { - - /* - * @Schema annotation parameter constraints only affect Swagger service - * index page behavior; still need to validate against model. TODO - */ - - var query = new QueryParameters(longitude, latitude, vs30, truncate, maxdir); - return HazardService.handleDoGetHazard(request, query); + HttpRequest<?> http, + @Schema( + minimum = "-360", + maximum = "360") @PathVariable double longitude, + @Schema( + minimum = "-90", + maximum = "90") @PathVariable double latitude, + @Schema( + minimum = "150", + maximum = "3000") @PathVariable int vs30, + @QueryValue( + defaultValue = "false") boolean truncate, + @QueryValue( + defaultValue = "false") boolean maxdir) { + try { + HazardService.Request request = new HazardService.Request( + http, longitude, latitude, vs30, truncate, maxdir); + return HazardService.processRequest(request); + } catch (Exception e) { + return ServicesUtil.handleError(e, HazardService.NAME, http.getUri().getPath()); + } } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java similarity index 62% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java index 89f03a87b3a4d52e36b209be6f79799729b46a51..90cf98ef5a97a0d7b67f388695d06484d438c09f 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/HazardService.java @@ -1,20 +1,24 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www.hazard; import static com.google.common.base.Preconditions.checkState; import static gov.usgs.earthquake.nshmp.calc.HazardExport.curvesBySource; +import static gov.usgs.earthquake.nshmp.data.DoubleData.checkInRange; +import static gov.usgs.earthquake.nshmp.geo.Coordinates.checkLatitude; +import static gov.usgs.earthquake.nshmp.geo.Coordinates.checkLongitude; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.function.Function; import com.google.common.base.Stopwatch; import gov.usgs.earthquake.nshmp.calc.CalcConfig; import gov.usgs.earthquake.nshmp.calc.Hazard; +import gov.usgs.earthquake.nshmp.calc.HazardCalcs; import gov.usgs.earthquake.nshmp.calc.Site; import gov.usgs.earthquake.nshmp.data.MutableXySequence; import gov.usgs.earthquake.nshmp.data.XySequence; @@ -23,16 +27,13 @@ import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.model.SourceType; -import gov.usgs.earthquake.nshmp.www.HazardController; import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.WsUtils; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Parameter; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceQueryData; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceRequestData; import gov.usgs.earthquake.nshmp.www.services.SourceServices.SourceModel; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; @@ -46,13 +47,13 @@ import jakarta.inject.Singleton; @Singleton public final class HazardService { - private static final String NAME = "Hazard Service"; + static final String NAME = "Hazard Service"; /** HazardController.doGetUsage() handler. */ public static HttpResponse<String> handleDoGetMetadata(HttpRequest<?> request) { var url = request.getUri().getPath(); try { - var usage = new RequestMetadata(ServletUtil.model()); + var usage = new UsageMetadata(ServletUtil.model()); var response = ResponseBody.usage() .name(NAME) .url(url) @@ -67,111 +68,82 @@ public final class HazardService { } /** HazardController.doGetHazard() handler. */ - public static HttpResponse<String> handleDoGetHazard( - HttpRequest<?> request, - QueryParameters query) { - + public static HttpResponse<String> processRequest(Request request) { try { - // TODO still need to validate - // if (query.isEmpty()) { - // return handleDoGetUsage(urlHelper); - // } - // query.checkParameters(); - var data = new RequestData(query); - var response = process(request, data); - var svcResponse = ServletUtil.GSON.toJson(response); + Response response = process(request); + var body = ResponseBody.success() + .name(NAME) + .url(request.http.getUri().getPath()) + .request(request) + .response(response) + .build(); + String svcResponse = ServletUtil.GSON.toJson(body); return HttpResponse.ok(svcResponse); } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, request.getUri().getPath()); + return ServicesUtil.handleError(e, NAME, request.http.getUri().getPath()); } } - static ResponseBody<RequestData, ResponseData> process( - HttpRequest<?> request, - RequestData data) + /* + * Developer notes: + * + * Future calculation configuration options: vertical GMs + * + * NSHM Hazard Tool will not pass truncation and maxdir args/flags as the apps + * apply truncation and scaling on the client. + */ + + static Response process(Request request) throws InterruptedException, ExecutionException { - var configFunction = new ConfigFunction(); - var siteFunction = new SiteFunction(data); var stopwatch = Stopwatch.createStarted(); - var hazard = ServicesUtil.calcHazard(configFunction, siteFunction); + var hazard = calcHazard(request); return new ResultBuilder() + .request(request) .hazard(hazard) - .requestData(data) .timer(stopwatch) - .url(request) .build(); } - static class ConfigFunction implements Function<HazardModel, CalcConfig> { - @Override - public CalcConfig apply(HazardModel model) { - var configBuilder = CalcConfig.copyOf(model.config()); - return configBuilder.build(); - } - } + public static Hazard calcHazard(Request request) + throws InterruptedException, ExecutionException { - static class SiteFunction implements Function<CalcConfig, Site> { - final RequestData data; + HazardModel model = ServletUtil.model(); - private SiteFunction(RequestData data) { - this.data = data; - } + // will we be passing in options for config?? + CalcConfig config = CalcConfig.copyOf(model.config()).build(); - @Override // TODO this needs to pick up SiteData - public Site apply(CalcConfig config) { - return Site.builder() - .location(Location.create(data.longitude, data.latitude)) - .vs30(data.vs30) - .build(); - } + // TODO this needs to pick up SiteData + Site site = Site.builder() + .location(Location.create(request.longitude, request.latitude)) + .vs30(request.vs30) + .build(); + CompletableFuture<Hazard> future = futureHazard(model, config, site); + return future.get(); } - public static class QueryParameters { + private static CompletableFuture<Hazard> futureHazard( + HazardModel model, + CalcConfig config, + Site site) { - final double longitude; - final double latitude; - final int vs30; - final boolean truncate; - final boolean maxdir; - - public QueryParameters( - double longitude, - double latitude, - int vs30, - boolean truncate, - boolean maxdir) { - - this.longitude = longitude; - this.latitude = latitude; - this.vs30 = vs30; - this.truncate = truncate; - this.maxdir = maxdir; - } - - // void checkParameters() { - // checkParameter(longitude, "longitude"); - // checkParameter(latitude, "latitude"); - // checkParameter(vs30, "vs30"); - // } + return CompletableFuture.supplyAsync( + () -> HazardCalcs.hazard(model, config, site, ServletUtil.CALC_EXECUTOR), + ServletUtil.TASK_EXECUTOR); } - // private static void checkParameter(Object param, String id) { - // checkNotNull(param, "Missing parameter: %s", id); - // // TODO check range here - // } - - /* Service request and model metadata */ - static class RequestMetadata { + private static class UsageMetadata { final SourceModel model; final DoubleParameter longitude; final DoubleParameter latitude; final DoubleParameter vs30; - RequestMetadata(HazardModel model) { + UsageMetadata(HazardModel model) { this.model = new SourceModel(model); + // perhaps move out to shared factory with parameter instances + // // TODO need min max from model longitude = new DoubleParameter( "Longitude", @@ -186,27 +158,36 @@ public final class HazardService { Coordinates.LAT_RANGE.upperEndpoint()); vs30 = new DoubleParameter( - "Latitude", + "Vs30", "m/s", 150, 1500); } } - static class RequestData { + public static final class Request { + transient HttpRequest<?> http; final double longitude; final double latitude; final double vs30; final boolean truncate; final boolean maxdir; - RequestData(QueryParameters query) { - this.longitude = query.longitude; - this.latitude = query.latitude; - this.vs30 = query.vs30; - this.truncate = query.truncate; - this.maxdir = query.maxdir; + public Request( + HttpRequest<?> http, + double longitude, + double latitude, + int vs30, + boolean truncate, + boolean maxdir) { + + this.http = http; + this.longitude = checkLongitude(longitude); + this.latitude = checkLatitude(latitude); + this.vs30 = checkInRange(Site.VS30_RANGE, Site.Key.VS30, vs30); + this.truncate = truncate; + this.maxdir = maxdir; } } @@ -220,40 +201,21 @@ public final class HazardService { } } - private static String imtShortLabel(Imt imt) { - if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { - return imt.name(); - } else if (imt.isSA()) { - return imt.period() + " s"; - } - return imt.toString(); - } - - @Deprecated - static class RequestDataOld extends ServiceRequestData { - final double vs30; - - RequestDataOld(Query query, double vs30) { - super(query); - this.vs30 = vs30; - } - } - - private static final class ResponseData { + private static final class Response { final ResponseMetadata metadata; - final List<HazardResponse> hazardCurves; + final List<ImtCurves> hazardCurves; - ResponseData(ResponseMetadata metadata, List<HazardResponse> hazardCurves) { + Response(ResponseMetadata metadata, List<ImtCurves> hazardCurves) { this.metadata = metadata; this.hazardCurves = hazardCurves; } } - private static final class HazardResponse { + private static final class ImtCurves { final Parameter imt; final List<Curve> data; - HazardResponse(Imt imt, List<Curve> data) { + ImtCurves(Imt imt, List<Curve> data) { this.imt = new Parameter(imtShortLabel(imt), imt.name()); this.data = data; } @@ -273,9 +235,8 @@ public final class HazardService { private static final class ResultBuilder { - String url; Stopwatch timer; - RequestData request; + Request request; Map<Imt, Map<SourceType, MutableXySequence>> componentMaps; Map<Imt, MutableXySequence> totalMap; @@ -311,23 +272,18 @@ public final class HazardService { return this; } - ResultBuilder url(HttpRequest<?> request) { - url = request.getUri().getPath(); - return this; - } - ResultBuilder timer(Stopwatch timer) { this.timer = timer; return this; } - ResultBuilder requestData(RequestData request) { + ResultBuilder request(Request request) { this.request = request; return this; } - ResponseBody<RequestData, ResponseData> build() { - var hazards = new ArrayList<HazardResponse>(); + Response build() { + var hazards = new ArrayList<ImtCurves>(); for (Imt imt : totalMap.keySet()) { var curves = new ArrayList<Curve>(); @@ -345,18 +301,15 @@ public final class HazardService { updateCurve(request, typeMap.get(type), imt))); } - hazards.add(new HazardResponse(imt, List.copyOf(curves))); + hazards.add(new ImtCurves(imt, List.copyOf(curves))); } Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); - var response = new ResponseData(new ResponseMetadata(server), List.copyOf(hazards)); + var response = new Response( + new ResponseMetadata(server), + List.copyOf(hazards)); - return ResponseBody.<RequestData, ResponseData> success() - .name(NAME) - .url(url) - .request(request) - .response(response) - .build(); + return response; } } @@ -364,7 +317,7 @@ public final class HazardService { /* Convert to linear and possibly truncate and scale to max-direction. */ private static XySequence updateCurve( - RequestData request, + Request request, XySequence curve, Imt imt) { @@ -398,25 +351,13 @@ public final class HazardService { return limit; } - @Deprecated - public static class Query extends ServiceQueryData { - Integer vs30; - - public Query(Double longitude, Double latitude, Integer vs30) { - super(longitude, latitude); - this.vs30 = vs30; - } - - @Override - public boolean isNull() { - return super.isNull() && vs30 == null; - } - - @Override - public void checkValues() { - super.checkValues(); - WsUtils.checkValue(ServicesUtil.Key.VS30, vs30); + private static String imtShortLabel(Imt imt) { + if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { + return imt.name(); + } else if (imt.isSA()) { + return imt.period() + " s"; } + return imt.toString(); } } diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/MaxDirection.java b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/MaxDirection.java similarity index 97% rename from src/main/java/gov/usgs/earthquake/nshmp/www/services/MaxDirection.java rename to src/main/java/gov/usgs/earthquake/nshmp/www/hazard/MaxDirection.java index e35c59355037416482937ce865aeee59400af2cf..99c40c42d3127fcca3bb176c6fb837d20d0d6c3d 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/MaxDirection.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/hazard/MaxDirection.java @@ -1,4 +1,4 @@ -package gov.usgs.earthquake.nshmp.www.services; +package gov.usgs.earthquake.nshmp.www.hazard; import static gov.usgs.earthquake.nshmp.gmm.Imt.PGA; import static gov.usgs.earthquake.nshmp.gmm.Imt.SA0P01; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java index 1120be8504341bf5e2b843fb4b3615f2f9216869..ed8a2973d0cb7ce54e00d8d68d35dd70c4260c51 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Metadata.java @@ -4,7 +4,7 @@ import com.google.common.base.Stopwatch; import com.google.common.base.Throwables; import gov.usgs.earthquake.nshmp.geo.Coordinates; -import gov.usgs.earthquake.nshmp.www.services.ServletUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; /** * Service metadata, parameterization, and constraint strings, in JSON format. diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService2.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService2.java deleted file mode 100644 index 63fb4261bd5c38b37aea9926f00c787e3cdbd1d1..0000000000000000000000000000000000000000 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/HazardService2.java +++ /dev/null @@ -1,452 +0,0 @@ -package gov.usgs.earthquake.nshmp.www.services; - -import static com.google.common.base.Preconditions.checkState; -import static gov.usgs.earthquake.nshmp.calc.HazardExport.curvesBySource; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.function.Function; - -import com.google.common.base.Stopwatch; - -import gov.usgs.earthquake.nshmp.calc.CalcConfig; -import gov.usgs.earthquake.nshmp.calc.Hazard; -import gov.usgs.earthquake.nshmp.calc.Site; -import gov.usgs.earthquake.nshmp.data.MutableXySequence; -import gov.usgs.earthquake.nshmp.data.XySequence; -import gov.usgs.earthquake.nshmp.geo.Coordinates; -import gov.usgs.earthquake.nshmp.geo.Location; -import gov.usgs.earthquake.nshmp.gmm.Imt; -import gov.usgs.earthquake.nshmp.model.HazardModel; -import gov.usgs.earthquake.nshmp.model.SourceType; -import gov.usgs.earthquake.nshmp.www.HazardController; -import gov.usgs.earthquake.nshmp.www.ResponseBody; -import gov.usgs.earthquake.nshmp.www.WsUtils; -import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; -import gov.usgs.earthquake.nshmp.www.meta.Metadata; -import gov.usgs.earthquake.nshmp.www.meta.Parameter; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceQueryData; -import gov.usgs.earthquake.nshmp.www.services.SourceServices.SourceModel; - -import io.micronaut.http.HttpRequest; -import io.micronaut.http.HttpResponse; -import jakarta.inject.Singleton; - -/** - * Probabilistic seismic hazard calculation handler for - * {@link HazardController}. - * - * @author U.S. Geological Survey - */ -@Singleton -public final class HazardService2 { - - private static final String NAME = "Hazard Service"; - - /** HazardController.doGetUsage() handler. */ - public static HttpResponse<String> handleDoGetMetadata(HttpRequest<?> request) { - var url = request.getUri().getPath(); - try { - var usage = new RequestMetadata(ServletUtil.model());// SourceServices.ResponseData(); - var response = ResponseBody.usage() - .name(NAME) - .url(url) - .request(url) - .response(usage) - .build(); - var svcResponse = ServletUtil.GSON.toJson(response); - return HttpResponse.ok(svcResponse); - } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, url); - } - } - - /** HazardController.doGetHazard() handler. */ - public static HttpResponse<String> handleDoGetHazard( - HttpRequest<?> request, - RequestData args) { - - try { - // TODO still need to validate - // if (query.isEmpty()) { - // return handleDoGetUsage(urlHelper); - // } - // query.checkParameters(); - - // var data = new RequestData(query); - - ResponseBody<RequestData, ResponseData> response = process(request, args); - String svcResponse = ServletUtil.GSON.toJson(response); - return HttpResponse.ok(svcResponse); - - } catch (Exception e) { - return ServicesUtil.handleError(e, NAME, request.getUri().getPath()); - } - } - - static ResponseBody<RequestData, ResponseData> process( - HttpRequest<?> request, - RequestData data) throws InterruptedException, ExecutionException { - - var configFunction = new ConfigFunction(); - var siteFunction = new SiteFunction(data); - var stopwatch = Stopwatch.createStarted(); - var hazard = ServicesUtil.calcHazard(configFunction, siteFunction); - - return new ResultBuilder() - .hazard(hazard) - .requestData(data) - .timer(stopwatch) - .url(request) - .build(); - } - - static class ConfigFunction implements Function<HazardModel, CalcConfig> { - @Override - public CalcConfig apply(HazardModel model) { - var configBuilder = CalcConfig.copyOf(model.config()); - return configBuilder.build(); - } - } - - static class SiteFunction implements Function<CalcConfig, Site> { - final RequestData data; - - private SiteFunction(RequestData data) { - this.data = data; - } - - @Override // TODO this needs to pick up SiteData - public Site apply(CalcConfig config) { - return Site.builder() - .location(Location.create(data.longitude, data.latitude)) - .vs30(data.vs30) - .build(); - } - } - - // public static class QueryParameters { - // - // final double longitude; - // final double latitude; - // final int vs30; - // final boolean truncate; - // final boolean maxdir; - // - // public QueryParameters( - // double longitude, - // double latitude, - // int vs30, - // boolean truncate, - // boolean maxdir) { - // - // this.longitude = longitude; - // this.latitude = latitude; - // this.vs30 = vs30; - // this.truncate = truncate; - // this.maxdir = maxdir; - // } - // - // // void checkParameters() { - // // checkParameter(longitude, "longitude"); - // // checkParameter(latitude, "latitude"); - // // checkParameter(vs30, "vs30"); - // // } - // } - - // private static void checkParameter(Object param, String id) { - // checkNotNull(param, "Missing parameter: %s", id); - // // TODO check range here - // } - - /* Service request and model metadata */ - static class RequestMetadata { - - final SourceModel model; - final DoubleParameter longitude; - final DoubleParameter latitude; - final DoubleParameter vs30; - - RequestMetadata(HazardModel model) { - this.model = new SourceModel(model); - // TODO need min max from model - longitude = new DoubleParameter( - "Longitude", - "°", - Coordinates.LON_RANGE.lowerEndpoint(), - Coordinates.LON_RANGE.upperEndpoint()); - - latitude = new DoubleParameter( - "Latitude", - "°", - Coordinates.LAT_RANGE.lowerEndpoint(), - Coordinates.LAT_RANGE.upperEndpoint()); - - vs30 = new DoubleParameter( - "Latitude", - "m/s", - 150, - 1500); - } - } - - // static class RequestData { - // - // final double longitude; - // final double latitude; - // final double vs30; - // final boolean truncate; - // final boolean maxdir; - // - // RequestData(QueryParameters query) { - // this.longitude = query.longitude; - // this.latitude = query.latitude; - // this.vs30 = query.vs30; - // this.truncate = query.truncate; - // this.maxdir = query.maxdir; - // } - // } - - private static final class ResponseMetadata { - final String xlabel = "Ground Motion (g)"; - final String ylabel = "Annual Frequency of Exceedence"; - final Object server; - - ResponseMetadata(Object server) { - this.server = server; - } - } - - private static String imtShortLabel(Imt imt) { - if (imt.equals(Imt.PGA) || imt.equals(Imt.PGV)) { - return imt.name(); - } else if (imt.isSA()) { - return imt.period() + " s"; - } - return imt.toString(); - } - - // @Deprecated - // static class RequestDataOld extends ServiceRequestData { - // final double vs30; - // - // RequestDataOld(Query query, double vs30) { - // super(query); - // this.vs30 = vs30; - // } - // } - - private static final class ResponseData { - final ResponseMetadata metadata; - final List<HazardResponse> hazardCurves; - - ResponseData(ResponseMetadata metadata, List<HazardResponse> hazardCurves) { - this.metadata = metadata; - this.hazardCurves = hazardCurves; - } - } - - private static final class HazardResponse { - final Parameter imt; - final List<Curve> data; - - HazardResponse(Imt imt, List<Curve> data) { - this.imt = new Parameter(imtShortLabel(imt), imt.name()); - this.data = data; - } - } - - private static final class Curve { - final String component; - final XySequence values; - - Curve(String component, XySequence values) { - this.component = component; - this.values = values; - } - } - - private static final String TOTAL_KEY = "Total"; - - private static final class ResultBuilder { - - String url; - Stopwatch timer; - RequestData request; - - Map<Imt, Map<SourceType, MutableXySequence>> componentMaps; - Map<Imt, MutableXySequence> totalMap; - - ResultBuilder hazard(Hazard hazardResult) { - // TODO necessary?? - checkState(totalMap == null, "Hazard has already been added to this builder"); - - componentMaps = new EnumMap<>(Imt.class); - totalMap = new EnumMap<>(Imt.class); - - var typeTotalMaps = curvesBySource(hazardResult); - - for (var imt : hazardResult.curves().keySet()) { - - /* Total curve for IMT. */ - XySequence.addToMap(imt, totalMap, hazardResult.curves().get(imt)); - - /* Source component curves for IMT. */ - var typeTotalMap = typeTotalMaps.get(imt); - var componentMap = componentMaps.get(imt); - - if (componentMap == null) { - componentMap = new EnumMap<>(SourceType.class); - componentMaps.put(imt, componentMap); - } - - for (var type : typeTotalMap.keySet()) { - XySequence.addToMap(type, componentMap, typeTotalMap.get(type)); - } - } - - return this; - } - - ResultBuilder url(HttpRequest<?> request) { - url = request.getUri().getPath(); - return this; - } - - ResultBuilder timer(Stopwatch timer) { - this.timer = timer; - return this; - } - - ResultBuilder requestData(RequestData request) { - this.request = request; - return this; - } - - ResponseBody<RequestData, ResponseData> build() { - var hazards = new ArrayList<HazardResponse>(); - - for (Imt imt : totalMap.keySet()) { - var curves = new ArrayList<Curve>(); - - // total curve - curves.add(new Curve( - TOTAL_KEY, - updateCurve(request, totalMap.get(imt), imt))); - - // component curves - var typeMap = componentMaps.get(imt); - for (SourceType type : typeMap.keySet()) { - curves.add(new Curve( - type.toString(), - updateCurve(request, typeMap.get(type), imt))); - } - - hazards.add(new HazardResponse(imt, List.copyOf(curves))); - } - - Object server = Metadata.serverData(ServletUtil.THREAD_COUNT, timer); - var response = new ResponseData(new ResponseMetadata(server), List.copyOf(hazards)); - - return ResponseBody.<RequestData, ResponseData> success() - .name(NAME) - .url(url) - .request(request) - .response(response) - .build(); - } - } - - private static final double TRUNCATION_LIMIT = 1e-4; - - /* Convert to linear and possibly truncate and scale to max-direction. */ - private static XySequence updateCurve( - RequestData request, - XySequence curve, - Imt imt) { - - /* - * If entire curve is <1e-4, this method will return a curve consisting of - * just the first point in the supplied curve. - * - * TODO We probably want to move the TRUNCATION_LIMIT out to a config. - */ - - double[] yValues = curve.yValues().toArray(); - int limit = request.truncate ? truncationLimit(yValues) : yValues.length; - yValues = Arrays.copyOf(yValues, limit); - - double scale = request.maxdir ? MaxDirection.FACTORS.get(imt) : 1.0; - double[] xValues = curve.xValues() - .limit(yValues.length) - .map(Math::exp) - .map(x -> x * scale) - .toArray(); - - return XySequence.create(xValues, yValues); - } - - private static int truncationLimit(double[] yValues) { - int limit = 1; - double y = yValues[0]; - while (y > TRUNCATION_LIMIT && limit < yValues.length) { - y = yValues[limit++]; - } - return limit; - } - - @Deprecated - public static class Query extends ServiceQueryData { - Integer vs30; - - public Query(Double longitude, Double latitude, Integer vs30) { - super(longitude, latitude); - this.vs30 = vs30; - } - - @Override - public boolean isNull() { - return super.isNull() && vs30 == null; - } - - @Override - public void checkValues() { - super.checkValues(); - WsUtils.checkValue(ServicesUtil.Key.VS30, vs30); - } - } - - public static final class RequestData { - - final double longitude; - final double latitude; - final int vs30; - final boolean truncate; - final boolean maxdir; - - public RequestData( - double longitude, - double latitude, - int vs30, - boolean truncate, - boolean maxdir) { - - this.longitude = longitude; - this.latitude = latitude; - this.vs30 = vs30; - this.truncate = truncate; - this.maxdir = maxdir; - } - - // void checkParameters() { - // checkParameter(longitude, "longitude"); - // checkParameter(latitude, "latitude"); - // checkParameter(vs30, "vs30"); - // } - } - -} diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java index 0312e7359a0af379d43fdd55c8a5903edce67a2a..8c1a50c43132cf4160bd38b4be3f6a0fb9076e8b 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/RateService.java @@ -17,14 +17,15 @@ import gov.usgs.earthquake.nshmp.geo.Location; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.RateController; import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; +import gov.usgs.earthquake.nshmp.www.ServicesUtil.Key; +import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceQueryData; +import gov.usgs.earthquake.nshmp.www.ServicesUtil.ServiceRequestData; import gov.usgs.earthquake.nshmp.www.meta.DoubleParameter; import gov.usgs.earthquake.nshmp.www.meta.Metadata; import gov.usgs.earthquake.nshmp.www.meta.Metadata.DefaultParameters; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.Key; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceQueryData; -import gov.usgs.earthquake.nshmp.www.services.ServicesUtil.ServiceRequestData; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java index 3d0dfe0a7055376d3b5e764e252f23ca80f4f265..4a7882cdbc8bb789d79b79e1f03aadfaba63ee2b 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceLogicTreesService.java @@ -2,6 +2,8 @@ package gov.usgs.earthquake.nshmp.www.services; import gov.usgs.earthquake.nshmp.model.Models; import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.SourceLogicTreesController; import io.micronaut.http.HttpRequest; diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java index 83910b2fc623cd7b27e81855da5263435cb87d65..bd22e1c37247c832f4265305b80268f7aeb613a8 100644 --- a/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java +++ b/src/main/java/gov/usgs/earthquake/nshmp/www/services/SourceServices.java @@ -12,9 +12,10 @@ import gov.usgs.earthquake.nshmp.gmm.Imt; import gov.usgs.earthquake.nshmp.gmm.NehrpSiteClass; import gov.usgs.earthquake.nshmp.model.HazardModel; import gov.usgs.earthquake.nshmp.www.ResponseBody; +import gov.usgs.earthquake.nshmp.www.ServicesUtil; +import gov.usgs.earthquake.nshmp.www.ServletUtil; import gov.usgs.earthquake.nshmp.www.WsUtils; import gov.usgs.earthquake.nshmp.www.meta.Metadata; - import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import jakarta.inject.Singleton; @@ -105,7 +106,7 @@ public class SourceServices { Set<Gmm> gmms; Map<NehrpSiteClass, Double> siteClasses; - SourceModel(HazardModel model) { + public SourceModel(HazardModel model) { name = model.name(); gmms = model.gmms(); siteClasses = model.siteClasses();