#include "StdAfx.h" #include "GenerateSEC.h" #include #include #include // --- GEOS 上下文管理 --- void geos_message_handler(const char* fmt, ...) { /* 忽略通知 */ } class GEOSContext { public: GEOSContext() { handle = initGEOS_r(geos_message_handler, geos_message_handler); } ~GEOSContext() { finishGEOS_r(handle); } GEOSContextHandle_t handle; }; static GEOSContext g_geos; // --- 辅助工具 --- inline double Round6(double v) { return std::floor(v * 1e6 + 0.5) / 1e6; } /** * 核心逻辑:深度复刻 Python export_to_csv_single_line * 1. 动态更新 Shell (包含已连接孔洞的点) * 2. 6位精度搜索最近点 * 3. 严格的跳转重复点序列 */ static std::vector GEOSPolygonToSingleLine(const GEOSGeometry* poly) { GEOSContextHandle_t h = g_geos.handle; if (!poly || GEOSGeomTypeId_r(h, poly) != GEOS_POLYGON) return {}; auto extract_ring = [&](const GEOSGeometry* ring) { const GEOSCoordSequence* seq = GEOSGeom_getCoordSeq_r(h, ring); uint32_t size; GEOSCoordSeq_getSize_r(h, seq, &size); std::vector pts; for (uint32_t i = 0; i < size - 1; ++i) { // [:-1] 移除闭合点 double x, y; GEOSCoordSeq_getXY_r(h, seq, i, &x, &y); pts.push_back({ x, y }); } return pts; }; // 1. 提取初始外环 std::vector shell = extract_ring(GEOSGetExteriorRing_r(h, poly)); // 2. 提取所有内孔 int num_holes = GEOSGetNumInteriorRings_r(h, poly); std::vector> holes; for (int i = 0; i < num_holes; ++i) { holes.push_back(extract_ring(GEOSGetInteriorRingN_r(h, poly, i))); } // 3. 贪婪连接逻辑 while (!holes.empty()) { double min_d2 = (std::numeric_limits::max)(); int best_hole_idx = -1; size_t best_s = 0, best_h = 0; for (int i = 0; i < (int)holes.size(); ++i) { const auto& hole = holes[i]; for (size_t s_idx = 0; s_idx < shell.size(); ++s_idx) { double sx = Round6(shell[s_idx].x0); double sy = Round6(shell[s_idx].y0); for (size_t h_idx = 0; h_idx < hole.size(); ++h_idx) { double hx = Round6(hole[h_idx].x0); double hy = Round6(hole[h_idx].y0); double d2 = (sx - hx) * (sx - hx) + (sy - hy) * (sy - hy); // 严格小于,模拟 np.argmin 的第一个索引优先原则 if (d2 < min_d2 - 1e-12) { min_d2 = d2; best_hole_idx = i; best_s = s_idx; best_h = h_idx; } } } } // 4. 执行拼接 std::vector target_hole = holes[best_hole_idx]; holes.erase(holes.begin() + best_hole_idx); CPoint2D conn_shell = shell[best_s]; CPoint2D conn_hole = target_hole[best_h]; std::vector next_shell; next_shell.reserve(shell.size() + target_hole.size() + 2); for (size_t i = 0; i <= best_s; ++i) next_shell.push_back(shell[i]); for (size_t i = 0; i < target_hole.size(); ++i) { next_shell.push_back(target_hole[(best_h + i) % target_hole.size()]); } next_shell.push_back(conn_hole); next_shell.push_back(conn_shell); for (size_t i = best_s + 1; i < shell.size(); ++i) next_shell.push_back(shell[i]); shell = next_shell; // 更新 shell } if (!shell.empty()) shell.push_back(shell[0]); // 最后补回闭合点 return shell; } /** * 基础生成逻辑:对齐 Python _generate_base_shapes */ static GEOSGeometry* InternalGenerateBase(const std::vector& wells, double w, double h, double ang, double gap) { GEOSContextHandle_t handle = g_geos.handle; std::vector polys; double rad = ang * 3.14159265358979 / 180.0; for (const auto& well : wells) { GEOSCoordSequence* seq = GEOSCoordSeq_create_r(handle, 5, 2); double dx[] = { -w / 2, w / 2, w / 2, -w / 2, -w / 2 }; double dy[] = { -h / 2, -h / 2, h / 2, h / 2, -h / 2 }; for (int i = 0; i < 5; ++i) { double rx = dx[i] * cos(rad) - dy[i] * sin(rad); double ry = dx[i] * sin(rad) + dy[i] * cos(rad); GEOSCoordSeq_setXY_r(handle, seq, i, well.x0 + rx, well.y0 + ry); } GEOSGeometry* ring = GEOSGeom_createLinearRing_r(handle, seq); polys.push_back(GEOSGeom_createPolygon_r(handle, ring, nullptr, 0)); } // 1. Unary Union (对应 Python unary_union) GEOSGeometry* coll = GEOSGeom_createCollection_r(handle, GEOS_MULTIPOLYGON, polys.data(), (uint32_t)polys.size()); GEOSGeometry* merged = GEOSUnaryUnion_r(handle, coll); // 2. Simplify (对应 Python simplify(0.01)) GEOSGeometry* simplified = GEOSSimplify_r(handle, merged, 0.01); GEOSGeom_destroy_r(handle, merged); // 3. Gap Buffer (斜接策略) if (gap > 0.01) { GEOSBufferParams* params = GEOSBufferParams_create_r(handle); GEOSBufferParams_setJoinStyle_r(handle, params, GEOSBUF_JOIN_MITRE); GEOSBufferParams_setMitreLimit_r(handle, params, 5.0); GEOSGeometry* temp = GEOSBufferWithParams_r(handle, simplified, params, gap / 2.0); GEOSGeom_destroy_r(handle, simplified); simplified = GEOSBufferWithParams_r(handle, temp, params, -gap / 2.0); GEOSGeom_destroy_r(handle, temp); GEOSBufferParams_destroy_r(handle, params); } return simplified; } NItem::GenerateSECReserve::GenerateSECReserve() { } NItem::GenerateSECReserve::~GenerateSECReserve() { } std::vector> NItem::GenerateSECReserve::CalculatePDP( const std::vector& wells, double width, double height, double angle_deg, double gap, const std::vector& boundaryPoints, bool useClip) { GEOSContextHandle_t h = g_geos.handle; GEOSGeometry* pdp = InternalGenerateBase(wells, width, height, angle_deg, gap); if (useClip && boundaryPoints.size() >= 3) { GEOSCoordSequence* seq = GEOSCoordSeq_create_r(h, (uint32_t)boundaryPoints.size() + 1, 2); for (uint32_t i = 0; i < boundaryPoints.size(); ++i) { GEOSCoordSeq_setXY_r(h, seq, i, boundaryPoints[i].x0, boundaryPoints[i].y0); } GEOSCoordSeq_setXY_r(h, seq, (uint32_t)boundaryPoints.size(), boundaryPoints[0].x0, boundaryPoints[0].y0); GEOSGeometry* b_ring = GEOSGeom_createLinearRing_r(h, seq); GEOSGeometry* b_poly = GEOSGeom_createPolygon_r(h, b_ring, nullptr, 0); GEOSGeometry* clipped = GEOSIntersection_r(h, pdp, b_poly); GEOSGeom_destroy_r(h, pdp); GEOSGeom_destroy_r(h, b_poly); pdp = clipped; } std::vector> results; int n = GEOSGetNumGeometries_r(h, pdp); for (int i = 0; i < n; ++i) { const GEOSGeometry* g = GEOSGetGeometryN_r(h, pdp, i); GEOSGeometry* g_simple = GEOSSimplify_r(h, g, 0.01); // 导出前 simplify results.push_back(GEOSPolygonToSingleLine(g_simple)); GEOSGeom_destroy_r(h, g_simple); } GEOSGeom_destroy_r(h, pdp); return results; } void NItem::GenerateSECReserve::CalculatePDNP(const std::vector& wells, double width, double height, double angle_deg, double gap, bool useClip, const std::vector& boundaryPoints, std::vector>& pdnpLines) { GEOSGeometry* pdnp = InternalGenerateBase(wells, width, height, angle_deg, gap); InternalClipAndExport(pdnp, boundaryPoints, useClip, pdnpLines); } void NItem::GenerateSECReserve::CalculatePUD(const std::vector& wells, double width, double height, double angle_deg, double gap, double pud_mult, bool useClip, const std::vector& boundaryPoints, std::vector>& pudLines) { GEOSContextHandle_t h = g_geos.handle; // 生成作为基准的 PDNP 形状 GEOSGeometry* base_pdnp = InternalGenerateBase(wells, width, height, angle_deg, gap); // 执行扩边运算 GEOSBufferParams* params = GEOSBufferParams_create_r(h); GEOSBufferParams_setJoinStyle_r(h, params, GEOSBUF_JOIN_MITRE); GEOSBufferParams_setMitreLimit_r(h, params, 5.0); GEOSGeometry* expanded = GEOSBufferWithParams_r(h, base_pdnp, params, width * pud_mult); // PUD = 扩边区域 - 原始区域 GEOSGeometry* pud = GEOSDifference_r(h, expanded, base_pdnp); // 清理中间变量 GEOSGeom_destroy_r(h, base_pdnp); GEOSGeom_destroy_r(h, expanded); GEOSBufferParams_destroy_r(h, params); // 裁剪并导出 InternalClipAndExport(pud, boundaryPoints, useClip, pudLines); } /** * 内部辅助函数:处理裁剪、简化并导出单线点集 * 为了避免内存泄漏,此函数会消耗(destroy)传入的 geom */ void NItem::GenerateSECReserve::InternalClipAndExport(GEOSGeometry* geom, const std::vector& boundaryPoints, bool useClip, std::vector>& outLines) { GEOSContextHandle_t h = g_geos.handle; if (!geom) return; // 1. 裁剪逻辑 GEOSGeometry* finalGeom = geom; if (useClip && boundaryPoints.size() >= 3) { GEOSCoordSequence* seq = GEOSCoordSeq_create_r(h, (uint32_t)boundaryPoints.size() + 1, 2); for (uint32_t i = 0; i < boundaryPoints.size(); ++i) { GEOSCoordSeq_setXY_r(h, seq, i, boundaryPoints[i].x0, boundaryPoints[i].y0); } GEOSCoordSeq_setXY_r(h, seq, (uint32_t)boundaryPoints.size(), boundaryPoints[0].x0, boundaryPoints[0].y0); GEOSGeometry* b_poly = GEOSGeom_createPolygon_r(h, GEOSGeom_createLinearRing_r(h, seq), nullptr, 0); finalGeom = GEOSIntersection_r(h, geom, b_poly); GEOSGeom_destroy_r(h, geom); GEOSGeom_destroy_r(h, b_poly); } // 2. 导出逻辑 if (finalGeom) { int n = GEOSGetNumGeometries_r(h, finalGeom); for (int i = 0; i < n; ++i) { const GEOSGeometry* g = GEOSGetGeometryN_r(h, finalGeom, i); GEOSGeometry* g_simple = GEOSSimplify_r(h, g, 0.01); outLines.push_back(GEOSPolygonToSingleLine(g_simple)); GEOSGeom_destroy_r(h, g_simple); } GEOSGeom_destroy_r(h, finalGeom); } } void NItem::GenerateSECReserve::CalculateExpansion( const std::vector& wells, double width, double height, double angle_deg, double gap, double pud_mult, const std::vector& boundaryPoints, bool useClip, std::vector>& pdnpLines, std::vector>& pudLines) { GEOSContextHandle_t h = g_geos.handle; GEOSGeometry* pdnp = InternalGenerateBase(wells, width, height, angle_deg, gap); // PUD 派生 GEOSBufferParams* params = GEOSBufferParams_create_r(h); GEOSBufferParams_setJoinStyle_r(h, params, GEOSBUF_JOIN_MITRE); GEOSBufferParams_setMitreLimit_r(h, params, 5.0); GEOSGeometry* expanded = GEOSBufferWithParams_r(h, pdnp, params, width * pud_mult); GEOSGeometry* pud = GEOSDifference_r(h, expanded, pdnp); GEOSGeom_destroy_r(h, expanded); GEOSBufferParams_destroy_r(h, params); // 裁剪 (逻辑同 PDP)... auto clip_func = [&](GEOSGeometry* geom) { if (!useClip || boundaryPoints.size() < 3) return geom; GEOSCoordSequence* seq = GEOSCoordSeq_create_r(h, (uint32_t)boundaryPoints.size() + 1, 2); for (uint32_t i = 0; i < boundaryPoints.size(); ++i) { GEOSCoordSeq_setXY_r(h, seq, i, boundaryPoints[i].x0, boundaryPoints[i].y0); } GEOSCoordSeq_setXY_r(h, seq, (uint32_t)boundaryPoints.size(), boundaryPoints[0].x0, boundaryPoints[0].y0); GEOSGeometry* b_poly = GEOSGeom_createPolygon_r(h, GEOSGeom_createLinearRing_r(h, seq), nullptr, 0); GEOSGeometry* res = GEOSIntersection_r(h, geom, b_poly); GEOSGeom_destroy_r(h, geom); GEOSGeom_destroy_r(h, b_poly); return res; }; pdnp = clip_func(pdnp); pud = clip_func(pud); auto to_lines = [&](GEOSGeometry* geom, std::vector>& out) { int n = GEOSGetNumGeometries_r(h, geom); for (int i = 0; i < n; ++i) { GEOSGeometry* g_simple = GEOSSimplify_r(h, GEOSGetGeometryN_r(h, geom, i), 0.01); out.push_back(GEOSPolygonToSingleLine(g_simple)); GEOSGeom_destroy_r(h, g_simple); } }; to_lines(pdnp, pdnpLines); to_lines(pud, pudLines); GEOSGeom_destroy_r(h, pdnp); GEOSGeom_destroy_r(h, pud); }