geopro/src/io/gpr/GprSurveyAssembler.cpp

159 lines
5.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "io/gpr/GprSurveyAssembler.hpp"
#include <algorithm>
#include <cstddef>
#include <fstream>
#include <numeric>
#include <sstream>
#include <stdexcept>
#include "io/gpr/GprGeometry.hpp"
#include "io/gpr/IprHeader.hpp"
#include "io/gpr/IprbReader.hpp"
#include "io/gpr/LocalPath.hpp"
namespace geopro::io::gpr {
namespace {
// 把 .iprb 路径的扩展名替换为 .iprh取最后一个 '.' 之后)。
std::string toHeaderPath(const std::string& iprbPath) {
const std::size_t dot = iprbPath.find_last_of('.');
const std::size_t slash = iprbPath.find_last_of("/\\");
// 仅当 '.' 在最后一个路径分隔符之后才视为扩展名。
if (dot != std::string::npos &&
(slash == std::string::npos || dot > slash)) {
return iprbPath.substr(0, dot) + ".iprh";
}
return iprbPath + ".iprh";
}
std::string readFileText(const std::string& path) {
std::ifstream f(localPath(path), std::ios::binary);
if (!f) {
throw std::runtime_error("无法打开文件: " + path);
}
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
// 由各通道 .ord 横偏、header、已读 BScan 段装配 GprSurvey。
// scans 已是待装配的道段(全线或 slabntraces 取各通道段道数最小值(对齐);
// x0 由调用方给出(全线=0slab=t0*dx。samples 校验、Y 升序置换、值转置同源。
geopro::core::GprSurvey assembleFromScans(const std::vector<double>& channelY0,
const std::vector<IprHeader>& headers,
const std::vector<BScan>& scans,
double x0) {
const std::size_t nchan = scans.size();
const int samples = scans.front().samples;
std::int64_t minTraces = scans.front().traces;
for (std::size_t c = 0; c < nchan; ++c) {
if (scans[c].samples != samples) {
throw std::runtime_error("通道 samples 不一致");
}
minTraces = std::min(minTraces, scans[c].traces);
}
geopro::core::GprSurvey survey;
survey.samples = samples;
survey.ntraces = static_cast<int>(minTraces);
survey.x0 = x0;
survey.dx = headers.front().distanceInterval;
survey.z0 = 0.0;
survey.dz = (samples > 1) ? depthOfSample(1, headers.front()) : 0.0;
// 按 Y 升序求置换order[c] = 升序第 c 位对应的原通道索引。
std::vector<std::size_t> order(nchan);
std::iota(order.begin(), order.end(), std::size_t{0});
std::stable_sort(order.begin(), order.end(),
[&](std::size_t a, std::size_t b) {
return channelY0[a] < channelY0[b];
});
survey.channelY.resize(nchan);
const std::size_t ntraces = static_cast<std::size_t>(survey.ntraces);
const std::size_t ns = static_cast<std::size_t>(samples);
survey.values.assign(nchan * ntraces * ns, 0.0);
for (std::size_t c = 0; c < nchan; ++c) {
const std::size_t src = order[c];
survey.channelY[c] = channelY0[src];
const BScan& bscan = scans[src];
for (std::size_t t = 0; t < ntraces; ++t) {
for (std::size_t s = 0; s < ns; ++s) {
survey.values[(c * ntraces + t) * ns + s] =
static_cast<double>(bscan.data[t * ns + s]);
}
}
}
return survey;
}
} // namespace
geopro::core::GprSurvey assembleGprSurvey(
const std::vector<std::string>& channelIprbPaths,
const std::string& ordPath) {
// 1. .ord -> 各通道横偏(文件序)。
const std::vector<double> channelY0 =
parseChannelXOffsets(readFileText(ordPath));
if (channelY0.size() != channelIprbPaths.size()) {
throw std::runtime_error(
"通道数不一致: .ord 有效通道数与 .iprb 路径数量不符");
}
const std::size_t nchan = channelIprbPaths.size();
if (nchan == 0) {
throw std::runtime_error("无通道可装配");
}
// 2. 各通道读 header + 全线 BScan。
std::vector<IprHeader> headers;
std::vector<BScan> scans;
headers.reserve(nchan);
scans.reserve(nchan);
for (const std::string& iprbPath : channelIprbPaths) {
const IprHeader h = parseIprHeader(readFileText(toHeaderPath(iprbPath)));
headers.push_back(h);
scans.push_back(readIprb(iprbPath, h));
}
// 3. 全线 x0=0其余校验/标尺/排序/置换由公共 helper 完成。
return assembleFromScans(channelY0, headers, scans, 0.0);
}
geopro::core::GprSurvey assembleGprSurveySlab(
const std::vector<std::string>& channelIprbPaths,
const std::string& ordPath,
std::int64_t t0, std::int64_t t1) {
// 1. .ord -> 各通道横偏(文件序)。
const std::vector<double> channelY0 =
parseChannelXOffsets(readFileText(ordPath));
if (channelY0.size() != channelIprbPaths.size()) {
throw std::runtime_error(
"通道数不一致: .ord 有效通道数与 .iprb 路径数量不符");
}
const std::size_t nchan = channelIprbPaths.size();
if (nchan == 0) {
throw std::runtime_error("无通道可装配");
}
// 2. 各通道读 header + 仅 [t0,t1) 道段readIprbRange 内做越界校验)。
std::vector<IprHeader> headers;
std::vector<BScan> scans;
headers.reserve(nchan);
scans.reserve(nchan);
for (const std::string& iprbPath : channelIprbPaths) {
const IprHeader h = parseIprHeader(readFileText(toHeaderPath(iprbPath)));
headers.push_back(h);
scans.push_back(readIprbRange(iprbPath, h, t0, t1));
}
// 3. x0 = t0*dx使该段世界 X 与全线对齐;其余同全线装配。
const double dx = headers.front().distanceInterval;
return assembleFromScans(channelY0, headers, scans,
static_cast<double>(t0) * dx);
}
} // namespace geopro::io::gpr