-
Notifications
You must be signed in to change notification settings - Fork 24
[seismology] Add DepthLookup abstract interface #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
gempa-jabe
merged 9 commits into
SeisComP:main
from
comoglu:feature/defaultdepthsetter-interface
Jul 9, 2026
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
fad1c89
[seismology] Add DepthLookup abstract interface
comoglu ed7f0d0
[seismology] DepthLookup: fetch/fetchMaxDepth API, backends own config
comoglu eff40d7
[seismology] DepthLookup: fetch/fetchMaxDepth throw on no match
comoglu 243e39f
[seismology] DepthLookup: backend owns the fallback
comoglu c069340
[seismology] Address gempa-jabe review on DepthLookup interface
comoglu 49e9ca5
[seismology] Fix fetchMaxDepth returning defaultDepth in both backends
comoglu bfdf5d5
[seismology] Fix stale comments; remove dangling plugin reference
comoglu d0d433a
[seismology] Address gempa-jabe review: noexcept + GeoFeatureSetObserver
comoglu 49ff43c
[seismology] Rename fetchCandidates -> fetchCandidateDepths
comoglu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| /*************************************************************************** | ||
| * 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 <seiscomp/seismology/depthlookup.h> | ||
| #include <seiscomp/core/interfacefactory.ipp> | ||
| #include <seiscomp/geo/feature.h> | ||
| #include <seiscomp/geo/featureset.h> | ||
| #include <seiscomp/core/strings.h> | ||
| #include <seiscomp/logging/log.h> | ||
|
|
||
| #include <algorithm> | ||
| #include <optional> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
|
|
||
| IMPLEMENT_INTERFACE_FACTORY(Seiscomp::Seismology::DepthLookup, SC_SYSTEM_CORE_API); | ||
|
|
||
|
|
||
| namespace Seiscomp { | ||
| namespace Seismology { | ||
|
|
||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Helpers | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| namespace { | ||
|
|
||
| std::optional<double> 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; | ||
| } | ||
|
|
||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // DepthLookupConstant | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| class DepthLookupConstant : public DepthLookup { | ||
| public: | ||
| 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); | ||
| } | ||
| try { | ||
| _maxDepth = config.getDouble("depths.constant.maxDepth"); | ||
| } | ||
| catch ( ... ) {} | ||
| return true; | ||
| } | ||
|
|
||
| double fetch(double, double) const noexcept override { | ||
| return _value; | ||
| } | ||
|
|
||
| double fetchMaxDepth(double, double) const noexcept override { | ||
| return _maxDepth; | ||
| } | ||
|
|
||
| private: | ||
| double _value{10.0}; | ||
| double _maxDepth{1000.0}; | ||
| }; | ||
|
|
||
| REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); | ||
|
|
||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // DepthLookupPolygon | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| /** | ||
| * 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, public Geo::GeoFeatureSetObserver { | ||
| 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); | ||
| } | ||
| try { | ||
| _maxDepthFallback = config.getDouble("depths.polygon.maxDepth"); | ||
| } | ||
| catch ( ... ) {} | ||
|
|
||
| try { | ||
| _names = config.getStrings("depths.polygon.regions"); | ||
| } | ||
| catch ( ... ) {} | ||
|
|
||
| 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(); | ||
|
|
||
| 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")}); | ||
|
comoglu marked this conversation as resolved.
|
||
| SEISCOMP_DEBUG("DepthLookup/Polygon: loaded region '%s' " | ||
| "defaultDepth=%.0f", f->name().c_str(), *dd); | ||
| } | ||
|
|
||
| SEISCOMP_INFO("DepthLookup/Polygon: %zu region(s) loaded", | ||
| _entries.size()); | ||
| } | ||
|
|
||
| struct Entry { | ||
| const Geo::GeoFeature *feature{nullptr}; | ||
| double defaultDepth{0.0}; | ||
| std::optional<double> maxDepth; | ||
| }; | ||
|
|
||
| std::vector<std::string> _names; | ||
| double _fallback{10.0}; | ||
| double _maxDepthFallback{1000.0}; | ||
| std::vector<Entry> _entries; | ||
| }; | ||
|
|
||
| REGISTER_DEPTH_LOOKUP(DepthLookupPolygon, "Polygon"); | ||
|
|
||
| } // anonymous namespace | ||
|
|
||
|
|
||
| } // namespace Seismology | ||
| } // namespace Seiscomp | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| /*************************************************************************** | ||
| * 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 <string> | ||
| #include <vector> | ||
|
|
||
| #include <seiscomp/config/config.h> | ||
| #include <seiscomp/core/baseobject.h> | ||
| #include <seiscomp/core/interfacefactory.h> | ||
| #include <seiscomp/core.h> | ||
|
|
||
|
|
||
| namespace Seiscomp { | ||
| namespace Seismology { | ||
|
|
||
|
|
||
| DEFINE_SMARTPOINTER(DepthLookup); | ||
|
|
||
| /** | ||
| * @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" 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). | ||
| * 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 | ||
| * never needs to supply or handle a fallback value. | ||
| */ | ||
| 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). | ||
| * | ||
| * 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 noexcept = 0; | ||
|
|
||
| /** | ||
| * @brief Return the maximum acceptable 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. | ||
| */ | ||
| virtual double fetchMaxDepth(double lat, double lon) const noexcept = 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 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<double> fetchCandidateDepths(double lat, double lon) const noexcept { | ||
| return {fetch(lat, lon)}; | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| DEFINE_INTERFACE_FACTORY(DepthLookup); | ||
|
|
||
|
|
||
| } // namespace Seismology | ||
| } // namespace Seiscomp | ||
|
|
||
|
|
||
| #define REGISTER_DEPTH_LOOKUP(Class, Service) \ | ||
| Seiscomp::Core::Generic::InterfaceFactory<Seiscomp::Seismology::DepthLookup, Class> \ | ||
| __##Class##InterfaceFactory__(Service) | ||
|
|
||
|
|
||
| #endif // SEISCOMP_SEISMOLOGY_DEPTHLOOKUP_H |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.