/*------------------------------------------------------------------------------ * Copyright (c) 2023 by Bai Bing (seread@163.com) * See COPYING file for copying and redistribution conditions. * * Alians IT Studio. *----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "params.h" #include "version.h" // Target area 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; template std::pair get_actually_area_concerns(const std::vector &points) { auto x = ais::getAxisValues(points, ais::Axis3DType::X); auto y = ais::getAxisValues(points, ais::Axis3DType::Y); auto xMinMax = ais::getMinMax(x); auto yMinMax = ais::getMinMax(y); return {xMinMax, yMinMax}; } 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; } void dump_to_grd(ais::GridInterpolator &gi, const char *filename) { std::ofstream outfile; outfile.open(filename, std::ios::out); // header outfile << "DSAA" << std::endl; outfile << gi.header_info().c_str(); // data outfile << gi.data_info().c_str(); outfile.close(); } // Sample Points std::vector mock_sample_points(size_t count, std::vector &areaConcerns) { auto x = ais::getAxisValues(areaConcerns, ais::Axis3DType::X); auto y = ais::getAxisValues(areaConcerns, ais::Axis3DType::Y); auto [xMin, xMax] = ais::getMinMax(x); auto [yMin, yMax] = ais::getMinMax(y); std::vector points; srand(time(nullptr)); for (int i = 0; i < count; i++) { double x = std::rand() / double(RAND_MAX) * (xMax - xMin) + xMin; // min~max double y = std::rand() / double(RAND_MAX) * (yMax - yMin) + yMin; // min~max double z = std::rand() / double(RAND_MAX) * count / 5; // 0.00 ~ count/5 ais::PointXYZ point(x, y, z); points.push_back(point); } // write points to randoms.csv std::ofstream outfile; outfile.open("randoms.csv", std::ios::out); outfile << "x,y,z" << std::endl; for (auto &p : points) { outfile << p.x << "," << p.y << "," << p.z << std::endl; } outfile.close(); return points; } std::vector load_sample_points(const char *filename) { std::vector points; std::ifstream infile; infile.open(filename, std::ios::in); if (!infile.is_open()) { std::cout << "Failed to open sample points file: " << filename << std::endl; exit(-1); } std::string line; // show process std::cout << "|"; while (std::getline(infile, line)) { if (line[0] == '#' || line.empty()) continue; ais::PointXYZ p(line.c_str()); if (!p.isNan()) { points.push_back(p); if (points.size() > 0 && (points.size() % 10000 == 0)) { if(points.size() % 100000 == 0) std::cout << "*"; else if (points.size() % 50000 == 0) std::cout << "+"; else std::cout << "-"; } } } std::cout << "|" << std::endl; std::cout << "Total points: " << points.size() << std::endl; return points; } static ais::Settings settings; void parse_args(int argc, char **argv) { // parse params char c; int optIndex = 0; std::set onParams = {"on", "1", "true", "yes"}; while (-1 != (c = ais::getopt_long_2(argc, argv, ais::shortopts, ais::longopts, &optIndex, &ais::optarg2))) { switch (c) { case 's': settings.randomsFilename = ais::optarg2; break; case 't': settings.concernPointsFilename = ais::optarg2; break; case 'c': { int count = atoi(ais::optarg2); // only use mock random points when specified this option settings.useMockRandoms = true; settings.mockRandomCount = count ? count : 200; // default is 200 break; } case 'b': settings.breaklinesFilename = ais::optarg2; break; case 'a': settings.faultsFilename = ais::optarg2; break; case 'x': settings.xCount = atoi(ais::optarg2); break; case 'y': settings.yCount = atoi(ais::optarg2); break; case 'X': settings.targetXGridSize = atof(ais::optarg2); break; case 'Y': settings.targetYGridSize = atof(ais::optarg2); break; case 'i': settings.maxIteration = atoi(ais::optarg2); break; case 'r': settings.residual = atof(ais::optarg2); break; case 'f': settings.fillValue = atof(ais::optarg2); break; case 'e': settings.estimateFactor = atoi(ais::optarg2); break; case 'w': { // corner weight should between 4~256 int v = atoi(ais::optarg2); if (v < 4) v = 4; if (v > 256) v = 256; settings.cornerWeight = v; break; } case 'm': { std::string value = ais::optarg2; value = ais::tolower(value); settings.useMultiThread = onParams.find(value.c_str()) != onParams.end(); break; } case 'o': { settings.outputFilename = ais::optarg2; if (settings.outputFilename.empty()) settings.outputFilename = "output.grd"; break; } case 'l': { // 0 add fault point with out any constrict, default is 0 // 1-4 add fault point with different constrict level, 1-less 4-more auto faultEdgeLevel = (ais::optarg2 != NULL) ? atoi(ais::optarg2) : 0; if (faultEdgeLevel < 0 || faultEdgeLevel > 4) { std::cout << "Invalid param, the fault edge level must between 0 and 4, default is 0" << std::endl; exit(-1); } settings.faultEdgeLevel = faultEdgeLevel; break; } case '?': case 'h': ais::usage(argv[0]); exit(EXIT_SUCCESS); case 'v': std::cout << ais::VERSION << std::endl; exit(EXIT_SUCCESS); } } } int main(int argc, char **argv) { auto pwd = std::filesystem::current_path(); std::cout << pwd << std::endl; parse_args(argc, argv); // specialized area points to limited target grid area std::vector areaPoints; if (!settings.concernPointsFilename.empty()) { areaPoints = load_area_concerns(settings.concernPointsFilename.c_str()); } // load or mock simple points std::vector samplePoints; if (settings.useMockRandoms) { std::cout << "Mock random points (" << settings.mockRandomCount << " points) in area (0, 0)~(10, 10)"; areaPoints = build_area_by_concerns_points(0, 0, 10.0, 10.0); samplePoints = mock_sample_points(settings.mockRandomCount, areaPoints); } else { if (settings.randomsFilename.empty()) { settings.randomsFilename = "data.csv"; } std::cout << "Loading sample points from " << settings.randomsFilename << std::endl; samplePoints = load_sample_points(settings.randomsFilename.c_str()); } // build area points with actual points range or specific area auto [xMinMax, yMinMax] = areaPoints.empty() ? get_actually_area_concerns(samplePoints) : get_actually_area_concerns(areaPoints); areaPoints = build_area_by_concerns_points(xMinMax.first, yMinMax.first, xMinMax.second, yMinMax.second); // drop duplicated and out-of-area points, remove drop_duplicated_points due to performance problem // samplePoints = drop_duplicated_points(samplePoints); ais::filter_points(areaPoints, samplePoints); // adjust the target grid size by the specified grid size if (!std::isnan(settings.targetXGridSize) && settings.targetXGridSize != 0.0) { size_t gridCount = std::ceil((xMinMax.second - xMinMax.first) / settings.targetXGridSize); if (gridCount < 4) { std::cout << "X grid size is too big, can't build a valid grid" << std::endl; exit(-1); } if (settings.xCount != gridCount + 1) { xMinMax.second = xMinMax.first + gridCount * settings.targetXGridSize; areaPoints = build_area_by_concerns_points(xMinMax.first, yMinMax.first, xMinMax.second, yMinMax.second); settings.xCount = gridCount + 1; } } if (!std::isnan(settings.targetYGridSize) && settings.targetYGridSize != 0.0) { size_t gridCount = std::ceil((yMinMax.second - yMinMax.first) / settings.targetYGridSize); if (gridCount < 4) { std::cout << "Y grid size is too big, can't build a valid grid" << std::endl; exit(-1); } if (settings.yCount != gridCount + 1) { yMinMax.second = yMinMax.first + gridCount * settings.targetYGridSize; areaPoints = build_area_by_concerns_points(xMinMax.first, yMinMax.first, xMinMax.second, yMinMax.second); settings.yCount = gridCount + 1; } } // target matrix, point count = grid count + 1 ais::Shape targetShape = {settings.yCount, settings.xCount}; std::shared_ptr target = std::make_shared(targetShape); // fill target matrix with nan values for next iteration target->zeros(); // load breaklines from file std::vector breaklines; if (settings.breaklinesFilename.length() > 0) { ais::BreakLineFile blf(settings.breaklinesFilename.c_str()); for (auto &kv : blf.lines) { breaklines.emplace_back(kv.second); } } // load faults from file std::vector faults; if (settings.faultsFilename.length() > 0) { ais::FaultFile ff(settings.faultsFilename.c_str()); for (auto &kv : ff.faults) { faults.emplace_back(kv.second); } } // build Minimum-Curvature interpolation grid ais::GridInterpolator gi(samplePoints, areaPoints, target, breaklines, faults); gi.update_grid_params(settings.params()); std::cout << gi.params_info(); std::cout << gi.dump_data_statistics(); // start grid intersection from working thread auto gs = [&gi]() { gi.start(); }; std::thread tWorker(gs); // start output from other thread auto gOutput = [&gi]() { do { std::this_thread::sleep_for(std::chrono::seconds(1)); std::string msg = gi.get_progress_msg(); if (!msg.empty()) { std::cout << msg; } } while (!gi.is_end()); }; std::thread tOutput(gOutput); tWorker.join(); tOutput.join(); std::cout << "Interpolated result: " << std::endl; std::cout << "---------------------------------------" << std::endl; std::cout << gi.report(); std::cout << "---------------------------------------" << std::endl; std::cout << "Output file: " << settings.outputFilename << std::endl; dump_to_grd(gi, settings.outputFilename.c_str()); return 0; }