diff --git a/libs/seiscomp/seismology/CMakeLists.txt b/libs/seiscomp/seismology/CMakeLists.txt index 361c8854..8d3f8f71 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 00000000..be186f11 --- /dev/null +++ b/libs/seiscomp/seismology/depthlookup.cpp @@ -0,0 +1,194 @@ +/*************************************************************************** + * 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 &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 fetch(double, double) const override { + return _value; + } + + double fetchMaxDepth(double, double) const override { + return _value; + } + + private: + double _value{10.0}; +}; + +REGISTER_DEPTH_LOOKUP(DepthLookupConstant, "Constant"); + + +// --------------------------------------------------------------------------- +// DepthLookupPolygon +// --------------------------------------------------------------------------- + +/** + * 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) + */ +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"); + } + catch ( ... ) {} + + if ( names.empty() ) { + SEISCOMP_WARNING("DepthLookup/Polygon: no regions configured " + "under depths.polygon.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 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(_fallback); + } + } + return _fallback; + } + + private: + struct Entry { + const Geo::GeoFeature *feature{nullptr}; + double defaultDepth{0.0}; + std::optional maxDepth; + }; + + double _fallback{10.0}; + 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 00000000..6406a4d7 --- /dev/null +++ b/libs/seiscomp/seismology/depthlookup.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * 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 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: + 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; the implementation supplies its + * own configured fallback when no region/slab matches. + */ + 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. + */ + virtual double fetchMaxDepth(double lat, double lon) 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