#include #include #include "utils/pystring.h" #include "SurfaceGridWrapper.h" #include #include #include "DrawModel\\BaseLib.h" #include "DrawOperator\\DrawLib.h" #include "utils/pystring.h" #include "GridSmooth.h" std::wstring StringToWstring(const std::string& wstr) { std::wstring res; int len = MultiByteToWideChar(CP_ACP, 0, wstr.c_str(), static_cast(wstr.size()), nullptr, 0); if (len == 0) { return L""; } std::vector buffer(len + 1); if (MultiByteToWideChar(CP_ACP, 0, wstr.c_str(), static_cast(wstr.size()), buffer.data(), len) == 0) { throw std::runtime_error("Failed to convert string to wstring."); } res.assign(buffer.begin(), buffer.end()); return res; } std::string WstringToString(std::wstring wstr) { std::string res; int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), static_cast(wstr.size()), nullptr, 0, nullptr, nullptr); if (len <= 0) { return ""; } res.resize(len); WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), static_cast(wstr.size()), &res[0], len, nullptr, nullptr); return res; } template void qDeleteAll(std::vector &vec) { for (auto* p : vec) { delete p; } vec.clear(); } // Target area static std::vector build_area_by_concerns_points(double xMin, double yMin, double xMax, double yMax) { std::vector points; points.push_back(ais::Point(xMin, yMin)); points.push_back(ais::Point(xMin, yMax)); points.push_back(ais::Point(xMax, yMin)); points.push_back(ais::Point(xMax, yMax)); points.push_back(ais::Point(xMin, yMin)); return points; } std::vector load_area_concerns(const char *filename) { std::vector points; std::ifstream ifs(filename); if (!ifs) { std::cout << "Failed to opening file " << filename << std::endl; std::cout << "Use sample points' coordinates value to established target area." << std::endl; return points; } std::string line; while (std::getline(ifs, line)) { // skip version number and object name if (line == "Version 2030" || line.find("Pline") != std::string::npos) continue; std::istringstream iss(line); std::vector tokens; std::string token; while (std::getline(iss, token, ',')) { tokens.push_back(token); } if (!tokens.empty()) points.push_back(ais::Point(std::stod(tokens[0]), std::stod(tokens[1]))); } return points; } using axisRange = std::pair; /** * * \\param points * \\return x 轴和范围 和 y 轴的范围 */ static std::pair get_actually_area_concerns(const std::vector &points) { // 获取所有 x 轴 auto x = ais::getAxisValues(points, ais::Axis3DType::X); // 获取所有 y 轴 auto y = ais::getAxisValues(points, ais::Axis3DType::Y); // 获取 x 的范围 auto xMinMax = ais::getMinMax(x); // 获取 y 的范围 auto yMinMax = ais::getMinMax(y); return { xMinMax, yMinMax }; } /** * 移除重复的点 * \\param points * \\return */ static std::vector drop_duplicated_points(std::vector &points) { std::vector result; std::sort(points.begin(), points.end()); result.reserve(points.size()); for (auto &p : points) { if (std::find(std::execution::par, result.begin(), result.end(), p) == result.end()) result.push_back(p); } return result; } /** * 输出结果到文件 * \\param gi * \\param filename 目标文件 */ static void dump_to_grd(ais::GridInterpolator &gi, const std::string &filename) { std::ofstream outfile; outfile.open(filename, std::ios::out); if (!outfile.is_open()) { std::string errMsg = "Failed to open grd file: " + filename; throw std::runtime_error(errMsg); } // header outfile << "DSAA" << std::endl; outfile << gi.header_info().c_str(); // data outfile << gi.data_info().c_str(); outfile.close(); } /** * 加载采样文件 * \\param filename * \\return */ static std::vector load_sample_points(const std::string &filename) { std::vector points; std::ifstream infile; infile.open(filename, std::ios::in); if (!infile.is_open()) { std::string errMsg = "Failed to open sample points file: " + filename; throw std::runtime_error(errMsg); } std::string line; while (std::getline(infile, line)) { line = pystring::strip(line); if (line.empty() || line[0] == '#') continue; ais::PointXYZ p(line.c_str()); if (!p.isNan()) points.push_back(p); } return points; } static void AddContourCurve(CXy* pXy, vector* curves, vector* layer) { for (size_t i = 0; i < curves->size(); i++) { CCurve* pCurve = curves->at(i); if (pCurve == NULL) continue; CString* pName = layer->at(i); CCurveEx *ce = new CCurveEx(pCurve->num); for (int i = 0; i < ce->num; i++) { ce->x[i] = pCurve->x[i]; ce->y[i] = pCurve->y[i]; ce->z[i] = pCurve->z[i]; } ce->nPoint = pCurve->nPoint; ce->GetLocation(); POSITION pos = NULL; if (pCurve->name) ce->SetName(pCurve->name); pos = pXy->AddElement(ce, DOUBLEFOX_CURVE); pXy->SetElementLayer(pos, *pName); } } /** * 判断 dx dY 坐标是否在其中任意一个区域 * * \\param vec 区域集合,虽然是折线类,但这里是一个个闭合的区域 * \\param dX * \\param dY * \\return */ static bool any_contains(std::vector &vec, double dX, double dY) { return std::any_of(vec.begin(), vec.end(), [dX, dY](CCurveEx *pBorder) { return (pBorder->IsInside(dX, dY)); }); } /** * 创建带网格的 CDimension2D 对象,如果有边框,则将边框外的值设置为非法值 * * \\param xMin * \\param yMin * \\param matrix * \\param nCols * \\param nRows * \\param dx * \\param dy * \\param zMin * \\param zMax * \\param insertTimes * \\param borders 边框集合,用户可能选中多个边框 * \\return */ static CDimension2D *CreateGridData(double xMin, double yMin, std::shared_ptr matrix, int nCols, int nRows, double dx, double dy, double zMin, double zMax, int insertTimes, std::vector &borders, double fillValue) { static const double valueNull = -1E301; //int nCount = nCols * nRows; auto *pDfg = new CDimension2D(); pDfg->Create(nCols, nRows, xMin, yMin, dx, dy); // any_contains 里面调了 CCurveEx::IsInSide,这个函数不是线程安全的 // 不要轻易对下面这个循环做多线程或 openmp 加速 for (int i = 0; i < nCols; i++) { for (int j = 0; j < nRows; j++) { double dX = pDfg->x(i); double dY = pDfg->y(j); double value = matrix->at(j, i); if (std::isinf(value) || std::isnan(value)) { value = valueNull; } // 检查value是否在[zMin, zMax]范围内 if (value < zMin || value > zMax) { value = valueNull; } if (borders.empty()) { pDfg->SetValue(i, j, value); continue; } if (any_contains(borders, dX, dY)) { pDfg->SetValue(i, j, value); } else { pDfg->SetValue(i, j, valueNull); } } } pDfg->range[0] = zMin; pDfg->range[1] = zMax; return pDfg; } static void CreateBorder(std::shared_ptr pXy, std::vector &ccurves) { CLayer* pLayerBorder = pXy->FindAddLayer("边界11"); for (auto *pCurve : ccurves) { POSITION posNew = pXy->AddElement(pCurve, DOUBLEFOX_CURVE); pXy->SetElementLayer(posNew, pLayerBorder); } } /** * 生成断层 * * \\param faultFile * \\param pXy * \\param pDfg * \\return */ static bool CreateFaults(CString faultFile, std::shared_ptr pXy, CDimension2D *pDfg) { // 生成断层 std::unique_ptr pXyFaults = std::make_unique(); if (!pXyFaults->ReadWithExtension(faultFile)) { return false; } CLayer* pLayerFault = pXy->FindAddLayer("断层"); CPtrList *pl = pXyFaults->GetValueList(); for (POSITION pos = pl->GetHeadPosition(); pos != nullptr; pl->GetNext(pos)) { COne *pOne = pXyFaults->GetAt(pos); if (pOne->GetType() == DOUBLEFOX_CURVE) { CCurveEx* pCurveFault = (CCurveEx*)(pOne->GetValue()); CCurveEx* pCurveNew = new CCurveEx; (*pCurveNew) = (*pCurveFault); POSITION posNew = pXy->AddElement(pCurveNew, DOUBLEFOX_CURVE); pXy->SetElementLayer(posNew, pLayerFault); pDfg->Faultage(*(CCurve*)pCurveNew); } } return true; } /** * 生成等值线 * * \\param pXy * \\param pMeshNew * \\param pDfg * \\param contourStep * \\param contourMarkStep * \\return */ static void CreateContourLines(std::shared_ptr pXy, CMesh *pMeshNew, CDimension2D *pDfg, double contourStep, int contourMarkStep) { if (abs(contourStep) < 1E-5) { return; } CString strLayerMark = _T("Layer:\\等值线\\标注"); CString strLayerOther = _T("Layer:\\等值线\\无标注"); CString curLayerName = pXy->GetCurrentLayer()->GetPathName(); CLayer* pMarkLayer = pXy->FindLayer(strLayerMark); CLayer* pOtherLayer = pXy->FindLayer(strLayerOther); if (pMarkLayer == nullptr) { pMarkLayer = pXy->FindAddLayer(strLayerMark); pMarkLayer->HowToViewCurve = new CHowToViewCurve(); pMarkLayer->HowToViewCurve->EnableDrawSourceCurve(FALSE); CCurveInName* pInName = new CCurveInName(); CRect8 rect = pMeshNew->GetRect(); pInName->text_h = rect.Width() / 300; pInName->m_size.cx = pInName->text_h*0.06; pInName->color = RGB(0, 0, 0); pMarkLayer->HowToViewCurve->Add(pInName); } if (pOtherLayer == nullptr) { pOtherLayer = pXy->FindAddLayer(strLayerOther); pOtherLayer->HowToViewCurve = new CHowToViewCurve(); pOtherLayer->HowToViewCurve->EnableDrawSourceCurve(FALSE); CCurveProperties* pview = new CCurveProperties(); pview->color = RGB(0, 0, 0); pOtherLayer->HowToViewCurve->Add(pview); } std::vector pCurves; std::vector pLayers; pMeshNew->ContourCreate(&pCurves, &pLayers, contourStep, contourMarkStep, strLayerMark, strLayerOther, pDfg->range[0], pDfg->range[1]); AddContourCurve(pXy.get(), &pCurves, &pLayers); qDeleteAll(pCurves); qDeleteAll(pLayers); } /** * 将网格信息保存到文件中,根据文件后缀名自动生成对应格式 * * \\param pXy * \\param outputFile 要保存的文件名 * \\param contourStep * \\param contourMarkStep * \\param insertTimes * \\param pDfg 要保存的数据 * \\return */ static bool SaveFile(CString outputFile, CString faultFile , double contourStep, int contourMarkStep, int insertTimes, int smoothTimes , CDimension2D *pDfg, std::vector &borders) { std::shared_ptr pXy = std::make_shared(); // 添加网格 CMesh* pMeshNew = new CMesh(); // 网格类 CLayer* pLayer = pXy->FindAddLayer("背景"); POSITION posNew = pXy->AddElement(pMeshNew, DOUBLEFOX_MESH); pXy->SetElementLayer(posNew, pLayer); // 设置网格 pMeshNew->SetMesh(pDfg, MESH_DFG, FALSE); pMeshNew->m_nTimes = insertTimes; // 生成边框 CreateBorder(pXy, borders); // 生成断层 CreateFaults(faultFile, pXy, pDfg); for (int i = 0; i < smoothTimes; i++) { CGridSmooth::ExcuteSmooth(pXy, pDfg); } // 生成等值线 CreateContourLines(pXy, pMeshNew, pDfg, contourStep, contourMarkStep); pMeshNew->EnableUpdateRuler(TRUE); pMeshNew->UpdateColorRuler(); pXy->SaveAsWithExtension(outputFile); return true; } // 获取边界线/边框 static std::vector get_border(CString borderFile) { if (!CFindFileEx::IsFileExists(borderFile)) { return {}; } std::shared_ptr pXy = std::make_shared(); borderFile = borderFile.Trim(); if (borderFile.GetLength() <= 0 || borderFile == "NULL") { return {}; } if (!pXy->ReadWithExtension(borderFile)) { return {}; } std::vector result; CPtrList* pl = pXy->GetValueList(); for (POSITION pos = pl->GetHeadPosition(); pos != nullptr; pl->GetNext(pos)) { COne* pOne = (COne *)pl->GetAt(pos); if (pOne->GetType() == DOUBLEFOX_CURVE) { CCurveEx *pCurve = (CCurveEx*)(pOne->GetValue()); CCurveEx *pCurveNew = new CCurveEx(); *pCurveNew = *pCurve; result.push_back(pCurveNew); } } return result; } static bool get_borderRange(CString borderFile, double& xMin, double& yMin, double& xMax, double& yMax) { if (!CFindFileEx::IsFileExists(borderFile)) { return false; } std::shared_ptr pXy = std::make_shared(); borderFile = borderFile.Trim(); if (borderFile.GetLength() <= 0 || borderFile == "NULL") { return false; } if (!pXy->ReadWithExtension(borderFile)) { return false; } xMin = 1e100; yMin = 1e100; xMax = -1e100; yMax = -1e100; CPtrList* pl = pXy->GetValueList(); for (POSITION pos = pl->GetHeadPosition(); pos != nullptr; pl->GetNext(pos)) { COne* pOne = (COne *)pl->GetAt(pos); if (pOne->GetType() == DOUBLEFOX_CURVE) { CCurveEx *pCurve = (CCurveEx*)(pOne->GetValue()); for (int i = 0; i < pCurve->num; i++) { if (pCurve->x[i] < xMin) { xMin = pCurve->x[i]; } if (pCurve->x[i] > xMax) { xMax = pCurve->x[i]; } if (pCurve->y[i] < yMin) { yMin = pCurve->y[i]; } if (pCurve->y[i] > yMax) { yMax = pCurve->y[i]; } } } } return true; } static void dump_to_cxy(ais::GridInterpolator &gi, double fillValue, CString faultFile, CString borderFile, CString filename , double contourStep, int contourMarkStep, int insertTimes,int smoothTimes, double zValueMin, double zValueMax) { std::map params = gi.params(); double xMin = std::any_cast(params["x_min"]); double xMax = std::any_cast(params["x_max"]); double yMin = std::any_cast(params["y_min"]); double yMax = std::any_cast(params["y_max"]); double zMin = zValueMin;// std::any_cast(params["z_min"]); double zMax = zValueMax;// std::any_cast(params["z_max"]); size_t nCols = std::any_cast(params["x_nodes"]); size_t nRows = std::any_cast(params["y_nodes"]); double dx = std::any_cast(params["x_grid_size"]); double dy = std::any_cast(params["y_grid_size"]); std::shared_ptr matrix = gi.matrix(); std::vector curves = get_border(borderFile); auto pDfg = CreateGridData(xMin, yMin, matrix, (int)nCols, (int)nRows, dx, dy, zMin, zMax, 0, curves, fillValue); SaveFile(filename, faultFile, contourStep, contourMarkStep, insertTimes, smoothTimes, pDfg, curves); } /** * 加载断裂线文件 * \\param filename 文件名 * \\return */ static std::vector loadBreadLines(const std::string &filename) { std::vector result; ais::BreakLineFile blf(filename.c_str()); for (auto &kv : blf.lines) { result.emplace_back(kv.second); } return result; } /** * 加载断层 * \\param filename 文件名 * \\return */ static std::vector loadFaults(const std::string &filename) { std::vector result; ais::FaultFile ff(filename.c_str()); for (auto &kv : ff.faults) { result.emplace_back(kv.second); } return result; } double range(const axisRange &minMax) { return minMax.second - minMax.first; } size_t calcYNodeCount(const axisRange &xMinMax, const axisRange &yMinMax, size_t xNodeCount) { double xRange = range(xMinMax); double yRange = range(yMinMax); double step = xRange / xNodeCount; return std::ceil(yRange / step); } bool IsValidGridSize(double gridSize) { return !std::isnan(gridSize) && gridSize > 0.0; } static void logMessage(const std::string_view& message) { std::time_t now = std::time(nullptr); std::tm* timeinfo = std::localtime(&now); char buffer[80]; memset(buffer, 0, sizeof(buffer)); std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo); std::ofstream logFile("log.txt", std::ios::app); if (logFile.is_open()) { // 将日志消息写入文件 logFile << "[" << buffer << "] " << message << std::endl; } } static void logMessage(int lineNO) { logMessage(std::to_string(lineNO)); } // 如果没有正确设置表格间隔、迭代次数(太少了不行),运算会跑飞 // 迭代次数 maxIteration =xNodeCount*yNodeCount*2;fillValue,TODO:输出时处理 // 网格数对效果影响大,需要根据经验设置 bool BuildMinCurvatureGrid3Impl( const wchar_t* sourcePointFile, const wchar_t* faultFile, const wchar_t* borderFile, size_t xNodeCount, int maxIteration, double residual, double fillValue, int faultEdgeLevel, const wchar_t* outputFile, int estimateFactor, int cornerWeight, double contourStep, int contourMarkStep, int insertTimes, int smoothTimes, double xMin, double yMin, double xMax, double yMax ) { if (cornerWeight < 4) cornerWeight = 4; if (cornerWeight > 256) cornerWeight = 256; // -1 表示默认值,迭代两万次,这里为了保证,使用 <= 0 判断 if (maxIteration <= 0) { maxIteration = 20000; } if (faultFile == nullptr) { faultFile = L""; } if (borderFile == nullptr) { borderFile = L""; } if (sourcePointFile == nullptr || outputFile == nullptr) { return false; } try { std::string sourcePointFileStr = WstringToString(sourcePointFile); std::string faultFileStr = WstringToString(faultFile); std::string outputFileStr = WstringToString(outputFile); sourcePointFileStr = pystring::strip(sourcePointFileStr); faultFileStr = pystring::strip(faultFileStr); outputFileStr = pystring::strip(outputFileStr); std::vector samplePoints = load_sample_points(sourcePointFileStr); auto[xMinMax, yMinMax] = get_actually_area_concerns(samplePoints); std::vector areaPoints = build_area_by_concerns_points(xMinMax.first, yMinMax.first, xMinMax.second, yMinMax.second); if (xMin < xMax && yMin < yMax) { areaPoints = build_area_by_concerns_points(xMin, yMin, xMax, yMax); } CString cstrBorderFile(borderFile); if (cstrBorderFile.GetLength() > 0 && cstrBorderFile!= "NULL") { double dXMin = 0, dYMin = 0, dXMax = 0, dYMax = 0; bool bSuccess = get_borderRange(cstrBorderFile, dXMin, dYMin, dXMax, dYMax); if (bSuccess) { areaPoints = build_area_by_concerns_points(dXMin, dYMin, dXMax, dYMax); } } // 移除重复和超出区域的点 std::vector effectivePoints = ais::filter_points(areaPoints, samplePoints); if (xNodeCount == 0) { xNodeCount = 101; // x 轴 默认 101 个网格 } size_t xCount = xNodeCount; size_t yCount = calcYNodeCount(xMinMax, yMinMax, xNodeCount); // 设置表格大小 ais::Shape targetShape = { yCount, xCount }; std::shared_ptr target = std::make_shared(targetShape); // fill target matrix with nan values for next iteration std::cout << "targetShape.yCount = " << yCount << ", " << "xCount = " << xCount << "\n"; target->zeros(); std::vector breaklines; // 从文件中加载断层 std::vector faults; if (pystring::strip(faultFileStr).length() > 0 && faultFileStr != "NULL") { faults = loadFaults(faultFileStr); } if (effectivePoints.empty()) { return false; } ais::GridInterpolator gi(effectivePoints, areaPoints, target, breaklines, faults); std::map paramsOrigin = gi.params(); double zMin = std::any_cast(paramsOrigin["z_min"]); double zMax = std::any_cast(paramsOrigin["z_max"]); if (maxIteration <= 0) { maxIteration = static_cast(xCount * yCount * 2); // 推荐迭代次数 } std::map params { { "max_iteration", (size_t)maxIteration }, { "residual", residual }, { "estimate_factor", (int32_t)estimateFactor }, { "fault_edge_level", (int8_t)faultEdgeLevel }, { "fill_value", fillValue }, { "corner_weight", (short)cornerWeight }, { "use_multi_threads", true }, { "detail_level", (int8_t)0 }, }; gi.update_grid_params(params); std::cout << gi.params_info(); // start grid intersection auto gs = [&gi]() { gi.start(); }; std::thread t(gs); do { std::this_thread::sleep_for(std::chrono::milliseconds(500)); // 为什么要 sleep ? 发现原来是里面没有阻塞,这里不 sleep 就会疯狂去获取 std::string msg = gi.get_progress_msg(); if (!msg.empty()) { std::cout << msg; } } while (!gi.is_end()); t.join(); std::cout << "Interpolated result: " << std::endl; std::cout << "---------------------------------------" << std::endl; std::cout << gi.report(); std::cout << "---------------------------------------" << std::endl; std::cout << "Output file: " << outputFileStr << std::endl; paramsOrigin = gi.params(); zMin = std::any_cast(paramsOrigin["z_min"]); zMax = std::any_cast(paramsOrigin["z_max"]); dump_to_cxy(gi, fillValue, faultFile, borderFile, outputFile, contourStep, contourMarkStep, insertTimes, smoothTimes, zMin, zMax); //dump_to_grd(gi, outputFileStr.c_str()); } catch (std::exception e) { std::cout << e.what() << std::endl; return false; } return true; } static std::optional ExecuteCommand(LPSTR command) { STARTUPINFO si; PROCESS_INFORMATION pi; DWORD exitCode = 0; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); // 创建新的进程 if (CreateProcess(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { // 等待进程结束 WaitForSingleObject(pi.hProcess, INFINITE); // 获取进程的返回值 GetExitCodeProcess(pi.hProcess, &exitCode); // 关闭进程和线程的句柄 CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } else { std::cout << "Failed to create process" << std::endl; return {}; } std::cout << "Process exited with code: " << exitCode << std::endl; return exitCode; } /* bool BuildMinCurvatureGrid3( const wchar_t * sourcePointFile, const wchar_t * faultFile, const wchar_t * borderFile, size_t xNodeCount, int maxIteration, double residual, double fillValue, int faultEdgeLevel, const wchar_t * outputFile, int estimateFactor, int cornerWeight, double contourStep, int contourMarkStep, int insertTimes, double xMin, double yMin, double xMax, double yMax) { std::vector strArea{ std::to_string(xMin), std::to_string(yMin), std::to_string(xMax), std::to_string(yMax) }; std::map commandArgs { { "--source-point-file", wstringToString(sourcePointFile) }, { "--fault", wstringToString(faultFile) }, { "--breakline", wstringToString(borderFile) }, { "--x-nodes-count", std::to_string(xNodeCount) }, { "--max-iteration", std::to_string(maxIteration) }, { "--residual", std::to_string(residual) }, { "--fill-value", std::to_string(fillValue) }, { "--fault-edge-level", std::to_string(faultEdgeLevel) }, { "--output-file", wstringToString(outputFile) }, { "--estimate-factor", std::to_string(estimateFactor) }, { "--corner-weight", std::to_string(cornerWeight) }, { "--contour-step", std::to_string(contourStep) }, { "--contour-mark-step", std::to_string(contourMarkStep) }, { "--insert-times", std::to_string(insertTimes) }, { "--area", pystring::join(",", strArea) }, }; std::stringstream ss; ss << "SurfaceGrid.exe "; for (auto &pair : commandArgs) { ss << pair.first << " " << pair.second << " "; } string command = ss.str(); return ExecuteCommand(const_cast(command.c_str())) == EXIT_SUCCESS; } */