From fad1c898fd0dd7ebda688dced6dbf94ee20d1b66 Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Thu, 21 May 2026 07:46:04 +1000 Subject: [PATCH 1/9] [seismology] Add DepthLookup abstract interface Adds an extensible depth-lookup interface used by scautoloc and other processing modules to select region- or slab-specific default and maximum depths instead of a single global value. Two built-in implementations: "Constant" (passthrough) and "Polygon" (named GeoFeatureSet regions with defaultDepth/maxDepth attributes). Third-party backends register via REGISTER_DEPTH_LOOKUP in a plugin. --- libs/seiscomp/seismology/CMakeLists.txt | 2 + libs/seiscomp/seismology/depthlookup.cpp | 173 +++++++++++++++++++++++ libs/seiscomp/seismology/depthlookup.h | 97 +++++++++++++ 3 files changed, 272 insertions(+) create mode 100644 libs/seiscomp/seismology/depthlookup.cpp create mode 100644 libs/seiscomp/seismology/depthlookup.h diff --git a/libs/seiscomp/seismology/CMakeLists.txt b/libs/seiscomp/seismology/CMakeLists.txt index 361c88540..8d3f8f71f 100644 --- a/libs/seiscomp/seismology/CMakeLists.txt +++ b/libs/seiscomp/seismology/CMakeLists.txt @@ -1,4 +1,5 @@ SET(SEISMOLOGY_SOURCES + depthlookup.cpp firstmotion.cpp locatorinterface.cpp mb.cpp @@ -9,6 +10,7 @@ SET(SEISMOLOGY_SOURCES ) SET(SEISMOLOGY_HEADERS + depthlookup.h firstmotion.h ttt.h regions.h diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp new file mode 100644 index 000000000..117db7ee3 --- /dev/null +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + * Copyright (C) gempa GmbH * + * All rights reserved. * + * Contact: gempa GmbH (seiscomp-dev@gempa.de) * + * * + * GNU Affero General Public License Usage * + * This file may be used under the terms of the GNU Affero * + * Public License version 3.0 as published by the Free Software Foundation * + * and appearing in the file LICENSE included in the packaging of this * + * file. Please review the following information to ensure the GNU Affero * + * Public License version 3.0 requirements will be met: * + * https://www.gnu.org/licenses/agpl-3.0.html. * + * * + * Other Usage * + * Alternatively, this file may be used in accordance with the terms and * + * conditions contained in a signed written agreement between you and * + * gempa GmbH. * + ***************************************************************************/ + + +#define SEISCOMP_COMPONENT DepthLookup + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +IMPLEMENT_INTERFACE_FACTORY(Seiscomp::Seismology::DepthLookup, SC_SYSTEM_CORE_API); + + +namespace Seiscomp { +namespace Seismology { + + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +namespace { + +std::optional parseAttr(const Geo::GeoFeature *f, + const std::string &key) { + const auto &attrs = f->attributes(); + auto it = attrs.find(key); + if ( it == attrs.end() || it->second.empty() ) { + return std::nullopt; + } + double v; + if ( !Core::fromString(v, it->second) ) { + return std::nullopt; + } + return v; +} + +} // anonymous namespace + + +// --------------------------------------------------------------------------- +// DepthLookupConstant +// --------------------------------------------------------------------------- + +class DepthLookupConstant : public DepthLookup { + public: + bool init(const Config::Config &) override { return true; } + + double getDefaultDepth(double, double, double fallback) const override { + return fallback; + } + + double getMaxDepth(double, double, double fallback) const override { + return fallback; + } +}; + +REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); + + +// --------------------------------------------------------------------------- +// DepthLookupPolygon +// --------------------------------------------------------------------------- + +/** + * Queries named polygon features from SeisComP's global GeoFeatureSet. + * + * Config key: autoloc.regionDepth.regions (list of feature names) + */ +class DepthLookupPolygon : public DepthLookup { + public: + bool init(const Config::Config &config) override { + std::vector names; + try { + names = config.getStrings("autoloc.regionDepth.regions"); + } + catch ( ... ) {} + + if ( names.empty() ) { + SEISCOMP_WARNING("DepthLookup/Polygon: no regions configured " + "under autoloc.regionDepth.regions"); + return true; + } + + const Geo::GeoFeatureSet &fs = + Geo::GeoFeatureSetSingleton::getInstance(); + + for ( const auto *f : fs.features() ) { + if ( !f->closedPolygon() ) { + continue; + } + if ( std::find(names.begin(), names.end(), f->name()) == names.end() ) { + continue; + } + + auto dd = parseAttr(f, "defaultDepth"); + if ( !dd ) { + SEISCOMP_WARNING("DepthLookup/Polygon: feature '%s' has " + "no defaultDepth attribute — skipped", + f->name().c_str()); + continue; + } + + _entries.push_back({f, *dd, parseAttr(f, "maxDepth")}); + SEISCOMP_DEBUG("DepthLookup/Polygon: loaded region '%s' " + "defaultDepth=%.0f", f->name().c_str(), *dd); + } + + SEISCOMP_INFO("DepthLookup/Polygon: %zu region(s) loaded", + _entries.size()); + return true; + } + + double getDefaultDepth(double lat, double lon, + double fallback) const override { + for ( const auto &e : _entries ) { + if ( e.feature->contains({lat, lon}) ) { + return e.defaultDepth; + } + } + return fallback; + } + + double getMaxDepth(double lat, double lon, + double fallback) const override { + for ( const auto &e : _entries ) { + if ( e.feature->contains({lat, lon}) ) { + return e.maxDepth.value_or(fallback); + } + } + return fallback; + } + + private: + struct Entry { + const Geo::GeoFeature *feature{nullptr}; + double defaultDepth{0.0}; + std::optional maxDepth; + }; + + std::vector _entries; +}; + +REGISTER_DEPTH_LOOKUP(DepthLookupPolygon, "Polygon"); + + +} // namespace Seismology +} // namespace Seiscomp diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h new file mode 100644 index 000000000..d447545cf --- /dev/null +++ b/libs/seiscomp/seismology/depthlookup.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) gempa GmbH * + * All rights reserved. * + * Contact: gempa GmbH (seiscomp-dev@gempa.de) * + * * + * GNU Affero General Public License Usage * + * This file may be used under the terms of the GNU Affero * + * Public License version 3.0 as published by the Free Software Foundation * + * and appearing in the file LICENSE included in the packaging of this * + * file. Please review the following information to ensure the GNU Affero * + * Public License version 3.0 requirements will be met: * + * https://www.gnu.org/licenses/agpl-3.0.html. * + * * + * Other Usage * + * Alternatively, this file may be used in accordance with the terms and * + * conditions contained in a signed written agreement between you and * + * gempa GmbH. * + ***************************************************************************/ + + +#ifndef SEISCOMP_SEISMOLOGY_DEPTHLOOKUP_H +#define SEISCOMP_SEISMOLOGY_DEPTHLOOKUP_H + + +#include + +#include +#include +#include +#include + + +namespace Seiscomp { +namespace Seismology { + + +DEFINE_SMARTPOINTER(DepthLookup); + +/** + * @brief Abstract interface for region- and slab-based default/maximum depth + * lookup, used by scautoloc and other SeisComP processing modules. + * + * Concrete implementations are registered via the REGISTER_DEPTH_LOOKUP macro + * and instantiated at runtime through DepthLookupFactory::Create(). + * + * Two implementations ship with the library: + * - "Constant" Returns the caller-supplied fallback (no-op default). + * - "Polygon" Queries named polygon features from SeisComP's global + * GeoFeatureSet; each polygon must carry a @c defaultDepth + * attribute (km, required) and may carry @c maxDepth (km). + * + * A separate @c dlslab2 plugin (seiscomp/main) provides depth lookup from + * USGS Slab2.0 depth-footprint contours. + */ +class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { + public: + virtual ~DepthLookup() = default; + + /** + * @brief Initialise the implementation. + * + * Called once after construction. Each implementation reads its + * own settings from @p config. + * + * @return True on success. + */ + virtual bool init(const Config::Config &config) = 0; + + /** + * @brief Return the default depth (km) at (@p lat, @p lon). + * @param fallback Returned when no region/slab matches. + */ + virtual double getDefaultDepth(double lat, double lon, + double fallback) const = 0; + + /** + * @brief Return the maximum acceptable depth (km) at (@p lat, @p lon). + * @param fallback Returned when no region/slab matches. + */ + virtual double getMaxDepth(double lat, double lon, + double fallback) const = 0; +}; + + +DEFINE_INTERFACE_FACTORY(DepthLookup); + + +} // namespace Seismology +} // namespace Seiscomp + + +#define REGISTER_DEPTH_LOOKUP(Class, Service) \ +Seiscomp::Core::Generic::InterfaceFactory \ +__##Class##InterfaceFactory__(Service) + + +#endif // SEISCOMP_SEISMOLOGY_DEPTHLOOKUP_H From ed7f0d00a3e7cdf0d95e6323d449a39e5d4e8dcf Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Thu, 21 May 2026 20:47:39 +1000 Subject: [PATCH 2/9] [seismology] DepthLookup: fetch/fetchMaxDepth API, backends own config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename getDefaultDepth/getMaxDepth → fetch/fetchMaxDepth; remove caller-supplied fallback parameter. Each backend now owns its full depth knowledge including fallback via its own config namespace: depths.constant.value, depths.polygon.regions, depths.polygon.fallback. --- libs/seiscomp/seismology/depthlookup.cpp | 51 +++++++++++++++++------- libs/seiscomp/seismology/depthlookup.h | 20 ++++++---- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp index 117db7ee3..be186f11e 100644 --- a/libs/seiscomp/seismology/depthlookup.cpp +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -69,15 +69,27 @@ std::optional parseAttr(const Geo::GeoFeature *f, class DepthLookupConstant : public DepthLookup { public: - bool init(const Config::Config &) override { return true; } + bool init(const Config::Config &config) override { + try { + _value = config.getDouble("depths.constant.value"); + } + catch ( ... ) { + SEISCOMP_INFO("DepthLookup/Constant: depths.constant.value not set, " + "using default %.0f km", _value); + } + return true; + } - double getDefaultDepth(double, double, double fallback) const override { - return fallback; + double fetch(double, double) const override { + return _value; } - double getMaxDepth(double, double, double fallback) const override { - return fallback; + double fetchMaxDepth(double, double) const override { + return _value; } + + private: + double _value{10.0}; }; REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); @@ -90,20 +102,30 @@ REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); /** * Queries named polygon features from SeisComP's global GeoFeatureSet. * - * Config key: autoloc.regionDepth.regions (list of feature names) + * Config keys: + * depths.polygon.regions — ordered list of feature names + * depths.polygon.fallback — depth returned when no polygon matches (km) */ class DepthLookupPolygon : public DepthLookup { public: bool init(const Config::Config &config) override { + try { + _fallback = config.getDouble("depths.polygon.fallback"); + } + catch ( ... ) { + SEISCOMP_INFO("DepthLookup/Polygon: depths.polygon.fallback not set, " + "using default %.0f km", _fallback); + } + std::vector names; try { - names = config.getStrings("autoloc.regionDepth.regions"); + names = config.getStrings("depths.polygon.regions"); } catch ( ... ) {} if ( names.empty() ) { SEISCOMP_WARNING("DepthLookup/Polygon: no regions configured " - "under autoloc.regionDepth.regions"); + "under depths.polygon.regions"); return true; } @@ -136,24 +158,22 @@ class DepthLookupPolygon : public DepthLookup { return true; } - double getDefaultDepth(double lat, double lon, - double fallback) const override { + double fetch(double lat, double lon) const override { for ( const auto &e : _entries ) { if ( e.feature->contains({lat, lon}) ) { return e.defaultDepth; } } - return fallback; + return _fallback; } - double getMaxDepth(double lat, double lon, - double fallback) const override { + double fetchMaxDepth(double lat, double lon) const override { for ( const auto &e : _entries ) { if ( e.feature->contains({lat, lon}) ) { - return e.maxDepth.value_or(fallback); + return e.maxDepth.value_or(_fallback); } } - return fallback; + return _fallback; } private: @@ -163,6 +183,7 @@ class DepthLookupPolygon : public DepthLookup { std::optional maxDepth; }; + double _fallback{10.0}; std::vector _entries; }; diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h index d447545cf..6406a4d74 100644 --- a/libs/seiscomp/seismology/depthlookup.h +++ b/libs/seiscomp/seismology/depthlookup.h @@ -44,13 +44,17 @@ DEFINE_SMARTPOINTER(DepthLookup); * and instantiated at runtime through DepthLookupFactory::Create(). * * Two implementations ship with the library: - * - "Constant" Returns the caller-supplied fallback (no-op default). + * - "Constant" Returns a fixed depth read from @c depths.constant.value. * - "Polygon" Queries named polygon features from SeisComP's global * GeoFeatureSet; each polygon must carry a @c defaultDepth * attribute (km, required) and may carry @c maxDepth (km). + * Fallback is read from @c depths.polygon.fallback. * * A separate @c dlslab2 plugin (seiscomp/main) provides depth lookup from * USGS Slab2.0 depth-footprint contours. + * + * Each implementation owns all depth knowledge including its fallback value; + * callers pass no fallback. */ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { public: @@ -68,17 +72,19 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { /** * @brief Return the default depth (km) at (@p lat, @p lon). - * @param fallback Returned when no region/slab matches. + * + * Always returns a finite value; the implementation supplies its + * own configured fallback when no region/slab matches. */ - virtual double getDefaultDepth(double lat, double lon, - double fallback) const = 0; + virtual double fetch(double lat, double lon) const = 0; /** * @brief Return the maximum acceptable depth (km) at (@p lat, @p lon). - * @param fallback Returned when no region/slab matches. + * + * Always returns a finite value; the implementation supplies its + * own configured fallback when no region/slab matches. */ - virtual double getMaxDepth(double lat, double lon, - double fallback) const = 0; + virtual double fetchMaxDepth(double lat, double lon) const = 0; }; From eff40d748a4f17741309c5dbe224fd6f6cf8a2bf Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Thu, 4 Jun 2026 22:56:01 +1000 Subject: [PATCH 3/9] [seismology] DepthLookup: fetch/fetchMaxDepth throw on no match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per Jan's review: fetch() and fetchMaxDepth() now throw std::out_of_range when no region/zone contains the given location. Callers that need a fallback value use the utility functions fetchDepth() and fetchMaxDepth() which catch and return the fallback. Removes the fallback ownership from DepthLookupPolygon — callers decide what to do when outside all configured regions. --- libs/seiscomp/seismology/depthlookup.cpp | 52 +++++++++++++++++------- libs/seiscomp/seismology/depthlookup.h | 47 ++++++++++++++------- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp index be186f11e..c1aa078cc 100644 --- a/libs/seiscomp/seismology/depthlookup.cpp +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -103,20 +104,14 @@ REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); * Queries named polygon features from SeisComP's global GeoFeatureSet. * * Config keys: - * depths.polygon.regions — ordered list of feature names - * depths.polygon.fallback — depth returned when no polygon matches (km) + * depths.polygon.regions — ordered list of feature names + * + * Throws std::out_of_range when no configured polygon contains the point. + * Use fetchDepth() / fetchMaxDepth() utility functions for fallback behaviour. */ class DepthLookupPolygon : public DepthLookup { public: bool init(const Config::Config &config) override { - try { - _fallback = config.getDouble("depths.polygon.fallback"); - } - catch ( ... ) { - SEISCOMP_INFO("DepthLookup/Polygon: depths.polygon.fallback not set, " - "using default %.0f km", _fallback); - } - std::vector names; try { names = config.getStrings("depths.polygon.regions"); @@ -164,16 +159,19 @@ class DepthLookupPolygon : public DepthLookup { return e.defaultDepth; } } - return _fallback; + throw std::out_of_range("No polygon contains the given location"); } double fetchMaxDepth(double lat, double lon) const override { for ( const auto &e : _entries ) { if ( e.feature->contains({lat, lon}) ) { - return e.maxDepth.value_or(_fallback); + if ( e.maxDepth ) { + return *e.maxDepth; + } + throw std::out_of_range("Polygon has no maxDepth attribute"); } } - return _fallback; + throw std::out_of_range("No polygon contains the given location"); } private: @@ -183,12 +181,38 @@ class DepthLookupPolygon : public DepthLookup { std::optional maxDepth; }; - double _fallback{10.0}; std::vector _entries; }; REGISTER_DEPTH_LOOKUP(DepthLookupPolygon, "Polygon"); +// --------------------------------------------------------------------------- +// Utility functions +// --------------------------------------------------------------------------- + +double fetchDepth(const DepthLookup *lookup, + double lat, double lon, + double fallback) noexcept { + try { + return lookup->fetch(lat, lon); + } + catch ( ... ) { + return fallback; + } +} + +double fetchMaxDepth(const DepthLookup *lookup, + double lat, double lon, + double fallback) noexcept { + try { + return lookup->fetchMaxDepth(lat, lon); + } + catch ( ... ) { + return fallback; + } +} + + } // namespace Seismology } // namespace Seiscomp diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h index 6406a4d74..10cbef8f5 100644 --- a/libs/seiscomp/seismology/depthlookup.h +++ b/libs/seiscomp/seismology/depthlookup.h @@ -23,6 +23,7 @@ #include +#include #include #include @@ -37,24 +38,24 @@ namespace Seismology { DEFINE_SMARTPOINTER(DepthLookup); /** - * @brief Abstract interface for region- and slab-based default/maximum depth - * lookup, used by scautoloc and other SeisComP processing modules. + * @brief Abstract interface for region- and slab-based depth lookup, + * used by scautoloc and other SeisComP processing modules. * * Concrete implementations are registered via the REGISTER_DEPTH_LOOKUP macro * and instantiated at runtime through DepthLookupFactory::Create(). * * Two implementations ship with the library: - * - "Constant" Returns a fixed depth read from @c depths.constant.value. + * - "Constant" Always returns a fixed depth read from depths.constant.value. * - "Polygon" Queries named polygon features from SeisComP's global - * GeoFeatureSet; each polygon must carry a @c defaultDepth - * attribute (km, required) and may carry @c maxDepth (km). - * Fallback is read from @c depths.polygon.fallback. + * GeoFeatureSet; each polygon must carry a defaultDepth + * attribute (km, required) and may carry maxDepth (km). + * Throws std::out_of_range when no polygon matches. * - * A separate @c dlslab2 plugin (seiscomp/main) provides depth lookup from + * A separate dlslab2 plugin (seiscomp/main) provides depth lookup from * USGS Slab2.0 depth-footprint contours. * - * Each implementation owns all depth knowledge including its fallback value; - * callers pass no fallback. + * Use the utility functions fetchDepth() / fetchMaxDepth() when a fallback + * value is needed instead of an exception. */ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { public: @@ -63,7 +64,7 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { /** * @brief Initialise the implementation. * - * Called once after construction. Each implementation reads its + * Called once after construction. Each implementation reads its * own settings from @p config. * * @return True on success. @@ -73,16 +74,19 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { /** * @brief Return the default depth (km) at (@p lat, @p lon). * - * Always returns a finite value; the implementation supplies its - * own configured fallback when no region/slab matches. + * @throws std::out_of_range if no depth information is available + * for the given location (e.g. outside all configured + * regions or slab zones). Use fetchDepth() for a + * fallback-based alternative. */ virtual double fetch(double lat, double lon) const = 0; /** * @brief Return the maximum acceptable depth (km) at (@p lat, @p lon). * - * Always returns a finite value; the implementation supplies its - * own configured fallback when no region/slab matches. + * @throws std::out_of_range if no depth information is available + * for the given location. Use fetchMaxDepth() for a + * fallback-based alternative. */ virtual double fetchMaxDepth(double lat, double lon) const = 0; }; @@ -91,6 +95,21 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { DEFINE_INTERFACE_FACTORY(DepthLookup); +/** + * @brief Return lookup->fetch(lat, lon), or @p fallback if it throws. + */ +SC_SYSTEM_CORE_API double fetchDepth(const DepthLookup *lookup, + double lat, double lon, + double fallback) noexcept; + +/** + * @brief Return lookup->fetchMaxDepth(lat, lon), or @p fallback if it throws. + */ +SC_SYSTEM_CORE_API double fetchMaxDepth(const DepthLookup *lookup, + double lat, double lon, + double fallback) noexcept; + + } // namespace Seismology } // namespace Seiscomp From 243e39f59d9978fa6c01c94b560a96c483ffdff3 Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Thu, 4 Jun 2026 23:52:59 +1000 Subject: [PATCH 4/9] [seismology] DepthLookup: backend owns the fallback Reworked based on Jan's feedback: fetch() and fetchMaxDepth() always return a value, fallback is configured inside the backend. Removed the utility functions that required the client to supply a fallback. --- libs/seiscomp/seismology/depthlookup.cpp | 51 +++++++----------------- libs/seiscomp/seismology/depthlookup.h | 36 +++++------------ 2 files changed, 24 insertions(+), 63 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp index c1aa078cc..e7ea77b74 100644 --- a/libs/seiscomp/seismology/depthlookup.cpp +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include @@ -104,14 +103,20 @@ REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); * Queries named polygon features from SeisComP's global GeoFeatureSet. * * Config keys: - * depths.polygon.regions — ordered list of feature names - * - * Throws std::out_of_range when no configured polygon contains the point. - * Use fetchDepth() / fetchMaxDepth() utility functions for fallback behaviour. + * depths.polygon.regions — ordered list of feature names + * depths.polygon.fallback — depth returned when no polygon matches (km) */ class DepthLookupPolygon : public DepthLookup { public: bool init(const Config::Config &config) override { + try { + _fallback = config.getDouble("depths.polygon.fallback"); + } + catch ( ... ) { + SEISCOMP_INFO("DepthLookup/Polygon: depths.polygon.fallback not set, " + "using default %.0f km", _fallback); + } + std::vector names; try { names = config.getStrings("depths.polygon.regions"); @@ -159,19 +164,16 @@ class DepthLookupPolygon : public DepthLookup { return e.defaultDepth; } } - throw std::out_of_range("No polygon contains the given location"); + return _fallback; } double fetchMaxDepth(double lat, double lon) const override { for ( const auto &e : _entries ) { if ( e.feature->contains({lat, lon}) ) { - if ( e.maxDepth ) { - return *e.maxDepth; - } - throw std::out_of_range("Polygon has no maxDepth attribute"); + return e.maxDepth.value_or(_fallback); } } - throw std::out_of_range("No polygon contains the given location"); + return _fallback; } private: @@ -181,38 +183,13 @@ class DepthLookupPolygon : public DepthLookup { std::optional maxDepth; }; + double _fallback{10.0}; std::vector _entries; }; REGISTER_DEPTH_LOOKUP(DepthLookupPolygon, "Polygon"); -// --------------------------------------------------------------------------- -// Utility functions -// --------------------------------------------------------------------------- - -double fetchDepth(const DepthLookup *lookup, - double lat, double lon, - double fallback) noexcept { - try { - return lookup->fetch(lat, lon); - } - catch ( ... ) { - return fallback; - } -} - -double fetchMaxDepth(const DepthLookup *lookup, - double lat, double lon, - double fallback) noexcept { - try { - return lookup->fetchMaxDepth(lat, lon); - } - catch ( ... ) { - return fallback; - } -} - } // namespace Seismology } // namespace Seiscomp diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h index 10cbef8f5..bc2c41c86 100644 --- a/libs/seiscomp/seismology/depthlookup.h +++ b/libs/seiscomp/seismology/depthlookup.h @@ -23,7 +23,6 @@ #include -#include #include #include @@ -49,13 +48,14 @@ DEFINE_SMARTPOINTER(DepthLookup); * - "Polygon" Queries named polygon features from SeisComP's global * GeoFeatureSet; each polygon must carry a defaultDepth * attribute (km, required) and may carry maxDepth (km). - * Throws std::out_of_range when no polygon matches. + * Returns depths.polygon.fallback when no polygon matches. * * A separate dlslab2 plugin (seiscomp/main) provides depth lookup from * USGS Slab2.0 depth-footprint contours. * - * Use the utility functions fetchDepth() / fetchMaxDepth() when a fallback - * value is needed instead of an exception. + * All depth knowledge — including the fallback for locations outside any + * configured region — is fully encapsulated in the backend. The client + * never needs to supply or handle a fallback value. */ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { public: @@ -74,19 +74,18 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { /** * @brief Return the default depth (km) at (@p lat, @p lon). * - * @throws std::out_of_range if no depth information is available - * for the given location (e.g. outside all configured - * regions or slab zones). Use fetchDepth() for a - * fallback-based alternative. + * Always returns a finite value. When no region or slab zone + * contains the given location the backend returns its own + * configured fallback depth. */ virtual double fetch(double lat, double lon) const = 0; /** * @brief Return the maximum acceptable depth (km) at (@p lat, @p lon). * - * @throws std::out_of_range if no depth information is available - * for the given location. Use fetchMaxDepth() for a - * fallback-based alternative. + * Always returns a finite value. When no region or slab zone + * contains the given location the backend returns its own + * configured fallback. */ virtual double fetchMaxDepth(double lat, double lon) const = 0; }; @@ -95,21 +94,6 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { DEFINE_INTERFACE_FACTORY(DepthLookup); -/** - * @brief Return lookup->fetch(lat, lon), or @p fallback if it throws. - */ -SC_SYSTEM_CORE_API double fetchDepth(const DepthLookup *lookup, - double lat, double lon, - double fallback) noexcept; - -/** - * @brief Return lookup->fetchMaxDepth(lat, lon), or @p fallback if it throws. - */ -SC_SYSTEM_CORE_API double fetchMaxDepth(const DepthLookup *lookup, - double lat, double lon, - double fallback) noexcept; - - } // namespace Seismology } // namespace Seiscomp From c069340f50ea09a1387086d08cfd87e23afca885 Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Tue, 7 Jul 2026 18:53:09 +1000 Subject: [PATCH 5/9] [seismology] Address gempa-jabe review on DepthLookup interface - Move DepthLookupConstant and DepthLookupPolygon into anonymous namespace so no concrete-class symbols are exported - Add fetchCandidates() virtual method with default implementation returning {fetch(lat,lon)}, resolving Joachim's open question on multi-depth support without breaking existing backends --- libs/seiscomp/seismology/depthlookup.cpp | 3 +-- libs/seiscomp/seismology/depthlookup.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp index e7ea77b74..56a8eee4a 100644 --- a/libs/seiscomp/seismology/depthlookup.cpp +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -60,8 +60,6 @@ std::optional parseAttr(const Geo::GeoFeature *f, return v; } -} // anonymous namespace - // --------------------------------------------------------------------------- // DepthLookupConstant @@ -189,6 +187,7 @@ class DepthLookupPolygon : public DepthLookup { REGISTER_DEPTH_LOOKUP(DepthLookupPolygon, "Polygon"); +} // anonymous namespace } // namespace Seismology diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h index bc2c41c86..bda21078d 100644 --- a/libs/seiscomp/seismology/depthlookup.h +++ b/libs/seiscomp/seismology/depthlookup.h @@ -23,6 +23,7 @@ #include +#include #include #include @@ -88,6 +89,19 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { * configured fallback. */ virtual double fetchMaxDepth(double lat, double lon) const = 0; + + /** + * @brief Return candidate seed depths (km) at (@p lat, @p lon). + * + * For regions with dual seismicity (shallow crustal layer above a + * subducting slab), a backend may return more than one depth so the + * caller can evaluate each seed independently. The default + * implementation returns {fetch(lat, lon)}, i.e. a single depth, + * which is the correct behaviour for all current backends. + */ + virtual std::vector fetchCandidates(double lat, double lon) const { + return {fetch(lat, lon)}; + } }; From 49e9ca57b57c3f36b4568be642e99f7bd0248881 Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Tue, 7 Jul 2026 19:36:17 +1000 Subject: [PATCH 6/9] [seismology] Fix fetchMaxDepth returning defaultDepth in both backends DepthLookupConstant was returning _value (10 km default) for both fetch() and fetchMaxDepth(). Any module using fetchMaxDepth() as a depth-rejection threshold would silently discard all events deeper than 10 km. Added a separate _maxDepth{1000.0} field configurable via depths.constant.maxDepth. Same issue in DepthLookupPolygon: fetchMaxDepth() fell back to _fallback (10 km) for unmatched locations and for polygons without a maxDepth attribute. Added _maxDepthFallback{1000.0} configurable via depths.polygon.maxDepth. --- libs/seiscomp/seismology/depthlookup.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp index 56a8eee4a..462de5d15 100644 --- a/libs/seiscomp/seismology/depthlookup.cpp +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -75,6 +75,10 @@ class DepthLookupConstant : public DepthLookup { SEISCOMP_INFO("DepthLookup/Constant: depths.constant.value not set, " "using default %.0f km", _value); } + try { + _maxDepth = config.getDouble("depths.constant.maxDepth"); + } + catch ( ... ) {} return true; } @@ -83,11 +87,12 @@ class DepthLookupConstant : public DepthLookup { } double fetchMaxDepth(double, double) const override { - return _value; + return _maxDepth; } private: double _value{10.0}; + double _maxDepth{1000.0}; }; REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); @@ -114,6 +119,10 @@ class DepthLookupPolygon : public DepthLookup { SEISCOMP_INFO("DepthLookup/Polygon: depths.polygon.fallback not set, " "using default %.0f km", _fallback); } + try { + _maxDepthFallback = config.getDouble("depths.polygon.maxDepth"); + } + catch ( ... ) {} std::vector names; try { @@ -168,10 +177,10 @@ class DepthLookupPolygon : public DepthLookup { double fetchMaxDepth(double lat, double lon) const override { for ( const auto &e : _entries ) { if ( e.feature->contains({lat, lon}) ) { - return e.maxDepth.value_or(_fallback); + return e.maxDepth.value_or(_maxDepthFallback); } } - return _fallback; + return _maxDepthFallback; } private: @@ -182,6 +191,7 @@ class DepthLookupPolygon : public DepthLookup { }; double _fallback{10.0}; + double _maxDepthFallback{1000.0}; std::vector _entries; }; From bfdf5d5666793ff71d04234722ffe0afa594ef72 Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Tue, 7 Jul 2026 19:41:46 +1000 Subject: [PATCH 7/9] [seismology] Fix stale comments; remove dangling plugin reference - DepthLookupPolygon docblock: add depths.polygon.maxDepth key - depthlookup.h class docstring: document maxDepth config keys for both backends; remove reference to dlslab2 plugin (does not exist) --- libs/seiscomp/seismology/depthlookup.cpp | 3 ++- libs/seiscomp/seismology/depthlookup.h | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp index 462de5d15..2db11d5a8 100644 --- a/libs/seiscomp/seismology/depthlookup.cpp +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -107,7 +107,8 @@ REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); * * Config keys: * depths.polygon.regions — ordered list of feature names - * depths.polygon.fallback — depth returned when no polygon matches (km) + * depths.polygon.fallback — default depth returned when no polygon matches (km) + * depths.polygon.maxDepth — max depth returned when no polygon matches (km) */ class DepthLookupPolygon : public DepthLookup { public: diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h index bda21078d..96f48c652 100644 --- a/libs/seiscomp/seismology/depthlookup.h +++ b/libs/seiscomp/seismology/depthlookup.h @@ -46,13 +46,12 @@ DEFINE_SMARTPOINTER(DepthLookup); * * Two implementations ship with the library: * - "Constant" Always returns a fixed depth read from depths.constant.value. + * Maximum depth is configurable via depths.constant.maxDepth. * - "Polygon" Queries named polygon features from SeisComP's global * GeoFeatureSet; each polygon must carry a defaultDepth * attribute (km, required) and may carry maxDepth (km). - * Returns depths.polygon.fallback when no polygon matches. - * - * A separate dlslab2 plugin (seiscomp/main) provides depth lookup from - * USGS Slab2.0 depth-footprint contours. + * Falls back to depths.polygon.fallback / depths.polygon.maxDepth + * when no polygon matches. * * All depth knowledge — including the fallback for locations outside any * configured region — is fully encapsulated in the backend. The client From d0d433a6e7fa250a1138a5f866af192dcc7df9af Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Wed, 8 Jul 2026 20:34:38 +1000 Subject: [PATCH 8/9] [seismology] Address gempa-jabe review: noexcept + GeoFeatureSetObserver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add noexcept to fetch(), fetchMaxDepth(), fetchCandidates() in the interface and all concrete overrides — makes the always-returns contract explicit at the type-system level - DepthLookupPolygon now inherits GeoFeatureSetObserver: registers on init(), stores _names as a member, rebuilds _entries via _buildEntries() on geoFeatureSetUpdated(). GeoFeatureSetObserver destructor handles deregistration automatically. Fixes dangling pointer crash if the global GeoFeatureSet is reloaded (e.g. user reloads map layers in scolv). --- libs/seiscomp/seismology/depthlookup.cpp | 72 ++++++++++++++---------- libs/seiscomp/seismology/depthlookup.h | 12 ++-- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.cpp b/libs/seiscomp/seismology/depthlookup.cpp index 2db11d5a8..31244e18e 100644 --- a/libs/seiscomp/seismology/depthlookup.cpp +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -82,11 +82,11 @@ class DepthLookupConstant : public DepthLookup { return true; } - double fetch(double, double) const override { + double fetch(double, double) const noexcept override { return _value; } - double fetchMaxDepth(double, double) const override { + double fetchMaxDepth(double, double) const noexcept override { return _maxDepth; } @@ -104,13 +104,15 @@ REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); /** * Queries named polygon features from SeisComP's global GeoFeatureSet. + * Registers as a GeoFeatureSetObserver so that _entries are rebuilt + * automatically if the feature set is reloaded (e.g. from scolv). * * Config keys: * depths.polygon.regions — ordered list of feature names * depths.polygon.fallback — default depth returned when no polygon matches (km) * depths.polygon.maxDepth — max depth returned when no polygon matches (km) */ -class DepthLookupPolygon : public DepthLookup { +class DepthLookupPolygon : public DepthLookup, public Geo::GeoFeatureSetObserver { public: bool init(const Config::Config &config) override { try { @@ -125,18 +127,47 @@ class DepthLookupPolygon : public DepthLookup { } catch ( ... ) {} - std::vector names; try { - names = config.getStrings("depths.polygon.regions"); + _names = config.getStrings("depths.polygon.regions"); } catch ( ... ) {} - if ( names.empty() ) { + if ( _names.empty() ) { SEISCOMP_WARNING("DepthLookup/Polygon: no regions configured " "under depths.polygon.regions"); return true; } + Geo::GeoFeatureSetSingleton::getInstance().registerObserver(this); + _buildEntries(); + return true; + } + + double fetch(double lat, double lon) const noexcept override { + for ( const auto &e : _entries ) { + if ( e.feature->contains({lat, lon}) ) { + return e.defaultDepth; + } + } + return _fallback; + } + + double fetchMaxDepth(double lat, double lon) const noexcept override { + for ( const auto &e : _entries ) { + if ( e.feature->contains({lat, lon}) ) { + return e.maxDepth.value_or(_maxDepthFallback); + } + } + return _maxDepthFallback; + } + + void geoFeatureSetUpdated() override { + _entries.clear(); + _buildEntries(); + } + + private: + void _buildEntries() { const Geo::GeoFeatureSet &fs = Geo::GeoFeatureSetSingleton::getInstance(); @@ -144,7 +175,7 @@ class DepthLookupPolygon : public DepthLookup { if ( !f->closedPolygon() ) { continue; } - if ( std::find(names.begin(), names.end(), f->name()) == names.end() ) { + if ( std::find(_names.begin(), _names.end(), f->name()) == _names.end() ) { continue; } @@ -163,37 +194,18 @@ class DepthLookupPolygon : public DepthLookup { SEISCOMP_INFO("DepthLookup/Polygon: %zu region(s) loaded", _entries.size()); - return true; - } - - double fetch(double lat, double lon) const override { - for ( const auto &e : _entries ) { - if ( e.feature->contains({lat, lon}) ) { - return e.defaultDepth; - } - } - return _fallback; - } - - double fetchMaxDepth(double lat, double lon) const override { - for ( const auto &e : _entries ) { - if ( e.feature->contains({lat, lon}) ) { - return e.maxDepth.value_or(_maxDepthFallback); - } - } - return _maxDepthFallback; } - private: struct Entry { const Geo::GeoFeature *feature{nullptr}; double defaultDepth{0.0}; std::optional maxDepth; }; - double _fallback{10.0}; - double _maxDepthFallback{1000.0}; - std::vector _entries; + std::vector _names; + double _fallback{10.0}; + double _maxDepthFallback{1000.0}; + std::vector _entries; }; REGISTER_DEPTH_LOOKUP(DepthLookupPolygon, "Polygon"); diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h index 96f48c652..c45572858 100644 --- a/libs/seiscomp/seismology/depthlookup.h +++ b/libs/seiscomp/seismology/depthlookup.h @@ -74,11 +74,11 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { /** * @brief Return the default depth (km) at (@p lat, @p lon). * - * Always returns a finite value. When no region or slab zone - * contains the given location the backend returns its own - * configured fallback depth. + * Always returns a finite value and never throws. When no region + * or slab zone contains the given location the backend returns its + * own configured fallback depth. */ - virtual double fetch(double lat, double lon) const = 0; + virtual double fetch(double lat, double lon) const noexcept = 0; /** * @brief Return the maximum acceptable depth (km) at (@p lat, @p lon). @@ -87,7 +87,7 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { * contains the given location the backend returns its own * configured fallback. */ - virtual double fetchMaxDepth(double lat, double lon) const = 0; + virtual double fetchMaxDepth(double lat, double lon) const noexcept = 0; /** * @brief Return candidate seed depths (km) at (@p lat, @p lon). @@ -98,7 +98,7 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { * implementation returns {fetch(lat, lon)}, i.e. a single depth, * which is the correct behaviour for all current backends. */ - virtual std::vector fetchCandidates(double lat, double lon) const { + virtual std::vector fetchCandidates(double lat, double lon) const noexcept { return {fetch(lat, lon)}; } }; From 49ff43c3c653674190f03dfb06f6a1cc9304b892 Mon Sep 17 00:00:00 2001 From: Mustafa Comoglu Date: Wed, 8 Jul 2026 21:06:47 +1000 Subject: [PATCH 9/9] [seismology] Rename fetchCandidates -> fetchCandidateDepths Suggested by Joachim for clarity. --- libs/seiscomp/seismology/depthlookup.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/seiscomp/seismology/depthlookup.h b/libs/seiscomp/seismology/depthlookup.h index c45572858..d6912bebc 100644 --- a/libs/seiscomp/seismology/depthlookup.h +++ b/libs/seiscomp/seismology/depthlookup.h @@ -93,12 +93,12 @@ class SC_SYSTEM_CORE_API DepthLookup : public Core::BaseObject { * @brief Return candidate seed depths (km) at (@p lat, @p lon). * * For regions with dual seismicity (shallow crustal layer above a - * subducting slab), a backend may return more than one depth so the - * caller can evaluate each seed independently. The default - * implementation returns {fetch(lat, lon)}, i.e. a single depth, - * which is the correct behaviour for all current backends. + * subducting slab), a backend may return more than one candidate + * depth for the caller to evaluate independently. The default + * implementation wraps fetch() and returns a single depth, which + * is the correct behaviour for all current backends. */ - virtual std::vector fetchCandidates(double lat, double lon) const noexcept { + virtual std::vector fetchCandidateDepths(double lat, double lon) const noexcept { return {fetch(lat, lon)}; } };