Skip to content
Merged
2 changes: 2 additions & 0 deletions libs/seiscomp/seismology/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
SET(SEISMOLOGY_SOURCES
depthlookup.cpp
firstmotion.cpp
locatorinterface.cpp
mb.cpp
Expand All @@ -9,6 +10,7 @@ SET(SEISMOLOGY_SOURCES
)

SET(SEISMOLOGY_HEADERS
depthlookup.h
firstmotion.h
ttt.h
regions.h
Expand Down
217 changes: 217 additions & 0 deletions libs/seiscomp/seismology/depthlookup.cpp
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();
Comment thread
comoglu marked this conversation as resolved.

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")});
Comment thread
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
119 changes: 119 additions & 0 deletions libs/seiscomp/seismology/depthlookup.h
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