159 lines
5.5 KiB
C++
159 lines
5.5 KiB
C++
#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 已是待装配的道段(全线或 slab);ntraces 取各通道段道数最小值(对齐);
|
||
// x0 由调用方给出(全线=0,slab=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
|