fix(B05): 관 · 구조물 자체 측점이 종단 측점에서 정수 m 격자로 붙던 것 — 파일 이름에만 정수 m
PLAN 19장 남은 차이(sub8 왕복 시험): 관 440.24 · 619.71 · 720.44 · 899.91 이 저장 뒤 종단 측점에서
440 · 620 · 720 · 900 으로 붙음(generate_sections 의 정수 m 격자 붙이기). 판정 (다) 유지 — 횡단 파일
이름(cross_{정수m}m)은 그대로 한 정수 m 에 한 장, 측점 값은 관 · 구조물 자체 측점 그대로:
- 같은 정수 m 에 격자 측점이 있으면 격자 대신 관 · 구조물 측점을 씀(kind irregular · 라벨 그대로)
- 시 · 종점 격자는 그대로 두고 라벨만 얹음(노선 끝 측점이 사라지지 않게)
- 비정규끼리 같은 정수 m 면 앞선 것만(종전과 같음 · 파일 한 장)
시험 — test_b05_b06_save_roundtrip[B05] 통과(관 측점 차이 0 · 사본 storage · 읽기 전용 DB) ·
test_b05_extra_stations_supplier(옛 「격자 우선」 기대를 새 규칙으로 · 시 · 종점 하나 더함) ·
test_b05_station_roundtrip · test_regeneration_extra_stations 통과. [B06] 경우의 「횡단 patch 263.47 —
받을 행이 없어 DB 가 버림」은 고치기 전 코드에서도 같게 실패(이번 변경과 무관 · 보고함).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVM3xZ1aUPcUTuW2WZSnwg
This commit is contained in:
@@ -341,17 +341,34 @@ def generate_sections(
|
||||
if 0.0 <= float(chainage) <= total
|
||||
}
|
||||
if extras:
|
||||
# 횡단 파일명이 정수 미터 반올림이라(cross_filename) 1m 안의 두 측점은 같은 파일을
|
||||
# 덮어써 측점 수와 횡단 파일 수가 어긋난다. 같은 정수 미터면 격자 측점으로 스냅해
|
||||
# 라벨만 얹고(아래 station["structure"] 부착), 비정규끼리 겹치면 앞선 것만 남긴다.
|
||||
# ponytail: 파일명이 정수 미터인 동안의 가드. 소수 1자리 규칙으로 바꾸면 지운다.
|
||||
buckets = {int(round(float(value))): round(float(value), 6) for value in station_chainage}
|
||||
# 횡단 파일명이 정수 미터 반올림이라(cross_filename) 한 정수 미터에 측점은 하나만 둔다.
|
||||
# 관 · 구조물 자체 측점은 소수 그대로 — 격자 측점과 같은 정수 미터면 격자 대신 그 측점을
|
||||
# 쓴다(시 · 종점 격자만 그대로 두고 라벨을 얹음). 비정규끼리 겹치면 앞선 것만 남긴다.
|
||||
# ponytail: 파일명이 정수 미터인 동안의 가드. 소수 이름 규칙으로 바꾸면 지운다.
|
||||
grid = [round(float(value), 6) for value in station_chainage]
|
||||
ends = {grid[0], grid[-1]}
|
||||
buckets = {int(round(value)): value for value in grid}
|
||||
taken: set[int] = set()
|
||||
dropped: set[float] = set()
|
||||
snapped: dict[float, str] = {}
|
||||
for chainage in sorted(extras):
|
||||
snapped.setdefault(buckets.setdefault(int(round(chainage)), chainage), extras[chainage])
|
||||
bucket = int(round(chainage))
|
||||
if bucket in taken:
|
||||
continue
|
||||
taken.add(bucket)
|
||||
regular = buckets.get(bucket)
|
||||
if regular in ends:
|
||||
snapped[regular] = extras[chainage]
|
||||
continue
|
||||
if regular is not None:
|
||||
dropped.add(regular)
|
||||
snapped[chainage] = extras[chainage]
|
||||
extras = snapped
|
||||
station_chainage = np.unique(
|
||||
np.r_[station_chainage, np.array(sorted(extras), dtype=np.float64)]
|
||||
np.r_[
|
||||
np.array([value for value in grid if value not in dropped], dtype=np.float64),
|
||||
np.array(sorted(extras), dtype=np.float64),
|
||||
]
|
||||
)
|
||||
station_xy = _interpolate_xy(points, route_chainage, station_chainage)
|
||||
tangents = np.vstack(
|
||||
|
||||
@@ -192,7 +192,7 @@ def test_matching_signature_loads_points(tmp_path):
|
||||
]
|
||||
|
||||
|
||||
# ── 횡단 파일명 충돌 스냅(격자 우선 불변식) ────────────────────────────────
|
||||
# ── 횡단 파일명 충돌 — 한 정수 미터에 측점 하나 · 관 · 구조물 자체 측점 우선(PLAN 19장) ──
|
||||
|
||||
|
||||
def _stations(extra_stations):
|
||||
@@ -204,17 +204,31 @@ def _stations(extra_stations):
|
||||
return result["longitudinal"]["stations"]
|
||||
|
||||
|
||||
def test_extra_station_within_one_meter_snaps_to_grid():
|
||||
def test_extra_station_in_grid_meter_replaces_grid_station():
|
||||
stations = _stations(((100.4, "X"),))
|
||||
assert 100.4 not in [station["chainage_m"] for station in stations]
|
||||
chainages = [station["chainage_m"] for station in stations]
|
||||
# 관 · 구조물 자체 측점 그대로 — 같은 정수 미터 격자 100 은 빠져 파일(cross_00100m)은 한 장
|
||||
assert 100.4 in chainages and 100.0 not in chainages
|
||||
labelled = [station for station in stations if station.get("structure") == "X"]
|
||||
assert [station["chainage_m"] for station in labelled] == [100.0]
|
||||
assert labelled[0]["kind"] == "regular" # 격자 측점 우선 — 기존 불변식
|
||||
assert [(station["chainage_m"], station["kind"]) for station in labelled] == [
|
||||
(100.4, "irregular")
|
||||
]
|
||||
assert len({cross_filename(c) for c in chainages}) == len(chainages)
|
||||
|
||||
|
||||
def test_extra_station_at_route_end_keeps_end_station():
|
||||
stations = _stations(((0.3, "S"), (349.8, "E")))
|
||||
chainages = [station["chainage_m"] for station in stations]
|
||||
# 시 · 종점은 격자 그대로 두고 라벨만 얹음
|
||||
assert chainages[0] == 0.0 and chainages[-1] == 350.0
|
||||
assert 0.3 not in chainages and 349.8 not in chainages
|
||||
assert stations[0].get("structure") == "S" and stations[-1].get("structure") == "E"
|
||||
|
||||
|
||||
def test_two_extras_in_same_meter_bucket_keep_one():
|
||||
stations = _stations(((80.2, "A"), (80.4, "B")))
|
||||
assert [station["chainage_m"] for station in stations if station.get("structure")] == [80.0]
|
||||
assert [station["chainage_m"] for station in stations if station.get("structure")] == [80.2]
|
||||
assert 80.0 not in [station["chainage_m"] for station in stations]
|
||||
|
||||
|
||||
def test_live_extras_stay_irregular_and_filenames_stay_unique():
|
||||
|
||||
@@ -52,9 +52,9 @@ def test_계획고_편집은_가까운_새_측점으로_옮김(results):
|
||||
assert "200.000" not in results["deleted"]["station_offsets"]
|
||||
|
||||
|
||||
# ── 서버 — 사용자가 놓은 구조물 측점은 소수 그대로(격자 붙이기는 횡단 측점에만) ─────────
|
||||
# 브레인 판정(다): 횡단 측점 · 파일 이름의 정수 m 격자 붙이기는 그대로 두고, 구조물 정본 측점만
|
||||
# 사용자가 놓은 소수 그대로 저장 · 다시 읽기. 임시 storage 에서만 씀(공유 storage 안 건드림).
|
||||
# ── 서버 — 관 · 구조물 자체 측점은 소수 그대로(정수 m 는 횡단 파일 이름에만) ─────────
|
||||
# 브레인 판정(다) · 19장 남은 차이: 파일 이름은 한 정수 m 에 한 장 그대로, 측점 값은 사용자가 놓은
|
||||
# 소수 그대로 저장 · 다시 읽기 · 종단 측점. 임시 storage 에서만 씀(공유 storage 안 건드림).
|
||||
|
||||
|
||||
def _flat_sampler():
|
||||
@@ -96,8 +96,8 @@ def test_사용자_구조물_119_8_저장_다시_읽기_그대로(tmp_path):
|
||||
SectionGenerationOptions(extra_stations=extras),
|
||||
)
|
||||
chainages = [round(s["chainage_m"], 3) for s in sections["longitudinal"]["stations"]]
|
||||
# 횡단 측점은 격자 120 으로 붙음(파일 이름 cross_00120m 한 장) — 정본 측점은 119.8 그대로
|
||||
assert 120.0 in chainages and 119.8 not in chainages
|
||||
# 횡단 측점도 자체 측점 119.8(같은 정수 미터 격자 120 대신 · 파일 이름 cross_00120m 한 장)
|
||||
assert 119.8 in chainages and 120.0 not in chainages
|
||||
assert cross_filename(119.8) == cross_filename(120.0)
|
||||
_, again = load_structures(str(tmp_path))
|
||||
assert [s.chainage_m for s in again] == [119.8] and again[0].memo == "메모만"
|
||||
|
||||
Reference in New Issue
Block a user