- 새 D03_PlanAnalysis_Network.py — 서버가 marks 로 조각 경계 · 길이 · 측점 다시 셈(높이 저장 안 함) · 옛 연도 노선은 읽을 때 한 선으로 · 업체 지정 조각 지움 409 · 산림청만 - 새 db_management/021_plan_network.sql — plan_networks · plan_route_pieces · plan_route_assignees.piece_id(DB 반영함) - main.py 라우터 한 벌 · Router.py 의 POST analyze · GET/PUT analysis 덩이 지움(지형 · 진행 길 그대로) - 지움: D03_PlanAnalysis_Analyze.py · D03_PlanAnalysis_Edit.py · config PLAN_ANALYZE_WORKERS - 시험: test_530_2_plan_analyze = 노선 정본 시험으로 다시 씀(옛 엔진 시험은 엔진과 함께 걷음) · test_530_2_plan_router = 분석 API 시험 지우고 끈 지점 규칙만 · test_530_3_plan_logic = Analyze 짝 시험 지움 · test_543_plan_edit 지움(꺾임점 R 고침 걷음 — 요구 389 11 직선만) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
4.0 KiB
Python
124 lines
4.0 KiB
Python
"""D03 화면 계산(PLAN 530-3) — TS `logic/D03_PlanAnalysis_Plan.ts`.
|
|
|
|
측점 누가 앞붙임 선(선 위 누가 = 등록 시작 누가) · 받은 지형 판정.
|
|
파이썬 짝(`D03_PlanAnalysis_Analyze`)은
|
|
옛 추천 엔진과 함께 걷음(PLAN 562-2).
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_530_3_plan_logic.py -q
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
TS_FILE = ROOT / "D03_PlanAnalysis" / "logic" / "D03_PlanAnalysis_Plan.ts"
|
|
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
|
|
|
_RUNNER = """
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import * as plan from "./D03_PlanAnalysis/logic/D03_PlanAnalysis_Plan.js";
|
|
const job = JSON.parse(readFileSync(process.argv[2], "utf8"));
|
|
const out = job.cases.map((c) => {
|
|
const rows = plan.chainRows(job.results, c.choices, c.start);
|
|
return {
|
|
picks: job.results.map((r) => plan.pickMode(r, c.choices[r.year])),
|
|
rows,
|
|
line: plan.joinedLine(job.results, rows),
|
|
stations: plan.stationChainages(rows, 20),
|
|
};
|
|
});
|
|
const lead = plan.leadInLine([[0, 0], [3, 4], [10, 4]], 1234.5);
|
|
const reach = plan.nodesInReach(
|
|
[[500, 500], [900, 500], [1700, 500]],
|
|
[{ x0: 0, y0: 0, x1: 1300, y1: 1000 }, null],
|
|
);
|
|
writeFileSync(process.argv[3], JSON.stringify({ out, lead, reach }));
|
|
"""
|
|
|
|
_P0, _P1, _P2 = [0.0, 0.0], [300.0, 400.0], [900.0, 400.0]
|
|
_RESULTS = [
|
|
{
|
|
"year": 2026,
|
|
"start": _P0,
|
|
"end": _P1,
|
|
"routes": {
|
|
"gradeline": {"planned": [_P0, [100.0, 200.0], _P1], "metrics": {"length_m": 530.0}},
|
|
"earthwork": {"error": "길 없음"},
|
|
"gentle": {"planned": [_P0, _P1], "metrics": {"length_m": 500.004}},
|
|
},
|
|
},
|
|
{
|
|
"year": 2027,
|
|
"start": _P1,
|
|
"end": _P2,
|
|
"routes": {
|
|
"gradeline": {"error": "길 없음"},
|
|
"gentle": {"planned": [_P1, [600.0, 500.0], _P2], "metrics": {"length_m": 640.333}},
|
|
"weighted": {"planned": [_P1, _P2], "metrics": {"length_m": 600.0}},
|
|
},
|
|
},
|
|
]
|
|
_CASES = [
|
|
{"choices": {}, "start": 1000.0},
|
|
{"choices": {"2026": "gentle", "2027": "weighted"}, "start": 17.25},
|
|
{"choices": {"2026": "earthwork", "2027": "follow"}, "start": 0.0},
|
|
]
|
|
|
|
|
|
def _run_ts(tmp_path: Path) -> dict:
|
|
out = tmp_path / "js"
|
|
subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
[
|
|
"node",
|
|
str(TSC),
|
|
str(TS_FILE),
|
|
"--outDir",
|
|
str(out),
|
|
"--rootDir",
|
|
str(ROOT),
|
|
"--module",
|
|
"esnext",
|
|
"--target",
|
|
"es2022",
|
|
"--moduleResolution",
|
|
"bundler",
|
|
"--ignoreConfig",
|
|
],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
for emitted in out.rglob("*.js"):
|
|
emitted.write_text(
|
|
re.sub(r'(from\s+"\.\.?/[^"]+)"', r'\1.js"', emitted.read_text(encoding="utf-8")),
|
|
encoding="utf-8",
|
|
)
|
|
(out / "runner.mjs").write_text(_RUNNER, encoding="utf-8")
|
|
job = tmp_path / "job.json"
|
|
result = tmp_path / "out.json"
|
|
job.write_text(json.dumps({"results": _RESULTS, "cases": _CASES}), encoding="utf-8")
|
|
subprocess.run( # noqa: S603
|
|
["node", str(out / "runner.mjs"), str(job), str(result)],
|
|
cwd=str(ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
)
|
|
return json.loads(result.read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_lead_in_and_reach(tmp_path: Path) -> None:
|
|
got = _run_ts(tmp_path)
|
|
back = got["lead"][0]
|
|
assert abs(back[0] + 1234.5 * 0.6) < 1e-9 and abs(back[1] + 1234.5 * 0.8) < 1e-9
|
|
assert got["lead"][1:] == [[0, 0], [3, 4], [10, 4]]
|
|
# 마디 0 = 첫 연도 안 · 마디 1 = 첫 연도 안 + 둘째 연도 DEM 없음 = 밖 · 마디 2 = 밖
|
|
assert got["reach"] == [True, False, False]
|