"""지형 분석·지표면 모델 생성 파라미터 (5장·5-2장). `config_system.py` 가 700줄을 넘어 떼어냈다(2026-09-04). 값·이름은 그대로이고, `config_system` 이 다시 내보내므로 기존 `from config.config_system import …` 는 불변이다. """ import os # 5. 지형 분석 알고리즘 파라미터 # ───────────────────────────────────────────────────────────────────────── # Trimesh 메쉬 생성 MESH_GRID_SIZE = float(os.getenv("MESH_GRID_SIZE", "1.0")) # 미터 단위 MESH_SMOOTHING_ITERATIONS = int(os.getenv("MESH_SMOOTHING_ITERATIONS", "0")) SURFACE_LAS_CHUNK_SIZE = int(os.getenv("SURFACE_LAS_CHUNK_SIZE", "500000")) SURFACE_DEFAULT_RGB_VALUE = int(os.getenv("SURFACE_DEFAULT_RGB_VALUE", "128")) SURFACE_GRID_CELL_SIZE_M = float(os.getenv("SURFACE_GRID_CELL_SIZE_M", "2.0")) SURFACE_GRID_HEIGHT_THRESHOLD_M = float(os.getenv("SURFACE_GRID_HEIGHT_THRESHOLD_M", "1.5")) # CSF (Cloth Simulation Filter) 지면 분류 파라미터 SURFACE_CSF_CLOTH_RESOLUTION_M = float(os.getenv("SURFACE_CSF_CLOTH_RESOLUTION_M", "1.5")) SURFACE_CSF_RIGIDNESS = int(os.getenv("SURFACE_CSF_RIGIDNESS", "1")) SURFACE_CSF_TIME_STEP = float(os.getenv("SURFACE_CSF_TIME_STEP", "0.65")) SURFACE_CSF_CLASS_THRESHOLD_M = float(os.getenv("SURFACE_CSF_CLASS_THRESHOLD_M", "0.5")) # 낙하 후 스프링 정착에 쓰는 여유 반복 수 (실제 낙하 구간은 현장 기복에서 계산한다) SURFACE_CSF_ITERATIONS = int(os.getenv("SURFACE_CSF_ITERATIONS", "150")) # 낙하+정착 반복 상한 — 기복 1500m(0.3185m/회)까지 커버한다 SURFACE_CSF_MAX_ITERATIONS = int(os.getenv("SURFACE_CSF_MAX_ITERATIONS", "5000")) SURFACE_CSF_SLOPE_SMOOTH = os.getenv("SURFACE_CSF_SLOPE_SMOOTH", "True").lower() == "true" SURFACE_CSF_SLOPE_SMOOTH_THRESHOLD_M = float( os.getenv("SURFACE_CSF_SLOPE_SMOOTH_THRESHOLD_M", "1.8") ) # PMF (Progressive Morphological Filter) 지면 분류 파라미터 SURFACE_PMF_CELL_SIZE_M = float(os.getenv("SURFACE_PMF_CELL_SIZE_M", "2.0")) SURFACE_PMF_MAX_WINDOW_SIZE = int(os.getenv("SURFACE_PMF_MAX_WINDOW_SIZE", "40")) SURFACE_PMF_INITIAL_WINDOW_SIZE = int(os.getenv("SURFACE_PMF_INITIAL_WINDOW_SIZE", "3")) SURFACE_PMF_SLOPE = float(os.getenv("SURFACE_PMF_SLOPE", "1.0")) SURFACE_PMF_MAX_DISTANCE_M = float(os.getenv("SURFACE_PMF_MAX_DISTANCE_M", "2.5")) # RANSAC (Local plane fitting) 지면 분류 파라미터 SURFACE_RANSAC_DISTANCE_THRESHOLD_M = float(os.getenv("SURFACE_RANSAC_DISTANCE_THRESHOLD_M", "0.3")) SURFACE_RANSAC_N = int(os.getenv("SURFACE_RANSAC_N", "3")) SURFACE_RANSAC_ITERATIONS = int(os.getenv("SURFACE_RANSAC_ITERATIONS", "100")) SURFACE_RANSAC_LOCAL_GRID_SIZE_M = float(os.getenv("SURFACE_RANSAC_LOCAL_GRID_SIZE_M", "10.0")) SURFACE_RANSAC_SEED = int(os.getenv("SURFACE_RANSAC_SEED", "42")) # LAS 자체 지면분류(class 2) 필터 — 이 비율 이상 분류돼 있을 때만 필터로 제공한다. # 미분류 LAS는 classification이 전부 0이라 마스크가 비어 버린다. SURFACE_CLASSIFIED_GROUND_MIN_RATIO = float( os.getenv("SURFACE_CLASSIFIED_GROUND_MIN_RATIO", "0.002") ) # 지면점 비율이 이 값 미만이면 필터가 사실상 실패한 것으로 보고 WARNING을 남긴다. SURFACE_GROUND_RATIO_WARN = float(os.getenv("SURFACE_GROUND_RATIO_WARN", "0.01")) # 계획노선이 지표면 밖으로 나가 잘릴 때, 잘린 쪽 끝에서 더 깎을 길이(m). # 서피스 가장자리는 점 밀도가 떨어져 외곽선이 불규칙하다 — 경계에 딱 붙여 자르면 # 그 구간 지반고가 못 미덥다 (2026-09-01 사용자 확정). # 30m 는 너무 많이 깎는다는 지적으로 3m 로 낮춤 (2026-09-04 사용자 지시). SURFACE_ROUTE_EDGE_TRIM_M = float(os.getenv("SURFACE_ROUTE_EDGE_TRIM_M", "3.0")) # ───────────────────────────────────────────────────────────────────────── # 5-2. 지표면 모델 생성 파라미터 (TIN/DTM/NURBS/implicit/meshfree) # ───────────────────────────────────────────────────────────────────────── # 고를 수 있는 지면 필터 전체 — 관리자가 B04 드롭다운에서 요청할 때 쓰인다. # 자동 전처리가 미리 만드는 조합이 아니다(그건 SURFACE_AUTO_* 가 정한다). SURFACE_MODEL_SOURCE_FILTERS = tuple( os.getenv("SURFACE_MODEL_SOURCE_FILTERS", "classification,grid_min_z,csf,pmf").split(",") ) # 자동 전처리가 만드는 조합 — 기본 필터 1종 × 아래 표현. 나머지는 관리자 요청 시 만든다. SURFACE_AUTO_METHODS = tuple(os.getenv("SURFACE_AUTO_METHODS", "dtm").split(",")) # 기본 필터는 입력 LAS를 보고 정한다 — 지면분류(class 2)가 있으면 그것을, 없으면 아래 값. SURFACE_AUTO_FALLBACK_FILTER = os.getenv("SURFACE_AUTO_FALLBACK_FILTER", "csf") # dtm을 먼저 빌드해야 TIN 등고선 사전 캐시가 dtm footprint를 참조할 수 있다 (PLAN D-6) SURFACE_MODEL_PRECOMPUTE = tuple( os.getenv("SURFACE_MODEL_PRECOMPUTE", "dtm,tin,nurbs,implicit,meshfree").split(",") ) SURFACE_MODEL_SMOOTHING_METHODS = tuple( os.getenv("SURFACE_MODEL_SMOOTHING_METHODS", "dtm,tin").split(",") ) SURFACE_MODEL_SYNC_TIMEOUT_SECONDS = int(os.getenv("SURFACE_MODEL_SYNC_TIMEOUT_SECONDS", "0")) # footprint(외곽) 산출 SURFACE_FOOTPRINT_RESOLUTION_M = float(os.getenv("SURFACE_FOOTPRINT_RESOLUTION_M", "1.0")) SURFACE_FOOTPRINT_GAP_CLOSE_M = float(os.getenv("SURFACE_FOOTPRINT_GAP_CLOSE_M", "1.0")) SURFACE_BOUNDARY_INSET_M = float(os.getenv("SURFACE_BOUNDARY_INSET_M", "1.0")) SURFACE_KEEP_LARGEST_FOOTPRINT = ( os.getenv("SURFACE_KEEP_LARGEST_FOOTPRINT", "True").lower() == "true" ) SURFACE_TILE_SIZE_M = float(os.getenv("SURFACE_TILE_SIZE_M", "50.0")) SURFACE_MAX_PREVIEW_VERTICES = int(os.getenv("SURFACE_MAX_PREVIEW_VERTICES", "500000")) # 표현별 파라미터 (old 버전 검증값 기준 — PLAN F) SURFACE_TIN_MAX_INPUT_POINTS = int(os.getenv("SURFACE_TIN_MAX_INPUT_POINTS", "500000")) SURFACE_DTM_GRID_RESOLUTION_M = float(os.getenv("SURFACE_DTM_GRID_RESOLUTION_M", "1.0")) SURFACE_NURBS_DEGREE = int(os.getenv("SURFACE_NURBS_DEGREE", "3")) SURFACE_NURBS_PATCH_SIZE_M = float(os.getenv("SURFACE_NURBS_PATCH_SIZE_M", "50.0")) SURFACE_NURBS_CONTROL_POINTS_PER_AXIS = int( os.getenv("SURFACE_NURBS_CONTROL_POINTS_PER_AXIS", "16") ) SURFACE_IMPLICIT_MAX_POINTS_PER_TILE = int( os.getenv("SURFACE_IMPLICIT_MAX_POINTS_PER_TILE", "10000") ) SURFACE_IMPLICIT_SMOOTHING = float(os.getenv("SURFACE_IMPLICIT_SMOOTHING", "0.1")) SURFACE_MESHFREE_MAX_MODEL_POINTS = int(os.getenv("SURFACE_MESHFREE_MAX_MODEL_POINTS", "500000")) SURFACE_MESHFREE_POINT_RADIUS_M = float(os.getenv("SURFACE_MESHFREE_POINT_RADIUS_M", "0.15")) # 스무딩 파라미터 SURFACE_SMOOTHING_DTM_SIGMA_M = float(os.getenv("SURFACE_SMOOTHING_DTM_SIGMA_M", "0.5")) SURFACE_SMOOTHING_DTM_SPLINE_SMOOTH = float(os.getenv("SURFACE_SMOOTHING_DTM_SPLINE_SMOOTH", "0.0")) SURFACE_SMOOTHING_DTM_PREVIEW_RESOLUTION_M = float( os.getenv("SURFACE_SMOOTHING_DTM_PREVIEW_RESOLUTION_M", "0.5") ) SURFACE_SMOOTHING_TIN_TAUBIN_ITERATIONS = int( os.getenv("SURFACE_SMOOTHING_TIN_TAUBIN_ITERATIONS", "10") ) SURFACE_SMOOTHING_TIN_TAUBIN_LAMBDA = float(os.getenv("SURFACE_SMOOTHING_TIN_TAUBIN_LAMBDA", "0.5")) SURFACE_SMOOTHING_TIN_TAUBIN_MU = float(os.getenv("SURFACE_SMOOTHING_TIN_TAUBIN_MU", "-0.53")) # 등고선 파라미터 SURFACE_CONTOUR_INTERVAL_M = float(os.getenv("SURFACE_CONTOUR_INTERVAL_M", "1.0")) SURFACE_CONTOUR_GRID_RESOLUTION_M = float(os.getenv("SURFACE_CONTOUR_GRID_RESOLUTION_M", "1.0")) # 도엽등고선 3D 서피스 (LAS 없는 설계 — 2026-08-30 사용자 확정) # 노선 XY bbox에 더하는 절취 여유(m). 300m = 횡단·코리도·성토면 여유(사용자 확정값). SHEET_SURFACE_MARGIN_M = float(os.getenv("SHEET_SURFACE_MARGIN_M", "300.0")) # 도엽등고선 DTM 격자 한 변(m). LAS DTM·등고선 캐시와 같은 1m(사용자 확정값). SHEET_SURFACE_GRID_M = float(os.getenv("SHEET_SURFACE_GRID_M", "1.0")) # 만들어 둘 등고선 보간 방식 — B04 화면에서 버튼으로 바꿔 가며 비교한다 # (2026-08-30 사용자 지시). 정의는 B04_PreProcess_Engine_SheetMethods.py. SHEET_SURFACE_METHODS = [ method.strip() for method in os.getenv( "SHEET_SURFACE_METHODS", "tin_sheet,tin,biharmonic,anudem,multires,laplace", ).split(",") if method.strip() ] # 확정에 쓸 기본 방식 — 라플라스 (2026-08-30 사용자 확정). SHEET_SURFACE_DEFAULT_METHOD = os.getenv("SHEET_SURFACE_DEFAULT_METHOD", "laplace") # 자동 전처리가 만드는 도엽 방식 — 기본 하나뿐이다. 여섯 방식을 매번 다 만들면 # WF1의 대부분(용화 실측 891초 중 655초)을 여기서 쓴다. 나머지는 관리자가 B04에서 # 그 방식을 고를 때 만든다 (2026-09-01 사용자 확정). SHEET_SURFACE_AUTO_METHODS = [ method.strip() for method in os.getenv("SHEET_SURFACE_AUTO_METHODS", SHEET_SURFACE_DEFAULT_METHOD).split(",") if method.strip() ] # 일반 사용자 WF1 자동 확정 기본값 SURFACE_CONFIRM_DEFAULT_FILTER = os.getenv("SURFACE_CONFIRM_DEFAULT_FILTER", "csf") SURFACE_CONFIRM_DEFAULT_METHOD = os.getenv("SURFACE_CONFIRM_DEFAULT_METHOD", "dtm") SURFACE_CONFIRM_DEFAULT_SMOOTH = ( os.getenv("SURFACE_CONFIRM_DEFAULT_SMOOTH", "True").lower() == "true" ) def build_surface_model_config() -> dict: """지표면 모델 파이프라인이 사용하는 config dict를 조립한다.""" return { "source_filters": list(SURFACE_MODEL_SOURCE_FILTERS), "precompute": list(SURFACE_MODEL_PRECOMPUTE), "smoothing_methods": tuple(SURFACE_MODEL_SMOOTHING_METHODS), "sync_timeout_seconds": SURFACE_MODEL_SYNC_TIMEOUT_SECONDS, "footprint_resolution_meters": SURFACE_FOOTPRINT_RESOLUTION_M, "footprint_gap_close_meters": SURFACE_FOOTPRINT_GAP_CLOSE_M, "boundary_inset_meters": SURFACE_BOUNDARY_INSET_M, "keep_largest_footprint": SURFACE_KEEP_LARGEST_FOOTPRINT, "tile_size_meters": SURFACE_TILE_SIZE_M, "max_preview_vertices": SURFACE_MAX_PREVIEW_VERTICES, "tin_max_input_points": SURFACE_TIN_MAX_INPUT_POINTS, "dtm_grid_resolution_meters": SURFACE_DTM_GRID_RESOLUTION_M, "nurbs_degree": SURFACE_NURBS_DEGREE, "nurbs_patch_size_meters": SURFACE_NURBS_PATCH_SIZE_M, "nurbs_control_points_per_axis": SURFACE_NURBS_CONTROL_POINTS_PER_AXIS, "implicit_max_points_per_tile": SURFACE_IMPLICIT_MAX_POINTS_PER_TILE, "implicit_smoothing": SURFACE_IMPLICIT_SMOOTHING, "meshfree_max_model_points": SURFACE_MESHFREE_MAX_MODEL_POINTS, "meshfree_point_radius_meters": SURFACE_MESHFREE_POINT_RADIUS_M, "smoothing_dtm_sigma_meters": SURFACE_SMOOTHING_DTM_SIGMA_M, "smoothing_dtm_spline_smooth": SURFACE_SMOOTHING_DTM_SPLINE_SMOOTH, "smoothing_dtm_preview_resolution_meters": SURFACE_SMOOTHING_DTM_PREVIEW_RESOLUTION_M, "smoothing_tin_taubin_iterations": SURFACE_SMOOTHING_TIN_TAUBIN_ITERATIONS, "smoothing_tin_taubin_lambda": SURFACE_SMOOTHING_TIN_TAUBIN_LAMBDA, "smoothing_tin_taubin_mu": SURFACE_SMOOTHING_TIN_TAUBIN_MU, "contour_interval_meters": SURFACE_CONTOUR_INTERVAL_M, "contour_grid_resolution_meters": SURFACE_CONTOUR_GRID_RESOLUTION_M, }