diff --git a/A00_Common/design/design_calc_sites.ts b/A00_Common/design/design_calc_sites.ts index b1cd2c4dc..c678ad044 100644 --- a/A00_Common/design/design_calc_sites.ts +++ b/A00_Common/design/design_calc_sites.ts @@ -6,6 +6,7 @@ * ground_sample(G1) server = ground 단계가 격자 대신 서버 표본(`B05_Profile_Api_TerrainGrid.ts`) * corridor_mesh(D3) server = 3D 미리보기 워커 안 띄움 · 서버 모형만(`B05_Profile_UI_Corridor_Worker_Client.ts`) * quantity · carry · fixed_browser server = 조작 중 서버 창구(511-A · `design_calc_server.ts`) + * haul(E2) 기본 server = /haul-plan · both = 브라우저가 배분 모듈을 그때 불러 셈(511-C · `B05_Profile_Api_HaulPlan.ts`) * ========================================================================== */ import { API_BASE_URL } from "@config/config_frontend"; @@ -18,6 +19,8 @@ export interface CalcSiteGroup { switchable: boolean; fixed: "both" | "server" | "browser" | null; why: string | null; + /** 줄마다 까닭 열쇠(번호 → 열쇠) — 없으면 `why`. */ + whys?: Record; } export interface CalcSitesBoard { @@ -63,6 +66,11 @@ export async function calcOnServer(key: string): Promise { return (await (opened ?? openCalcSites()))[key] === "server"; } +/** 판에 both 일 때만 브라우저 셈 — 못 받음 · 받기 전 = 아님(기본 server 인 haul 줄). */ +export async function calcInBrowser(key: string): Promise { + return (await (opened ?? openCalcSites()))[key] === "both"; +} + /** 동기 자리용 — 받기 전 = both. */ export const calcOnServerNow = (key: string): boolean => loaded[key] === "server"; diff --git a/A00_Common/design/logic/design_calc_node.ts b/A00_Common/design/logic/design_calc_node.ts index 7e05c8292..0cd58e9be 100644 --- a/A00_Common/design/logic/design_calc_node.ts +++ b/A00_Common/design/logic/design_calc_node.ts @@ -57,7 +57,7 @@ import { profileRunners, type ProfileInputs } from "./design_calc_node_profile"; import { routeRunners, type RouteInputs } from "./design_calc_node_route"; import { sectionsRunners, type SectionsInputs } from "./design_calc_node_sections"; import { STAGE_IDS, stageRunners, type StagesInputs } from "./design_calc_node_stages"; -import { TRIAL_IDS, trialRunners, type TrialInputs } from "./design_calc_node_trial"; +import { plain, TRIAL_IDS, trialRunners, type TrialInputs } from "./design_calc_node_trial"; type HaulOptions = NonNullable[2]>; @@ -141,6 +141,14 @@ async function runDesignCalc(run: readonly string[], inputs: CalcInputs) { : null; return massHaul ? massHaulPayload(massHaul) : null; }, + // 유토 배분 화면 꼴(/haul-plan 창구 · 511-C) — 인자 그대로(args) · 등록부 밖. 시험 길 표에 두면 + // 브라우저가 끄는 파일(`plain`)이라 배분 모듈이 첫 묶음에 실림 — 그래서 이 입구에만. + "haul.view": () => + plain( + computeHaulPlan( + ...((inputs.args?.["haul.view"] ?? []) as Parameters), + ), + ), // 두 벌 — haul_plan = 거르지 않은 수량 정본(B08) · haul_plan_drawing = 잔진동 거른 그림. "haul.plan": () => { need("masshaul.cumulative"); @@ -218,7 +226,7 @@ async function runDesignCalc(run: readonly string[], inputs: CalcInputs) { const unknown = run.filter((id) => !(id in RUNNERS)); const results: Record = {}; if (!unknown.length) for (const id of run) results[id] = await need(id); - const outside = [...TRIAL_IDS, ...STAGE_IDS]; + const outside = [...TRIAL_IDS, ...STAGE_IDS, "haul.view"]; const available = Object.keys(RUNNERS).filter((id) => !outside.includes(id)); return { results, unknown, available }; } diff --git a/A00_Common/design/logic/design_calc_registry_facility.ts b/A00_Common/design/logic/design_calc_registry_facility.ts index f2deae4d2..70649a4ac 100644 --- a/A00_Common/design/logic/design_calc_registry_facility.ts +++ b/A00_Common/design/logic/design_calc_registry_facility.ts @@ -185,8 +185,8 @@ export const FACILITY_ROWS: readonly CalcEntry[] = [ ts: `${U}common_util_mass_haul_balance.ts#computeHaulPlan`, runner: "node:haul.plan", pair: null, - mirror: [], - note: "브라우저 번들 밖(/haul-plan 요청) · 브라우저 실행 여부 협의", + mirror: ["test_511_c_haul_node"], + note: "기본 /haul-plan(늘 떠 있는 Node haul.view) · 판 haul both = 브라우저 dynamic import(511-C)", }, /* ── 배수 · 3D · 추천 ────────────────────────────────────────────────── */ { diff --git a/B01_Dashboard/B01_Dashboard_UI_CalcSites.ts b/B01_Dashboard/B01_Dashboard_UI_CalcSites.ts index 677a9a878..e86e11a01 100644 --- a/B01_Dashboard/B01_Dashboard_UI_CalcSites.ts +++ b/B01_Dashboard/B01_Dashboard_UI_CalcSites.ts @@ -40,7 +40,7 @@ function groupRows( changed: () => void, ): HTMLElement[][] { const server = (): HTMLInputElement => check(group.fixed !== "browser", true); - const why = K(`Why_${group.why ?? group.key}`); + const why = (no: string): string => K(`Why_${group.whys?.[no] ?? group.why ?? group.key}`); const head = el("span", { children: [el("strong", { text: K(group.key) }), ` (${group.nos.length})`], }); @@ -48,7 +48,7 @@ function groupRows( const browser = (): HTMLInputElement => check(group.fixed !== "server", true); return [ [head, browser(), server(), el("span")], - ...group.nos.map((no) => [calcName(no), browser(), server(), el("span", { text: why })]), + ...group.nos.map((no) => [calcName(no), browser(), server(), el("span", { text: why(no) })]), ]; } const boxes = [group.key, ...group.nos].map((id, index) => { @@ -70,7 +70,7 @@ function groupRows( calcName(no), boxes[index + 1], server(), - el("span", { text: why }), + el("span", { text: why(no) }), ]), ]; } diff --git a/B05_Profile/B05_Profile_Api_HaulPlan.ts b/B05_Profile/B05_Profile_Api_HaulPlan.ts index 78fe2810d..cfcab7693 100644 --- a/B05_Profile/B05_Profile_Api_HaulPlan.ts +++ b/B05_Profile/B05_Profile_Api_HaulPlan.ts @@ -2,9 +2,9 @@ * B05_Profile_Api_HaulPlan.ts * 유토 **배분**(평형선·운반거리·장비)을 서버에서 미리 받아 두는 자리. * - * 왜 서버인가(2026-09-06 사용자 확정) — 배분 산식은 노하우가 몰린 자리라 브라우저 번들에 - * 남기지 않는다. 화면은 누가토량까지만 스스로 내고(`common_util_mass_haul`), 그 결과를 - * 여기로 보내 배분을 받아 쥔다. 계산은 여전히 한 벌이다 — 서버가 같은 TS 를 Node 로 돈다. + * 기본은 서버(계산 자리 판 haul = server) — 화면은 누가토량까지만 스스로 내고(`common_util_mass_haul`), + * 그 결과를 여기로 보내 배분을 받아 쥔다. 판 haul = both(요구 321 · 511-C)면 배분 모듈을 **그때만** + * dynamic import 해 브라우저가 셈(기본값이면 안 받음). 계산은 한 벌 — 서버도 같은 TS 를 Node 로 돈다. * * **조용히 따라오게 한다** — 편집이 멈추면 뒤에서 물어 두므로, 유토곡선 패널을 펼치는 * 순간에는 이미 도착해 있다. 늦게 온 응답은 버린다(최신 요청만 채택). @@ -12,11 +12,30 @@ import { API_BASE_URL } from "@config/config_frontend"; import type { HaulPlan } from "@util/common_util_mass_haul_balance"; +import type { MassHaulResult } from "@util/common_util_mass_haul"; +import type { EarthworkConversion, HaulEquipmentLimit } from "@util/common_util_mass_haul_types"; +import { calcInBrowser } from "../A00_Common/design/design_calc_sites"; /** 서버가 돌려주는 배분 한 벌 — **화면이 쓰는 꼴 그대로**라 그리기 코드가 손대지 않는다. - * `import type` 이라 배분 모듈이 번들에 실리지 않는다(빌드에서 지워진다). */ + * `import type` 이라 배분 모듈이 첫 묶음에 안 실림(판 both 때만 따로 받음). */ export type HaulPlanPayload = HaulPlan | null; +/** 브라우저 셈 문맥 — 서버 `_mass_haul_context` 와 같은 값(sections/context 가 같은 함수로 냄). */ +export interface HaulPlanContext { + conversion: EarthworkConversion; + haulLimits?: HaulEquipmentLimit[]; +} + +/** 판 haul = both — 서버 /haul-plan(`_run_view`)과 같은 인자로 같은 함수. + * ponytail: 채집석 · 구조물 잔토는 서버 `haul_inputs_for` 가 늘 빈 값이라 null — 값 내기 시작하면 sections/context 로 실어 옴. */ +async function browserHaulPlan(result: unknown, ctx: HaulPlanContext): Promise { + const { computeHaulPlan } = await import("@util/common_util_mass_haul_balance"); + return computeHaulPlan(result as MassHaulResult, ctx.haulLimits, { + conversion: ctx.conversion, + drawing: true, + }); +} + /** 편집이 멈춘 것으로 볼 시간(ms). 계획고를 연속으로 누르는 동안은 안 보낸다. */ const SETTLE_MS = 400; /** 배분 계산 대기 상한 — Node 실행 200ms 대라 넉넉히 잡는다. */ @@ -85,8 +104,8 @@ async function postHaulPlan( } export interface HaulPlanPrefetch { - /** 새 누가토량 결과가 나왔음을 알린다 — 잠잠해지면 서버에 물어본다. */ - schedule: (result: unknown) => void; + /** 새 누가토량 결과가 나왔음을 알린다 — 잠잠해지면 서버에 물어본다(판 both = 브라우저 셈). */ + schedule: (result: unknown, ctx?: HaulPlanContext | null) => void; /** 지금 쥐고 있는 배분. 아직 못 받았으면 null. */ current: () => HaulPlanPayload; /** 화면을 떠날 때 예약을 지운다. */ @@ -106,6 +125,7 @@ export function createHaulPlanPrefetch( let sequence = 0; let plan: HaulPlanPayload = null; let pending: unknown = null; + let pendingCtx: HaulPlanContext | null = null; /** 마지막으로 보낸 입력의 표식 — 같은 값이면 다시 묻지 않는다. */ let sentKey: string | null = null; /** 받은 값을 화면에 반영하려고 다시 그리는 중 — 그 그리기가 거는 요청은 무시한다. */ @@ -116,10 +136,18 @@ export function createHaulPlanPrefetch( if (seq === sequence) sentKey = null; } - async function send(result: unknown, seq: number, key: string): Promise { + async function send( + result: unknown, + ctx: HaulPlanContext | null, + seq: number, + key: string, + ): Promise { const route = routeId(); if (!route) return forget(seq); - const got = await postHaulPlan(projectId, route, result, key); + const got = + ctx && (await calcInBrowser("haul")) + ? await browserHaulPlan(result, ctx).catch(() => undefined) + : await postHaulPlan(projectId, route, result, key); if (got === undefined) return forget(seq); // 늦게 온 응답은 버린다 — 그 사이 사용자가 계획고를 더 만졌을 수 있다. if (seq !== sequence) return; @@ -136,12 +164,13 @@ export function createHaulPlanPrefetch( } return { - schedule(result) { + schedule(result, ctx) { if (applying) return; // 응답을 반영하려고 다시 그리는 중이다. const key = keyOf(result); if (key === sentKey) return; // 같은 값 — 응답이 부른 재그리기가 되돌아온 것이다. sentKey = key; pending = result; + pendingCtx = ctx ?? null; sequence += 1; const seq = sequence; window.clearTimeout(timer); @@ -149,7 +178,7 @@ export function createHaulPlanPrefetch( const route = routeId(); const known = route ? kept.get(keptKey(projectId, route, key)) : undefined; if (known !== undefined) return void (plan = known); - timer = window.setTimeout(() => void send(pending, seq, key), SETTLE_MS); + timer = window.setTimeout(() => void send(pending, pendingCtx, seq, key), SETTLE_MS); }, current: () => plan, dispose() { diff --git a/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts b/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts index 0e69e5259..33f9f2a81 100644 --- a/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts +++ b/B05_Profile/B05_Profile_UI_Profile_MassHaul.ts @@ -309,7 +309,7 @@ export function createRouteMassHaulPanel( // 배분 선반입도 **여기서** 건다 — 접힘 여부와 무관하게 지나는 자리다(2026-09-06). // 아래 `draw` 안에 두었더니 접힘 가드 뒤라 한 번도 안 불렸고, 그래서 「편집이 멈추면 // 조용히 받아 둔다」가 성립하지 않았다(펼친 뒤에야 첫 요청이 나갔다). - haulPrefetch?.schedule(picked.result); + haulPrefetch?.schedule(picked.result, context); onSummary(badgeValuesFrom(picked.result)); } diff --git a/B06_Section/B06_Section_Router_HaulPlan.py b/B06_Section/B06_Section_Router_HaulPlan.py index 947de6450..c0dc8e821 100644 --- a/B06_Section/B06_Section_Router_HaulPlan.py +++ b/B06_Section/B06_Section_Router_HaulPlan.py @@ -5,8 +5,9 @@ 멈추면 그 결과를 여기로 보내 배분을 받아 쥔다. 그래서 유토곡선 패널을 펼치는 순간이 즉시가 된다(미리 받아 뒀으므로). -**계산은 한 벌이다** — 화면이 쓰던 TS 를 Node 진입점(`B06_Section_Server_Calc_Node.ts`)으로 -그대로 돌린다(CLAUDE.md 5장). 파이썬으로 옮기면 저장 정본과 화면 값이 갈린다. +**계산은 한 벌이다** — 화면이 쓰던 TS(`computeHaulPlan`)를 늘 떠 있는 Node 시험 길 `haul.view` +(`design_calc_node_trial.ts` · 511-C)로 그대로 돌린다(CLAUDE.md 5장). 파이썬으로 옮기면 저장 정본과 +화면 값이 갈린다. 정본을 만들지 않는다 — 이 응답은 **표시 전용**이다. 저장 정본은 [저장]·[확정] 뒤 `recompute_server_side` 가 따로 낸다. @@ -28,21 +29,19 @@ from fastapi.responses import JSONResponse from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path from B06_Section.B06_Section_Server_Calc_Prebuild import ( - BUNDLE, _mass_haul_context, conversion_factors_for, haul_inputs_for, haul_limits_for, ) +from common_util.common_util_calc_runner import run_calc_served from common_util.common_util_derived_store import read_derived, write_derived -from common_util.common_util_node_bundle import run_bundle_json from common_util.common_util_storage import resolve_stored_project_path from config.config_db import run_with_connection logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/projects", tags=["B06 Profile Cross"]) -_NPM_SCRIPT = "build:server-calc" # 측점 수 상한 — 정상 노선은 수백 곳이다. 그보다 크면 비정상 요청으로 본다. _MAX_POINTS = 5000 # 열쇠(누가토량 결과 + 계수 · 장비 경계) → 배분 — 같은 열쇠면 Node 를 다시 안 띄움 @@ -64,6 +63,26 @@ def _keep(key: str, plan: Any) -> None: _KEPT.popitem(last=False) +def _run_view(result: dict[str, Any], context: dict[str, Any]) -> dict[str, Any] | None: + """화면 꼴 배분(그림용 · 잔진동 거름) — 옛 server-calc `haul_plan_for` 갈래와 같은 인자.""" + options = { + key: context.get(key) + for key in ( + "collected_stone_deduction_m3", + "collected_stone_by_ground_m3", + "collected_stone_ground_unknown_m3", + "structure_spoil_m3", + "structure_spoil_points", + ) + } + options["conversion"] = context.get("earthwork_conversion") + options["drawing"] = True + args = [result, context.get("haul_equipment_limits"), options] + answer = run_calc_served(["haul.view"], {"args": {"haul.view": args}}) + results = answer.get("results") if isinstance(answer, dict) else None + return {"haul_plan": results["haul.view"]} if isinstance(results, dict) else None + + @router.post("/{project_id}/sections/{route_id}/haul-plan", response_model=None) async def compute_haul_plan( project_id: UUID, @@ -114,12 +133,7 @@ async def compute_haul_plan( _keep(key, stored["value"]) return JSONResponse(content={"status": "success", "haul_plan": stored["value"]}) try: - output = await asyncio.to_thread( - run_bundle_json, - BUNDLE, - _NPM_SCRIPT, - {"haul_plan_for": result, "context": context}, - ) + output = await asyncio.to_thread(_run_view, result, context) except Exception: logger.exception("유토 배분 계산 실패: project_id=%s route_id=%s", project_id, route_id) return JSONResponse( diff --git a/B06_Section/B06_Section_Server_Calc_Prebuild.py b/B06_Section/B06_Section_Server_Calc_Prebuild.py index 0b8d11742..6accacc07 100644 --- a/B06_Section/B06_Section_Server_Calc_Prebuild.py +++ b/B06_Section/B06_Section_Server_Calc_Prebuild.py @@ -13,8 +13,9 @@ **보안**이다 — 저장 경로가 브라우저 계산이면 유토 배분·운반거리 코드가 번들에 남아야 해서 화면에서 안 그려도 뺄 수 없다. 대가는 저장 대기 850ms → 약 980ms(Node 131ms). -**계산을 다시 짜지 않는다.** 화면이 쓰는 TS 를 Node 진입점(`B06_Section_Server_Calc_Node.ts`) -으로 감싸 그대로 돌린다. 파이썬으로 포팅하면 같은 기하가 두 벌이 되어 「그림은 이런데 +**계산을 다시 짜지 않는다.** 화면이 쓰는 TS 를 늘 떠 있는 Node(`design_calc_node.ts` · +`structure.layouts` · `masshaul.cumulative` · `haul.plan` · 511-C)로 그대로 돌린다. +파이썬으로 포팅하면 같은 기하가 두 벌이 되어 「그림은 이런데 수량은 저렇다」가 생긴다. 실패는 던진다 — 부르는 쪽([저장] · [확정])이 `recomputed: false` 로 화면에 알린다(388-2). @@ -38,7 +39,9 @@ from B06_Section.B06_Section_Repository import ( merge_longitudinal_section_data, ) from B06_Section.B06_Section_Repository_Bulk import merge_cross_section_design_batches -from common_util.common_util_node_bundle import bundle_stale, run_bundle_json +from common_util.common_util_calc_runner import BUNDLE as NODE_BUNDLE +from common_util.common_util_calc_runner import run_calc, run_calc_served +from common_util.common_util_node_bundle import bundle_stale from common_util.common_util_project_settings import ( haul_equipment_limits, mixed_conversion_factors, @@ -56,6 +59,7 @@ from config.config_system import ( logger = logging.getLogger(__name__) ROOT = Path(__file__).resolve().parents[1] +# 옛 server-calc 묶음 — 정본 길은 늘 떠 있는 Node(`_run_node`) · 이 묶음은 시험 대조만(B-6 걷음 몫) BUNDLE = ROOT / "config" / "server_calc_node" / "B06_Section_Server_Calc_Node.js" _NPM_SCRIPT = "build:server-calc" # 정본에 얹는 값만 받는다 — Node 가 다른 키를 내도 설계 데이터에 흘리지 않는다. @@ -87,16 +91,37 @@ def _node_fingerprint(node_input: dict[str, Any]) -> str: json.dumps(node_input, sort_keys=True, default=str).encode("utf-8"), usedforsecurity=False, ) - digest.update(str(BUNDLE.stat().st_mtime_ns if BUNDLE.is_file() else 0).encode()) + digest.update(str(NODE_BUNDLE.stat().st_mtime_ns if NODE_BUNDLE.is_file() else 0).encode()) return digest.hexdigest() +_NODE_IDS = ("structure.layouts", "masshaul.cumulative", "haul.plan") + + +def _run_node(node_input: dict[str, Any]) -> dict[str, Any] | None: + """늘 떠 있는 Node(못 부르면 새 프로세스) 셋 → 옛 server-calc 답 꼴 + `{areas, mass_haul, extra_walls}`.""" + answer = run_calc_served(_NODE_IDS, node_input) + results = answer.get("results") if isinstance(answer, dict) else None + if not isinstance(results, dict): + results = run_calc(_NODE_IDS, node_input) + if not isinstance(results, dict): + return None + layouts = results.get("structure.layouts") or {} + cumulative = results.get("masshaul.cumulative") + return { + "areas": layouts.get("areas"), + "mass_haul": {**cumulative, **(results.get("haul.plan") or {})} if cumulative else None, + "extra_walls": layouts.get("extra_walls"), + } + + def _node_output(node_input: dict[str, Any]) -> Any: """같은 입력이면 지난 결과 · 아니면 Node(면적 · 유토곡선)를 돌려 기억한다.""" key = _node_fingerprint(node_input) - if key in _NODE_CACHE and not bundle_stale(BUNDLE): + if key in _NODE_CACHE and not bundle_stale(NODE_BUNDLE): return _NODE_CACHE[key] - output = run_bundle_json(BUNDLE, _NPM_SCRIPT, node_input) + output = _run_node(node_input) if output is None: # 실패는 기억하지 않음 — 다음 [저장]이 다시 셈 return None if len(_NODE_CACHE) >= _NODE_CACHE_LIMIT: diff --git a/common_util/common_util_calc_runner.py b/common_util/common_util_calc_runner.py index 712ff8a56..439d22f84 100644 --- a/common_util/common_util_calc_runner.py +++ b/common_util/common_util_calc_runner.py @@ -6,7 +6,7 @@ 입력 꼴 = 입구 머리(`detail` · `route_points` · `context` · `excavation`). `run_calc_served` = 계산 자리 「서버」 창구(511-A · `B06_Section_Router_Calc`) — 늘 떠 있는 Node 하나(`--serve` · 한 줄 요청 · 한 줄 답) · 죽음 · 묶음 다시 지음 = 다시 띄움 · -시험 길 `TRIAL_IDS` · 묶음 단계 `STAGE_IDS`(511-B) 도 받음. +시험 길 `TRIAL_IDS` · 묶음 단계 `STAGE_IDS`(511-B) · 유토 배분 화면 꼴 `VIEW_IDS`(511-C) 도 받음. """ from __future__ import annotations @@ -72,6 +72,8 @@ TRIAL_IDS = frozenset( STAGE_IDS = frozenset( {"stage.route", "stage.follow", "stage.stations", "stage.profile", "stage.cross"} ) +# 유토 배분 화면 꼴(`design_calc_node.ts` `haul.view` · /haul-plan 창구 · 511-C) +VIEW_IDS = frozenset({"haul.view"}) SERVE_TIMEOUT_S = 60 @@ -145,7 +147,7 @@ atexit.register(_SERVED.stop) def run_calc_served(ids: Sequence[str], inputs: dict[str, Any]) -> dict[str, Any] | None: """창구 길 — `{results, ms(Node 셈), wall_ms(파이썬 ↔ Node)}` · `{error}` · 못 돌림 None.""" - unknown = [item for item in ids if item not in NODE_IDS | TRIAL_IDS | STAGE_IDS] + unknown = [item for item in ids if item not in NODE_IDS | TRIAL_IDS | STAGE_IDS | VIEW_IDS] if unknown: raise ValueError(f"서버 계산 id 아님: {', '.join(unknown)}") t0 = time.perf_counter() diff --git a/common_util/common_util_calc_sites.py b/common_util/common_util_calc_sites.py index 9ffaaf656..d2d5e45a5 100644 --- a/common_util/common_util_calc_sites.py +++ b/common_util/common_util_calc_sites.py @@ -3,8 +3,10 @@ 판 = `resources/master_template/calc_sites.json` `{version, sites: {묶음: "both" | "server"}}`. both = 조작 중 브라우저 셈 + [저장] 서버 정본(지금 등록부 자리) · server = 조작 중에도 서버 길 (G1 · D3 = 504-2 · ③ 토량 · ④ 이월 · ⑧ 고정 브라우저(시험용 · fixed 는 정본 칸 표시) = 511-A 창구). -판이 없거나 깨지면 기본값(전부 both = 등록부 그대로). [저장] · [확정] 서버 정본 길은 판을 안 읽음. -묶음 12 줄 = 등록부(`design_calc_registry*.ts`) # 전부 · 같은지 시험 `test_309_2_calc_sites.py`. +판이 없거나 깨지면 기본값(줄 `default` · 없으면 both = 등록부 그대로 · haul = server). +haul(E2 · 511-C · 요구 321) both = 브라우저가 배분 모듈을 그때만 불러 셈 · server = /haul-plan. +[저장] · [확정] 서버 정본 길은 판을 안 읽음. +묶음 13 줄 = 등록부(`design_calc_registry*.ts`) # 전부 · 같은지 시험 `test_309_2_calc_sites.py`. """ from __future__ import annotations @@ -65,12 +67,23 @@ GROUPS: tuple[dict[str, Any], ...] = ( }, # 폼 기본값 도구(B04 · B08 파이썬 · 고를 것 없음) {"key": "form_default", "nos": ["F9"], "switchable": False, "fixed": "both", "why": None}, + # 유토 배분 — 기본 server(지금 /haul-plan 그대로) · both = 브라우저 셈(511-C) + { + "key": "haul", + "nos": ["E2"], + "switchable": True, + "fixed": None, + "why": "haul", + "default": "server", + }, + # 서버만 — 줄마다 까닭(`whys` · 511-C) { "key": "fixed_server", - "nos": ["E2", "G3", "D1", "D2", "N1"], + "nos": ["G3", "D1", "D2", "N1"], "switchable": False, "fixed": "server", "why": "server", + "whys": {n: f"server_{n}" for n in ("G3", "D1", "D2", "N1")}, }, { "key": "fixed_browser", @@ -84,17 +97,18 @@ GROUPS: tuple[dict[str, Any], ...] = ( ORDER = tuple(g["key"] for g in GROUPS if g["switchable"]) SWITCHABLE = frozenset(ORDER) LOCKED = frozenset(g["key"] for g in GROUPS if not g["switchable"]) +DEFAULTS = {g["key"]: g.get("default", "both") for g in GROUPS if g["switchable"]} def read_sites() -> dict[str, Any]: - """판 — 없음 · 깨짐 · 모르는 값 = 그 줄 기본값(both).""" + """판 — 없음 · 깨짐 · 모르는 값 = 그 줄 기본값(`DEFAULTS`).""" try: raw = json.loads(PATH.read_text(encoding="utf-8")) except (OSError, ValueError): raw = {} stored = raw.get("sites") if isinstance(raw, dict) else None stored = stored if isinstance(stored, dict) else {} - sites = {k: stored.get(k) if stored.get(k) in VALUES else "both" for k in ORDER} + sites = {k: stored.get(k) if stored.get(k) in VALUES else DEFAULTS[k] for k in ORDER} version = raw.get("version") if isinstance(raw, dict) else None return {"version": version if isinstance(version, int) else 0, "sites": sites} diff --git a/resources/tester/fixtures/haul_511_c.json b/resources/tester/fixtures/haul_511_c.json new file mode 100644 index 000000000..7d52dd7f7 --- /dev/null +++ b/resources/tester/fixtures/haul_511_c.json @@ -0,0 +1 @@ +{"483":{"basis":"compacted","conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"cut_natural_m3":{"soil":2091.02,"ripping_rock":5776.64,"blasting_rock":0},"cut_compacted_m3":8611.7,"fill_compacted_m3":20716.74,"final_cumulative_m3":-12105.04,"surplus_m3":0,"shortage_m3":12105.04,"points":[{"station_id":"station_000000000000","chainage_m":0,"net_volume_m3":0,"cumulative_volume_m3":0,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-18.6,"fill_m3":0},{"station_id":"station_000000020000","chainage_m":20,"net_volume_m3":-369.38,"cumulative_volume_m3":-369.38,"cut_soil_m3":21.85,"cut_rock_m3":20.93,"cut_rr_m3":20.93,"cut_br_m3":0,"net_area_m2":-18.34,"fill_m3":413.42},{"station_id":"station_000000040000","chainage_m":40,"net_volume_m3":-274.03,"cumulative_volume_m3":-643.41,"cut_soil_m3":33.73,"cut_rock_m3":43.29,"cut_rr_m3":43.29,"cut_br_m3":0,"net_area_m2":-9.06,"fill_m3":354.83},{"station_id":"station_000000060000","chainage_m":60,"net_volume_m3":-115.69,"cumulative_volume_m3":-759.11,"cut_soil_m3":37.64,"cut_rock_m3":67.38,"cut_rr_m3":67.38,"cut_br_m3":0,"net_area_m2":-2.51,"fill_m3":228.07},{"station_id":"station_000000080000","chainage_m":80,"net_volume_m3":-207.19,"cumulative_volume_m3":-966.29,"cut_soil_m3":39.79,"cut_rock_m3":82.09,"cut_rr_m3":82.09,"cut_br_m3":0,"net_area_m2":-18.21,"fill_m3":338.63},{"station_id":"station_000000100000","chainage_m":100,"net_volume_m3":-633.05,"cumulative_volume_m3":-1599.34,"cut_soil_m3":42.35,"cut_rock_m3":92.72,"cut_rr_m3":92.72,"cut_br_m3":0,"net_area_m2":-45.09,"fill_m3":779.18},{"station_id":"station_000000120000","chainage_m":120,"net_volume_m3":-498.94,"cumulative_volume_m3":-2098.28,"cut_soil_m3":46.28,"cut_rock_m3":109.16,"cut_rr_m3":109.16,"cut_br_m3":0,"net_area_m2":-4.8,"fill_m3":667.76},{"station_id":"station_000000140000","chainage_m":140,"net_volume_m3":-259.65,"cumulative_volume_m3":-2357.93,"cut_soil_m3":33.87,"cut_rock_m3":64.11,"cut_rr_m3":64.11,"cut_br_m3":0,"net_area_m2":-21.16,"fill_m3":364.82},{"station_id":"station_000000160000","chainage_m":160,"net_volume_m3":-244.39,"cumulative_volume_m3":-2602.32,"cut_soil_m3":26.78,"cut_rock_m3":31.16,"cut_rr_m3":31.16,"cut_br_m3":0,"net_area_m2":-3.27,"fill_m3":304.78},{"station_id":"station_000000180000","chainage_m":180,"net_volume_m3":-18,"cumulative_volume_m3":-2620.32,"cut_soil_m3":33.02,"cut_rock_m3":40.08,"cut_rr_m3":40.08,"cut_br_m3":0,"net_area_m2":1.47,"fill_m3":94.42},{"station_id":"station_000000200000","chainage_m":200,"net_volume_m3":12.51,"cumulative_volume_m3":-2607.81,"cut_soil_m3":31.5,"cut_rock_m3":32.68,"cut_rr_m3":32.68,"cut_br_m3":0,"net_area_m2":-0.22,"fill_m3":53.91},{"station_id":"station_000000220000","chainage_m":220,"net_volume_m3":41.35,"cumulative_volume_m3":-2566.47,"cut_soil_m3":36.53,"cut_rock_m3":51.91,"cut_rr_m3":51.91,"cut_br_m3":0,"net_area_m2":4.36,"fill_m3":52.01},{"station_id":"station_000000240000","chainage_m":240,"net_volume_m3":289.03,"cumulative_volume_m3":-2277.44,"cut_soil_m3":58.42,"cut_rock_m3":219.33,"cut_rr_m3":219.33,"cut_br_m3":0,"net_area_m2":24.55,"fill_m3":19.08},{"station_id":"station_000000260000","chainage_m":260,"net_volume_m3":-238.44,"cumulative_volume_m3":-2515.88,"cut_soil_m3":61.87,"cut_rock_m3":252.02,"cut_rr_m3":252.02,"cut_br_m3":0,"net_area_m2":-48.39,"fill_m3":587.74},{"station_id":"station_000000280000","chainage_m":280,"net_volume_m3":-1858.09,"cumulative_volume_m3":-4373.97,"cut_soil_m3":44.49,"cut_rock_m3":107.89,"cut_rr_m3":107.89,"cut_br_m3":0,"net_area_m2":-137.42,"fill_m3":2023.82},{"station_id":"station_000000300000","chainage_m":300,"net_volume_m3":-1429.07,"cumulative_volume_m3":-5803.04,"cut_soil_m3":33.94,"cut_rock_m3":52.71,"cut_rr_m3":52.71,"cut_br_m3":0,"net_area_m2":-5.49,"fill_m3":1521.02},{"station_id":"station_000000320000","chainage_m":320,"net_volume_m3":-109.85,"cumulative_volume_m3":-5912.89,"cut_soil_m3":25.59,"cut_rock_m3":20.9,"cut_rr_m3":20.9,"cut_br_m3":0,"net_area_m2":-5.5,"fill_m3":157.23},{"station_id":"station_000000340000","chainage_m":340,"net_volume_m3":-87.85,"cumulative_volume_m3":-6000.74,"cut_soil_m3":27.32,"cut_rock_m3":27.87,"cut_rr_m3":27.87,"cut_br_m3":0,"net_area_m2":-3.29,"fill_m3":144.91},{"station_id":"station_000000360000","chainage_m":360,"net_volume_m3":-85.8,"cumulative_volume_m3":-6086.55,"cut_soil_m3":32.32,"cut_rock_m3":40.02,"cut_rr_m3":40.02,"cut_br_m3":0,"net_area_m2":-5.29,"fill_m3":161.51},{"station_id":"station_000000380000","chainage_m":380,"net_volume_m3":-103.06,"cumulative_volume_m3":-6189.61,"cut_soil_m3":30.52,"cut_rock_m3":33.82,"cut_rr_m3":33.82,"cut_br_m3":0,"net_area_m2":-5.01,"fill_m3":169.93},{"station_id":"station_000000400000","chainage_m":400,"net_volume_m3":-42.48,"cumulative_volume_m3":-6232.09,"cut_soil_m3":32.53,"cut_rock_m3":36.82,"cut_rr_m3":36.82,"cut_br_m3":0,"net_area_m2":0.77,"fill_m3":114.66},{"station_id":"station_000000420000","chainage_m":420,"net_volume_m3":9.02,"cumulative_volume_m3":-6223.07,"cut_soil_m3":33.06,"cut_rock_m3":32.79,"cut_rr_m3":32.79,"cut_br_m3":0,"net_area_m2":0.14,"fill_m3":58.93},{"station_id":"station_000000425000","chainage_m":425,"net_volume_m3":1.7,"cumulative_volume_m3":-6221.37,"cut_soil_m3":7.79,"cut_rock_m3":6.02,"cut_rr_m3":6.02,"cut_br_m3":0,"net_area_m2":0.55,"fill_m3":12.32},{"station_id":"station_000000440000","chainage_m":440,"net_volume_m3":5.4,"cumulative_volume_m3":-6215.97,"cut_soil_m3":21.82,"cut_rock_m3":15.92,"cut_rr_m3":15.92,"cut_br_m3":0,"net_area_m2":0.17,"fill_m3":32.79},{"station_id":"station_000000460000","chainage_m":460,"net_volume_m3":19.24,"cumulative_volume_m3":-6196.73,"cut_soil_m3":32.37,"cut_rock_m3":33.87,"cut_rr_m3":33.87,"cut_br_m3":0,"net_area_m2":1.75,"fill_m3":49.36},{"station_id":"station_000000480000","chainage_m":480,"net_volume_m3":-81.94,"cumulative_volume_m3":-6278.67,"cut_soil_m3":30.89,"cut_rock_m3":35.15,"cut_rr_m3":35.15,"cut_br_m3":0,"net_area_m2":-9.94,"fill_m3":150.69},{"station_id":"station_000000500000","chainage_m":500,"net_volume_m3":-110.69,"cumulative_volume_m3":-6389.36,"cut_soil_m3":25.58,"cut_rock_m3":22.41,"cut_rr_m3":22.41,"cut_br_m3":0,"net_area_m2":-1.13,"fill_m3":159.82},{"station_id":"station_000000520000","chainage_m":520,"net_volume_m3":-29.5,"cumulative_volume_m3":-6418.86,"cut_soil_m3":29.99,"cut_rock_m3":32.13,"cut_rr_m3":32.13,"cut_br_m3":0,"net_area_m2":-1.82,"fill_m3":93.92},{"station_id":"station_000000540000","chainage_m":540,"net_volume_m3":-49.64,"cumulative_volume_m3":-6468.5,"cut_soil_m3":31.54,"cut_rock_m3":34.97,"cut_rr_m3":34.97,"cut_br_m3":0,"net_area_m2":-3.14,"fill_m3":118.75},{"station_id":"station_000000560000","chainage_m":560,"net_volume_m3":-42.55,"cumulative_volume_m3":-6511.05,"cut_soil_m3":31.36,"cut_rock_m3":34.09,"cut_rr_m3":34.09,"cut_br_m3":0,"net_area_m2":-1.12,"fill_m3":110.48},{"station_id":"station_000000580000","chainage_m":580,"net_volume_m3":-30.35,"cumulative_volume_m3":-6541.4,"cut_soil_m3":26.84,"cut_rock_m3":25.13,"cut_rr_m3":25.13,"cut_br_m3":0,"net_area_m2":-1.92,"fill_m3":83.78},{"station_id":"station_000000600000","chainage_m":600,"net_volume_m3":-32.67,"cumulative_volume_m3":-6574.07,"cut_soil_m3":20.67,"cut_rock_m3":10.64,"cut_rr_m3":10.64,"cut_br_m3":0,"net_area_m2":-1.35,"fill_m3":63.66},{"station_id":"station_000000620000","chainage_m":620,"net_volume_m3":-21.07,"cumulative_volume_m3":-6595.14,"cut_soil_m3":18.95,"cut_rock_m3":6.65,"cut_rr_m3":6.65,"cut_br_m3":0,"net_area_m2":-0.76,"fill_m3":45.87},{"station_id":"station_000000640000","chainage_m":640,"net_volume_m3":-2.41,"cumulative_volume_m3":-6597.54,"cut_soil_m3":20.68,"cut_rock_m3":7.49,"cut_rr_m3":7.49,"cut_br_m3":0,"net_area_m2":0.52,"fill_m3":29.75},{"station_id":"station_000000660000","chainage_m":660,"net_volume_m3":-8.28,"cumulative_volume_m3":-6605.82,"cut_soil_m3":26.36,"cut_rock_m3":21.06,"cut_rr_m3":21.06,"cut_br_m3":0,"net_area_m2":-1.35,"fill_m3":56.54},{"station_id":"station_000000680000","chainage_m":680,"net_volume_m3":-18.75,"cumulative_volume_m3":-6624.57,"cut_soil_m3":29.59,"cut_rock_m3":30.57,"cut_rr_m3":30.57,"cut_br_m3":0,"net_area_m2":-0.53,"fill_m3":80.99},{"station_id":"station_000000700000","chainage_m":700,"net_volume_m3":-16.5,"cumulative_volume_m3":-6641.07,"cut_soil_m3":30.24,"cut_rock_m3":31.98,"cut_rr_m3":31.98,"cut_br_m3":0,"net_area_m2":-1.12,"fill_m3":80.97},{"station_id":"station_000000720000","chainage_m":720,"net_volume_m3":-18.2,"cumulative_volume_m3":-6659.27,"cut_soil_m3":30.65,"cut_rock_m3":33.63,"cut_rr_m3":33.63,"cut_br_m3":0,"net_area_m2":-0.7,"fill_m3":84.97},{"station_id":"station_000000740000","chainage_m":740,"net_volume_m3":-62.21,"cumulative_volume_m3":-6721.48,"cut_soil_m3":26.82,"cut_rock_m3":24.68,"cut_rr_m3":24.68,"cut_br_m3":0,"net_area_m2":-5.52,"fill_m3":115.09},{"station_id":"station_000000760000","chainage_m":760,"net_volume_m3":-85.12,"cumulative_volume_m3":-6806.6,"cut_soil_m3":27.13,"cut_rock_m3":25.34,"cut_rr_m3":25.34,"cut_br_m3":0,"net_area_m2":-2.99,"fill_m3":139.06},{"station_id":"station_000000780000","chainage_m":780,"net_volume_m3":-81.7,"cumulative_volume_m3":-6888.3,"cut_soil_m3":29.96,"cut_rock_m3":31.27,"cut_rr_m3":31.27,"cut_br_m3":0,"net_area_m2":-5.18,"fill_m3":145.09},{"station_id":"station_000000800000","chainage_m":800,"net_volume_m3":-652.25,"cumulative_volume_m3":-7540.55,"cut_soil_m3":27.68,"cut_rock_m3":26.25,"cut_rr_m3":26.25,"cut_br_m3":0,"net_area_m2":-60.05,"fill_m3":707.75},{"station_id":"station_000000820000","chainage_m":820,"net_volume_m3":-824.37,"cumulative_volume_m3":-8364.92,"cut_soil_m3":34.79,"cut_rock_m3":55.49,"cut_rr_m3":55.49,"cut_br_m3":0,"net_area_m2":-22.39,"fill_m3":920.32},{"station_id":"station_000000840000","chainage_m":840,"net_volume_m3":-540.49,"cumulative_volume_m3":-8905.41,"cut_soil_m3":43.25,"cut_rock_m3":91.33,"cut_rr_m3":91.33,"cut_br_m3":0,"net_area_m2":-31.66,"fill_m3":685.81},{"station_id":"station_000000860000","chainage_m":860,"net_volume_m3":-1182.55,"cumulative_volume_m3":-10087.95,"cut_soil_m3":34.64,"cut_rock_m3":60.11,"cut_rr_m3":60.11,"cut_br_m3":0,"net_area_m2":-86.6,"fill_m3":1283.75},{"station_id":"station_000000880000","chainage_m":880,"net_volume_m3":-1740.44,"cumulative_volume_m3":-11828.39,"cut_soil_m3":31.58,"cut_rock_m3":49.63,"cut_rr_m3":49.63,"cut_br_m3":0,"net_area_m2":-87.45,"fill_m3":1826.68},{"station_id":"station_000000900000","chainage_m":900,"net_volume_m3":-1005.05,"cumulative_volume_m3":-12833.44,"cut_soil_m3":34.66,"cut_rock_m3":56.98,"cut_rr_m3":56.98,"cut_br_m3":0,"net_area_m2":-13.06,"fill_m3":1102.63},{"station_id":"station_000000920000","chainage_m":920,"net_volume_m3":-322.66,"cumulative_volume_m3":-13156.1,"cut_soil_m3":32.9,"cut_rock_m3":43.54,"cut_rr_m3":43.54,"cut_br_m3":0,"net_area_m2":-19.21,"fill_m3":402.99},{"station_id":"station_000000940000","chainage_m":940,"net_volume_m3":-886.78,"cumulative_volume_m3":-14042.88,"cut_soil_m3":28.84,"cut_rock_m3":33.41,"cut_rr_m3":33.41,"cut_br_m3":0,"net_area_m2":-69.47,"fill_m3":951.66},{"station_id":"station_000000960000","chainage_m":960,"net_volume_m3":-869.46,"cumulative_volume_m3":-14912.34,"cut_soil_m3":26.07,"cut_rock_m3":20.82,"cut_rr_m3":20.82,"cut_br_m3":0,"net_area_m2":-17.48,"fill_m3":917.18},{"station_id":"station_000000980000","chainage_m":980,"net_volume_m3":-239.19,"cumulative_volume_m3":-15151.54,"cut_soil_m3":25.2,"cut_rock_m3":17.84,"cut_rr_m3":17.84,"cut_br_m3":0,"net_area_m2":-6.44,"fill_m3":282.66},{"station_id":"station_000001000000","chainage_m":1000,"net_volume_m3":-102.27,"cumulative_volume_m3":-15253.81,"cut_soil_m3":23.16,"cut_rock_m3":17.02,"cut_rr_m3":17.02,"cut_br_m3":0,"net_area_m2":-3.79,"fill_m3":142.94},{"station_id":"station_000001020000","chainage_m":1020,"net_volume_m3":-96.82,"cumulative_volume_m3":-15350.63,"cut_soil_m3":35.53,"cut_rock_m3":59.86,"cut_rr_m3":59.86,"cut_br_m3":0,"net_area_m2":-5.9,"fill_m3":198.52},{"station_id":"station_000001040000","chainage_m":1040,"net_volume_m3":1095.58,"cumulative_volume_m3":-14255.04,"cut_soil_m3":94.44,"cut_rock_m3":986.1,"cut_rr_m3":986.1,"cut_br_m3":0,"net_area_m2":115.45,"fill_m3":138.21},{"station_id":"station_000001060000","chainage_m":1060,"net_volume_m3":1869.54,"cumulative_volume_m3":-12385.5,"cut_soil_m3":126.5,"cut_rock_m3":1507.04,"cut_rr_m3":1507.04,"cut_br_m3":0,"net_area_m2":71.5,"fill_m3":0},{"station_id":"station_000001080000","chainage_m":1080,"net_volume_m3":790.62,"cumulative_volume_m3":-11594.88,"cut_soil_m3":81.97,"cut_rock_m3":626.13,"cut_rr_m3":626.13,"cut_br_m3":0,"net_area_m2":7.56,"fill_m3":12.59},{"station_id":"station_000001100000","chainage_m":1100,"net_volume_m3":60.74,"cumulative_volume_m3":-11534.14,"cut_soil_m3":42.78,"cut_rock_m3":76.89,"cut_rr_m3":76.89,"cut_br_m3":0,"net_area_m2":-1.49,"fill_m3":67.34},{"station_id":"station_000001120000","chainage_m":1120,"net_volume_m3":-14.51,"cumulative_volume_m3":-11548.65,"cut_soil_m3":33.56,"cut_rock_m3":43.95,"cut_rr_m3":43.95,"cut_br_m3":0,"net_area_m2":0.04,"fill_m3":95.92},{"station_id":"station_000001140000","chainage_m":1140,"net_volume_m3":-27.26,"cumulative_volume_m3":-11575.91,"cut_soil_m3":31.18,"cut_rock_m3":36.53,"cut_rr_m3":36.53,"cut_br_m3":0,"net_area_m2":-2.76,"fill_m3":97.88},{"station_id":"station_000001160000","chainage_m":1160,"net_volume_m3":-62.35,"cumulative_volume_m3":-11638.25,"cut_soil_m3":29.57,"cut_rock_m3":32.89,"cut_rr_m3":32.89,"cut_br_m3":0,"net_area_m2":-3.47,"fill_m3":127.27},{"station_id":"station_000001172990","chainage_m":1172.99,"net_volume_m3":-466.79,"cumulative_volume_m3":-12105.04,"cut_soil_m3":10.09,"cut_rock_m3":12.19,"cut_rr_m3":12.19,"cut_br_m3":0,"net_area_m2":-68.39,"fill_m3":490.08}]},"513":{"basis":"compacted","conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"cut_natural_m3":{"soil":2051.38,"ripping_rock":3871.2,"blasting_rock":0},"cut_compacted_m3":6356.19,"fill_compacted_m3":23514.75,"final_cumulative_m3":-17158.56,"surplus_m3":0,"shortage_m3":17158.56,"points":[{"station_id":"station_000000000000","chainage_m":0,"net_volume_m3":0,"cumulative_volume_m3":0,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":0.12,"fill_m3":0},{"station_id":"station_000000020000","chainage_m":20,"net_volume_m3":-34.67,"cumulative_volume_m3":-34.67,"cut_soil_m3":17,"cut_rock_m3":15.42,"cut_rr_m3":15.42,"cut_br_m3":0,"net_area_m2":-3.59,"fill_m3":67.94},{"station_id":"station_000000040000","chainage_m":40,"net_volume_m3":-29.06,"cumulative_volume_m3":-63.74,"cut_soil_m3":29.94,"cut_rock_m3":36.23,"cut_rr_m3":36.23,"cut_br_m3":0,"net_area_m2":0.68,"fill_m3":98.21},{"station_id":"station_000000060000","chainage_m":60,"net_volume_m3":-9.89,"cumulative_volume_m3":-73.63,"cut_soil_m3":31.75,"cut_rock_m3":46.56,"cut_rr_m3":46.56,"cut_br_m3":0,"net_area_m2":-1.67,"fill_m3":92.71},{"station_id":"station_000000080000","chainage_m":80,"net_volume_m3":23.36,"cumulative_volume_m3":-50.27,"cut_soil_m3":48.1,"cut_rock_m3":105.69,"cut_rr_m3":105.69,"cut_br_m3":0,"net_area_m2":4.01,"fill_m3":143.06},{"station_id":"station_000000100000","chainage_m":100,"net_volume_m3":83.27,"cumulative_volume_m3":33,"cut_soil_m3":50.96,"cut_rock_m3":131.59,"cut_rr_m3":131.59,"cut_br_m3":0,"net_area_m2":4.32,"fill_m3":115.9},{"station_id":"station_000000120000","chainage_m":120,"net_volume_m3":34.05,"cumulative_volume_m3":67.05,"cut_soil_m3":27.87,"cut_rock_m3":55.43,"cut_rr_m3":55.43,"cut_br_m3":0,"net_area_m2":-0.91,"fill_m3":55.61},{"station_id":"station_000000140000","chainage_m":140,"net_volume_m3":6.78,"cumulative_volume_m3":73.83,"cut_soil_m3":31.43,"cut_rock_m3":16.93,"cut_rr_m3":16.93,"cut_br_m3":0,"net_area_m2":1.59,"fill_m3":41.23},{"station_id":"station_000000160000","chainage_m":160,"net_volume_m3":7.93,"cumulative_volume_m3":81.76,"cut_soil_m3":33.23,"cut_rock_m3":19.38,"cut_rr_m3":19.38,"cut_br_m3":0,"net_area_m2":-0.8,"fill_m3":44.56},{"station_id":"station_000000180000","chainage_m":180,"net_volume_m3":67.19,"cumulative_volume_m3":148.95,"cut_soil_m3":38.83,"cut_rock_m3":81.98,"cut_rr_m3":81.98,"cut_br_m3":0,"net_area_m2":7.52,"fill_m3":63.27},{"station_id":"station_000000200000","chainage_m":200,"net_volume_m3":92.04,"cumulative_volume_m3":240.99,"cut_soil_m3":43.81,"cut_rock_m3":95.62,"cut_rr_m3":95.62,"cut_br_m3":0,"net_area_m2":1.69,"fill_m3":58.79},{"station_id":"station_000000220000","chainage_m":220,"net_volume_m3":139.74,"cumulative_volume_m3":380.73,"cut_soil_m3":48.98,"cut_rock_m3":132.87,"cut_rr_m3":132.87,"cut_br_m3":0,"net_area_m2":12.29,"fill_m3":59.14},{"station_id":"station_000000240000","chainage_m":240,"net_volume_m3":-104.03,"cumulative_volume_m3":276.7,"cut_soil_m3":52.27,"cut_rock_m3":148.99,"cut_rr_m3":148.99,"cut_br_m3":0,"net_area_m2":-22.69,"fill_m3":324.64},{"station_id":"station_000000260000","chainage_m":260,"net_volume_m3":-550.34,"cumulative_volume_m3":-273.65,"cut_soil_m3":26.52,"cut_rock_m3":37.9,"cut_rr_m3":37.9,"cut_br_m3":0,"net_area_m2":-32.34,"fill_m3":618.36},{"station_id":"station_000000280000","chainage_m":280,"net_volume_m3":-780.96,"cumulative_volume_m3":-1054.61,"cut_soil_m3":29.71,"cut_rock_m3":74,"cut_rr_m3":74,"cut_br_m3":0,"net_area_m2":-45.75,"fill_m3":893.91},{"station_id":"station_000000300000","chainage_m":300,"net_volume_m3":-605.27,"cumulative_volume_m3":-1659.87,"cut_soil_m3":40.22,"cut_rock_m3":94.48,"cut_rr_m3":94.48,"cut_br_m3":0,"net_area_m2":-14.77,"fill_m3":751.53},{"station_id":"station_000000320000","chainage_m":320,"net_volume_m3":-142.22,"cumulative_volume_m3":-1802.09,"cut_soil_m3":45.78,"cut_rock_m3":74.18,"cut_rr_m3":74.18,"cut_br_m3":0,"net_area_m2":0.55,"fill_m3":269.83},{"station_id":"station_000000340000","chainage_m":340,"net_volume_m3":37.42,"cumulative_volume_m3":-1764.67,"cut_soil_m3":45.1,"cut_rock_m3":84,"cut_rr_m3":84,"cut_br_m3":0,"net_area_m2":3.19,"fill_m3":101.03},{"station_id":"station_000000351000","chainage_m":351,"net_volume_m3":26.08,"cumulative_volume_m3":-1738.58,"cut_soil_m3":19.84,"cut_rock_m3":32.73,"cut_rr_m3":32.73,"cut_br_m3":0,"net_area_m2":1.55,"fill_m3":29.91},{"station_id":"station_000000360000","chainage_m":360,"net_volume_m3":31.23,"cumulative_volume_m3":-1707.35,"cut_soil_m3":19.85,"cut_rock_m3":36.46,"cut_rr_m3":36.46,"cut_br_m3":0,"net_area_m2":5.39,"fill_m3":29.11},{"station_id":"station_000000380000","chainage_m":380,"net_volume_m3":19.77,"cumulative_volume_m3":-1687.58,"cut_soil_m3":41.81,"cut_rock_m3":82.77,"cut_rr_m3":82.77,"cut_br_m3":0,"net_area_m2":-3.41,"fill_m3":114.29},{"station_id":"station_000000400000","chainage_m":400,"net_volume_m3":53.67,"cumulative_volume_m3":-1633.91,"cut_soil_m3":44.87,"cut_rock_m3":95.08,"cut_rr_m3":95.08,"cut_br_m3":0,"net_area_m2":8.78,"fill_m3":97.47},{"station_id":"station_000000420000","chainage_m":420,"net_volume_m3":123.17,"cumulative_volume_m3":-1510.74,"cut_soil_m3":44.25,"cut_rock_m3":87.67,"cut_rr_m3":87.67,"cut_br_m3":0,"net_area_m2":3.54,"fill_m3":18.79},{"station_id":"station_000000440000","chainage_m":440,"net_volume_m3":69.9,"cumulative_volume_m3":-1440.84,"cut_soil_m3":40.8,"cut_rock_m3":54.58,"cut_rr_m3":54.58,"cut_br_m3":0,"net_area_m2":3.45,"fill_m3":30.41},{"station_id":"station_000000460000","chainage_m":460,"net_volume_m3":-83.62,"cumulative_volume_m3":-1524.46,"cut_soil_m3":40.31,"cut_rock_m3":45.9,"cut_rr_m3":45.9,"cut_br_m3":0,"net_area_m2":-11.81,"fill_m3":173.37},{"station_id":"station_000000480000","chainage_m":480,"net_volume_m3":-1194.12,"cumulative_volume_m3":-2718.59,"cut_soil_m3":35.25,"cut_rock_m3":65.61,"cut_rr_m3":65.61,"cut_br_m3":0,"net_area_m2":-107.6,"fill_m3":1302.29},{"station_id":"station_000000500000","chainage_m":500,"net_volume_m3":-1944.94,"cumulative_volume_m3":-4663.53,"cut_soil_m3":41.09,"cut_rock_m3":96.63,"cut_rr_m3":96.63,"cut_br_m3":0,"net_area_m2":-86.9,"fill_m3":2094.5},{"station_id":"station_000000502000","chainage_m":502,"net_volume_m3":-205.89,"cumulative_volume_m3":-4869.41,"cut_soil_m3":4.06,"cut_rock_m3":6.13,"cut_rr_m3":6.13,"cut_br_m3":0,"net_area_m2":-118.99,"fill_m3":216.68},{"station_id":"station_000000520000","chainage_m":520,"net_volume_m3":-1421.38,"cumulative_volume_m3":-6290.8,"cut_soil_m3":29.02,"cut_rock_m3":21.1,"cut_rr_m3":21.1,"cut_br_m3":0,"net_area_m2":-38.94,"fill_m3":1472.09},{"station_id":"station_000000540000","chainage_m":540,"net_volume_m3":-517.42,"cumulative_volume_m3":-6808.22,"cut_soil_m3":21.57,"cut_rock_m3":11.28,"cut_rr_m3":11.28,"cut_br_m3":0,"net_area_m2":-12.8,"fill_m3":549.98},{"station_id":"station_000000560000","chainage_m":560,"net_volume_m3":-155.19,"cumulative_volume_m3":-6963.41,"cut_soil_m3":16.33,"cut_rock_m3":7.09,"cut_rr_m3":7.09,"cut_br_m3":0,"net_area_m2":-2.72,"fill_m3":178.15},{"station_id":"station_000000580000","chainage_m":580,"net_volume_m3":-21.92,"cumulative_volume_m3":-6985.32,"cut_soil_m3":19.38,"cut_rock_m3":11.29,"cut_rr_m3":11.29,"cut_br_m3":0,"net_area_m2":0.53,"fill_m3":52.51},{"station_id":"station_000000600000","chainage_m":600,"net_volume_m3":54.31,"cumulative_volume_m3":-6931.01,"cut_soil_m3":41.77,"cut_rock_m3":63.23,"cut_rr_m3":63.23,"cut_br_m3":0,"net_area_m2":4.9,"fill_m3":56.94},{"station_id":"station_000000620000","chainage_m":620,"net_volume_m3":45.54,"cumulative_volume_m3":-6885.47,"cut_soil_m3":45.42,"cut_rock_m3":75.77,"cut_rr_m3":75.77,"cut_br_m3":0,"net_area_m2":-0.35,"fill_m3":83.61},{"station_id":"station_000000640000","chainage_m":640,"net_volume_m3":122.68,"cumulative_volume_m3":-6762.79,"cut_soil_m3":49.07,"cut_rock_m3":113.84,"cut_rr_m3":113.84,"cut_br_m3":0,"net_area_m2":12.62,"fill_m3":54.1},{"station_id":"station_000000660000","chainage_m":660,"net_volume_m3":113.59,"cumulative_volume_m3":-6649.2,"cut_soil_m3":49.76,"cut_rock_m3":114.9,"cut_rr_m3":114.9,"cut_br_m3":0,"net_area_m2":-1.26,"fill_m3":65.05},{"station_id":"station_000000681000","chainage_m":681,"net_volume_m3":-127.33,"cumulative_volume_m3":-6776.53,"cut_soil_m3":32.98,"cut_rock_m3":31.99,"cut_rr_m3":31.99,"cut_br_m3":0,"net_area_m2":-10.87,"fill_m3":194.28},{"station_id":"station_000000700000","chainage_m":700,"net_volume_m3":-840.67,"cumulative_volume_m3":-7617.2,"cut_soil_m3":29.33,"cut_rock_m3":30.44,"cut_rr_m3":30.44,"cut_br_m3":0,"net_area_m2":-77.62,"fill_m3":902.53},{"station_id":"station_000000720000","chainage_m":720,"net_volume_m3":-882.72,"cumulative_volume_m3":-8499.92,"cut_soil_m3":35.46,"cut_rock_m3":63.13,"cut_rr_m3":63.13,"cut_br_m3":0,"net_area_m2":-10.65,"fill_m3":988.18},{"station_id":"station_000000741000","chainage_m":741,"net_volume_m3":-180.52,"cumulative_volume_m3":-8680.45,"cut_soil_m3":56.42,"cut_rock_m3":149.13,"cut_rr_m3":149.13,"cut_br_m3":0,"net_area_m2":-6.55,"fill_m3":405.04},{"station_id":"station_000000760000","chainage_m":760,"net_volume_m3":-129.16,"cumulative_volume_m3":-8809.6,"cut_soil_m3":47.84,"cut_rock_m3":111.92,"cut_rr_m3":111.92,"cut_br_m3":0,"net_area_m2":-7.05,"fill_m3":302.6},{"station_id":"station_000000781000","chainage_m":781,"net_volume_m3":-180.21,"cumulative_volume_m3":-8989.81,"cut_soil_m3":32.82,"cut_rock_m3":47.43,"cut_rr_m3":47.43,"cut_br_m3":0,"net_area_m2":-10.11,"fill_m3":265},{"station_id":"station_000000800000","chainage_m":800,"net_volume_m3":-1537.88,"cumulative_volume_m3":-10527.69,"cut_soil_m3":31.66,"cut_rock_m3":52.03,"cut_rr_m3":52.03,"cut_br_m3":0,"net_area_m2":-151.77,"fill_m3":1626.99},{"station_id":"station_000000821000","chainage_m":821,"net_volume_m3":-1840.85,"cumulative_volume_m3":-12368.53,"cut_soil_m3":38.85,"cut_rock_m3":57.23,"cut_rr_m3":57.23,"cut_br_m3":0,"net_area_m2":-23.55,"fill_m3":1942.49},{"station_id":"station_000000840000","chainage_m":840,"net_volume_m3":-783,"cumulative_volume_m3":-13151.53,"cut_soil_m3":36.5,"cut_rock_m3":80.64,"cut_rr_m3":80.64,"cut_br_m3":0,"net_area_m2":-58.87,"fill_m3":909.8},{"station_id":"station_000000861000","chainage_m":861,"net_volume_m3":-1181.94,"cumulative_volume_m3":-14333.47,"cut_soil_m3":40.94,"cut_rock_m3":104.93,"cut_rr_m3":104.93,"cut_br_m3":0,"net_area_m2":-53.69,"fill_m3":1341.03},{"station_id":"station_000000880000","chainage_m":880,"net_volume_m3":-607.16,"cumulative_volume_m3":-14940.64,"cut_soil_m3":34.4,"cut_rock_m3":63.49,"cut_rr_m3":63.49,"cut_br_m3":0,"net_area_m2":-10.22,"fill_m3":712.08},{"station_id":"station_000000901000","chainage_m":901,"net_volume_m3":57.24,"cumulative_volume_m3":-14883.4,"cut_soil_m3":53.75,"cut_rock_m3":144.15,"cut_rr_m3":144.15,"cut_br_m3":0,"net_area_m2":15.67,"fill_m3":159.07},{"station_id":"station_000000920000","chainage_m":920,"net_volume_m3":179.2,"cumulative_volume_m3":-14704.2,"cut_soil_m3":49.34,"cut_rock_m3":138.4,"cut_rr_m3":138.4,"cut_br_m3":0,"net_area_m2":3.19,"fill_m3":26.44},{"station_id":"station_000000941000","chainage_m":941,"net_volume_m3":41.98,"cumulative_volume_m3":-14662.22,"cut_soil_m3":49.08,"cut_rock_m3":105.64,"cut_rr_m3":105.64,"cut_br_m3":0,"net_area_m2":0.8,"fill_m3":125.26},{"station_id":"station_000000960000","chainage_m":960,"net_volume_m3":8.79,"cumulative_volume_m3":-14653.42,"cut_soil_m3":46.27,"cut_rock_m3":121.29,"cut_rr_m3":121.29,"cut_br_m3":0,"net_area_m2":0.12,"fill_m3":174.16},{"station_id":"station_000000980000","chainage_m":980,"net_volume_m3":-81.59,"cumulative_volume_m3":-14735.01,"cut_soil_m3":37.28,"cut_rock_m3":83.07,"cut_rr_m3":83.07,"cut_br_m3":0,"net_area_m2":-8.28,"fill_m3":211.91},{"station_id":"station_000001001000","chainage_m":1001,"net_volume_m3":-1175.69,"cumulative_volume_m3":-15910.7,"cut_soil_m3":34.96,"cut_rock_m3":51.49,"cut_rr_m3":51.49,"cut_br_m3":0,"net_area_m2":-103.69,"fill_m3":1267.14},{"station_id":"station_000001020000","chainage_m":1020,"net_volume_m3":-950.93,"cumulative_volume_m3":-16861.62,"cut_soil_m3":32.86,"cut_rock_m3":55.92,"cut_rr_m3":55.92,"cut_br_m3":0,"net_area_m2":3.59,"fill_m3":1045.64},{"station_id":"station_000001040000","chainage_m":1040,"net_volume_m3":-40.25,"cumulative_volume_m3":-16901.88,"cut_soil_m3":35.02,"cut_rock_m3":55.12,"cut_rr_m3":55.12,"cut_br_m3":0,"net_area_m2":-7.62,"fill_m3":135.99},{"station_id":"station_000001060000","chainage_m":1060,"net_volume_m3":-180.55,"cumulative_volume_m3":-17082.43,"cut_soil_m3":23.58,"cut_rock_m3":22.92,"cut_rr_m3":22.92,"cut_br_m3":0,"net_area_m2":-10.44,"fill_m3":228.47},{"station_id":"station_000001080000","chainage_m":1080,"net_volume_m3":-77.47,"cumulative_volume_m3":-17159.9,"cut_soil_m3":24.64,"cut_rock_m3":25.81,"cut_rr_m3":25.81,"cut_br_m3":0,"net_area_m2":2.69,"fill_m3":129.72},{"station_id":"station_000001080936","chainage_m":1080.94,"net_volume_m3":1.34,"cumulative_volume_m3":-17158.56,"cut_soil_m3":1.45,"cut_rock_m3":1.71,"cut_rr_m3":1.71,"cut_br_m3":0,"net_area_m2":0.17,"fill_m3":1.96}]},"592":{"basis":"compacted","conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"cut_natural_m3":{"soil":1906.46,"ripping_rock":2958.09,"blasting_rock":0},"cut_compacted_m3":5161.99,"fill_compacted_m3":20255.44,"final_cumulative_m3":-15093.45,"surplus_m3":0,"shortage_m3":15093.45,"points":[{"station_id":"station_000000000000","chainage_m":0,"net_volume_m3":0,"cumulative_volume_m3":0,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-0.17,"fill_m3":0},{"station_id":"station_000000020000","chainage_m":20,"net_volume_m3":-175.73,"cumulative_volume_m3":-175.73,"cut_soil_m3":20.28,"cut_rock_m3":24.12,"cut_rr_m3":24.12,"cut_br_m3":0,"net_area_m2":-17.41,"fill_m3":222.08},{"station_id":"station_000000040000","chainage_m":40,"net_volume_m3":-507.4,"cumulative_volume_m3":-683.13,"cut_soil_m3":29.55,"cut_rock_m3":28.33,"cut_rr_m3":28.33,"cut_br_m3":0,"net_area_m2":-33.33,"fill_m3":566.99},{"station_id":"station_000000060000","chainage_m":60,"net_volume_m3":-349.19,"cumulative_volume_m3":-1032.33,"cut_soil_m3":29.81,"cut_rock_m3":35.11,"cut_rr_m3":35.11,"cut_br_m3":0,"net_area_m2":-1.59,"fill_m3":416.93},{"station_id":"station_000000080000","chainage_m":80,"net_volume_m3":-225.46,"cumulative_volume_m3":-1257.79,"cut_soil_m3":35.37,"cut_rock_m3":58.86,"cut_rr_m3":58.86,"cut_br_m3":0,"net_area_m2":-20.96,"fill_m3":325.86},{"station_id":"station_000000100000","chainage_m":100,"net_volume_m3":-138.95,"cumulative_volume_m3":-1396.74,"cut_soil_m3":39.88,"cut_rock_m3":114.02,"cut_rr_m3":114.02,"cut_br_m3":0,"net_area_m2":7.07,"fill_m3":307.67},{"station_id":"station_000000120000","chainage_m":120,"net_volume_m3":-210.34,"cumulative_volume_m3":-1607.08,"cut_soil_m3":35.32,"cut_rock_m3":93.76,"cut_rr_m3":93.76,"cut_br_m3":0,"net_area_m2":-28.1,"fill_m3":351.36},{"station_id":"station_000000140000","chainage_m":140,"net_volume_m3":-281.15,"cumulative_volume_m3":-1888.23,"cut_soil_m3":28.19,"cut_rock_m3":22.2,"cut_rr_m3":22.2,"cut_br_m3":0,"net_area_m2":-0.02,"fill_m3":332.39},{"station_id":"station_000000160000","chainage_m":160,"net_volume_m3":-51.01,"cumulative_volume_m3":-1939.24,"cut_soil_m3":20.82,"cut_rock_m3":15.6,"cut_rr_m3":15.6,"cut_br_m3":0,"net_area_m2":-5.09,"fill_m3":87.92},{"station_id":"station_000000180000","chainage_m":180,"net_volume_m3":-81.44,"cumulative_volume_m3":-2020.68,"cut_soil_m3":20.3,"cut_rock_m3":13.08,"cut_rr_m3":13.08,"cut_br_m3":0,"net_area_m2":-3.06,"fill_m3":114.95},{"station_id":"station_000000200000","chainage_m":200,"net_volume_m3":-25.75,"cumulative_volume_m3":-2046.43,"cut_soil_m3":26,"cut_rock_m3":17.48,"cut_rr_m3":17.48,"cut_br_m3":0,"net_area_m2":0.48,"fill_m3":69.52},{"station_id":"station_000000220000","chainage_m":220,"net_volume_m3":-148.81,"cumulative_volume_m3":-2195.25,"cut_soil_m3":23.43,"cut_rock_m3":7.04,"cut_rr_m3":7.04,"cut_br_m3":0,"net_area_m2":-15.36,"fill_m3":178.1},{"station_id":"station_000000240000","chainage_m":240,"net_volume_m3":-322.38,"cumulative_volume_m3":-2517.62,"cut_soil_m3":23.97,"cut_rock_m3":13.48,"cut_rr_m3":13.48,"cut_br_m3":0,"net_area_m2":-16.87,"fill_m3":359.65},{"station_id":"station_000000260000","chainage_m":260,"net_volume_m3":-104.21,"cumulative_volume_m3":-2621.84,"cut_soil_m3":34.8,"cut_rock_m3":85.2,"cut_rr_m3":85.2,"cut_br_m3":0,"net_area_m2":6.45,"fill_m3":234.78},{"station_id":"station_000000280000","chainage_m":280,"net_volume_m3":-271.27,"cumulative_volume_m3":-2893.1,"cut_soil_m3":40.71,"cut_rock_m3":99.94,"cut_rr_m3":99.94,"cut_br_m3":0,"net_area_m2":-33.58,"fill_m3":424.34},{"station_id":"station_000000300000","chainage_m":300,"net_volume_m3":-616.51,"cumulative_volume_m3":-3509.61,"cut_soil_m3":37.89,"cut_rock_m3":74.89,"cut_rr_m3":74.89,"cut_br_m3":0,"net_area_m2":-28.07,"fill_m3":737.86},{"station_id":"station_000000320000","chainage_m":320,"net_volume_m3":-254.69,"cumulative_volume_m3":-3764.3,"cut_soil_m3":39.04,"cut_rock_m3":90.1,"cut_rr_m3":90.1,"cut_br_m3":0,"net_area_m2":2.6,"fill_m3":394.79},{"station_id":"station_000000340000","chainage_m":340,"net_volume_m3":11.08,"cumulative_volume_m3":-3753.21,"cut_soil_m3":38.72,"cut_rock_m3":69.8,"cut_rr_m3":69.8,"cut_br_m3":0,"net_area_m2":-1.49,"fill_m3":105.09},{"station_id":"station_000000360000","chainage_m":360,"net_volume_m3":-17.89,"cumulative_volume_m3":-3771.11,"cut_soil_m3":36.42,"cut_rock_m3":48.66,"cut_rr_m3":48.66,"cut_br_m3":0,"net_area_m2":-0.29,"fill_m3":107.37},{"station_id":"station_000000380000","chainage_m":380,"net_volume_m3":9.64,"cumulative_volume_m3":-3761.46,"cut_soil_m3":32.05,"cut_rock_m3":31.56,"cut_rr_m3":31.56,"cut_br_m3":0,"net_area_m2":1.26,"fill_m3":55.97},{"station_id":"station_000000389000","chainage_m":389,"net_volume_m3":2.21,"cumulative_volume_m3":-3759.25,"cut_soil_m3":10.85,"cut_rock_m3":7.65,"cut_rr_m3":7.65,"cut_br_m3":0,"net_area_m2":-0.77,"fill_m3":16.47},{"station_id":"station_000000400000","chainage_m":400,"net_volume_m3":-10.63,"cumulative_volume_m3":-3769.89,"cut_soil_m3":12.8,"cut_rock_m3":14.82,"cut_rr_m3":14.82,"cut_br_m3":0,"net_area_m2":-1.17,"fill_m3":39.42},{"station_id":"station_000000420000","chainage_m":420,"net_volume_m3":-83.39,"cumulative_volume_m3":-3853.28,"cut_soil_m3":29.08,"cut_rock_m3":39.15,"cut_rr_m3":39.15,"cut_br_m3":0,"net_area_m2":-7.17,"fill_m3":155.17},{"station_id":"station_000000440000","chainage_m":440,"net_volume_m3":-174.8,"cumulative_volume_m3":-4028.08,"cut_soil_m3":33.49,"cut_rock_m3":45.64,"cut_rr_m3":45.64,"cut_br_m3":0,"net_area_m2":-10.31,"fill_m3":258.11},{"station_id":"station_000000460000","chainage_m":460,"net_volume_m3":-52.07,"cumulative_volume_m3":-4080.15,"cut_soil_m3":35.92,"cut_rock_m3":66.92,"cut_rr_m3":66.92,"cut_br_m3":0,"net_area_m2":5.1,"fill_m3":162.37},{"station_id":"station_000000480000","chainage_m":480,"net_volume_m3":22.53,"cumulative_volume_m3":-4057.62,"cut_soil_m3":33.67,"cut_rock_m3":64.25,"cut_rr_m3":64.25,"cut_br_m3":0,"net_area_m2":-2.85,"fill_m3":82.63},{"station_id":"station_000000500000","chainage_m":500,"net_volume_m3":-30.66,"cumulative_volume_m3":-4088.28,"cut_soil_m3":32.19,"cut_rock_m3":42.89,"cut_rr_m3":42.89,"cut_br_m3":0,"net_area_m2":-0.22,"fill_m3":109.59},{"station_id":"station_000000520000","chainage_m":520,"net_volume_m3":-49.99,"cumulative_volume_m3":-4138.27,"cut_soil_m3":26.52,"cut_rock_m3":22.49,"cut_rr_m3":22.49,"cut_br_m3":0,"net_area_m2":-4.78,"fill_m3":100.05},{"station_id":"station_000000540000","chainage_m":540,"net_volume_m3":-24.8,"cumulative_volume_m3":-4163.07,"cut_soil_m3":26.84,"cut_rock_m3":18.4,"cut_rr_m3":18.4,"cut_br_m3":0,"net_area_m2":2.3,"fill_m3":70.4},{"station_id":"station_000000560000","chainage_m":560,"net_volume_m3":30.6,"cumulative_volume_m3":-4132.47,"cut_soil_m3":27.61,"cut_rock_m3":19.21,"cut_rr_m3":19.21,"cut_br_m3":0,"net_area_m2":0.76,"fill_m3":16.63},{"station_id":"station_000000580000","chainage_m":580,"net_volume_m3":6.8,"cumulative_volume_m3":-4125.67,"cut_soil_m3":25.47,"cut_rock_m3":13.96,"cut_rr_m3":13.96,"cut_br_m3":0,"net_area_m2":-0.08,"fill_m3":32.38},{"station_id":"station_000000600000","chainage_m":600,"net_volume_m3":-38.62,"cumulative_volume_m3":-4164.29,"cut_soil_m3":26.13,"cut_rock_m3":20.95,"cut_rr_m3":20.95,"cut_br_m3":0,"net_area_m2":-3.78,"fill_m3":86.54},{"station_id":"station_000000620000","chainage_m":620,"net_volume_m3":-5.38,"cumulative_volume_m3":-4169.67,"cut_soil_m3":30.35,"cut_rock_m3":63.51,"cut_rr_m3":63.51,"cut_br_m3":0,"net_area_m2":3.24,"fill_m3":106.69},{"station_id":"station_000000640000","chainage_m":640,"net_volume_m3":25.73,"cumulative_volume_m3":-4143.93,"cut_soil_m3":34.52,"cut_rock_m3":68.08,"cut_rr_m3":68.08,"cut_br_m3":0,"net_area_m2":-0.67,"fill_m3":84.65},{"station_id":"station_000000660000","chainage_m":660,"net_volume_m3":-21.63,"cumulative_volume_m3":-4165.57,"cut_soil_m3":32.09,"cut_rock_m3":38.59,"cut_rr_m3":38.59,"cut_br_m3":0,"net_area_m2":-1.49,"fill_m3":95.46},{"station_id":"station_000000680000","chainage_m":680,"net_volume_m3":0.21,"cumulative_volume_m3":-4165.36,"cut_soil_m3":30.97,"cut_rock_m3":40.04,"cut_rr_m3":40.04,"cut_br_m3":0,"net_area_m2":1.51,"fill_m3":74.32},{"station_id":"station_000000700000","chainage_m":700,"net_volume_m3":-41.16,"cumulative_volume_m3":-4206.52,"cut_soil_m3":30.04,"cut_rock_m3":33.54,"cut_rr_m3":33.54,"cut_br_m3":0,"net_area_m2":-5.63,"fill_m3":107.27},{"station_id":"station_000000720000","chainage_m":720,"net_volume_m3":-534.3,"cumulative_volume_m3":-4740.82,"cut_soil_m3":40.14,"cut_rock_m3":71.71,"cut_rr_m3":71.71,"cut_br_m3":0,"net_area_m2":-47.8,"fill_m3":653.97},{"station_id":"station_000000740000","chainage_m":740,"net_volume_m3":-1737.81,"cumulative_volume_m3":-6478.64,"cut_soil_m3":44.15,"cut_rock_m3":83.28,"cut_rr_m3":83.28,"cut_br_m3":0,"net_area_m2":-125.98,"fill_m3":1874.57},{"station_id":"station_000000760000","chainage_m":760,"net_volume_m3":-1329.34,"cumulative_volume_m3":-7807.98,"cut_soil_m3":41.92,"cut_rock_m3":77.5,"cut_rr_m3":77.5,"cut_br_m3":0,"net_area_m2":-6.95,"fill_m3":1457.36},{"station_id":"station_000000780000","chainage_m":780,"net_volume_m3":-346.18,"cumulative_volume_m3":-8154.16,"cut_soil_m3":45.09,"cut_rock_m3":100.2,"cut_rr_m3":100.2,"cut_br_m3":0,"net_area_m2":-27.66,"fill_m3":503.49},{"station_id":"station_000000800000","chainage_m":800,"net_volume_m3":-355.54,"cumulative_volume_m3":-8509.7,"cut_soil_m3":39.59,"cut_rock_m3":70.45,"cut_rr_m3":70.45,"cut_br_m3":0,"net_area_m2":-7.89,"fill_m3":473.24},{"station_id":"station_000000820000","chainage_m":820,"net_volume_m3":-152.56,"cumulative_volume_m3":-8662.26,"cut_soil_m3":31.69,"cut_rock_m3":47.91,"cut_rr_m3":47.91,"cut_br_m3":0,"net_area_m2":-7.37,"fill_m3":236.9},{"station_id":"station_000000840000","chainage_m":840,"net_volume_m3":-97.38,"cumulative_volume_m3":-8759.64,"cut_soil_m3":28.98,"cut_rock_m3":46.67,"cut_rr_m3":46.67,"cut_br_m3":0,"net_area_m2":-2.37,"fill_m3":177.84},{"station_id":"station_000000860000","chainage_m":860,"net_volume_m3":-17.18,"cumulative_volume_m3":-8776.82,"cut_soil_m3":28.62,"cut_rock_m3":30.05,"cut_rr_m3":30.05,"cut_br_m3":0,"net_area_m2":0.65,"fill_m3":77.94},{"station_id":"station_000000880000","chainage_m":880,"net_volume_m3":-515.88,"cumulative_volume_m3":-9292.7,"cut_soil_m3":24.85,"cut_rock_m3":14.07,"cut_rr_m3":14.07,"cut_br_m3":0,"net_area_m2":-52.24,"fill_m3":554.64},{"station_id":"station_000000900000","chainage_m":900,"net_volume_m3":-1445.46,"cumulative_volume_m3":-10738.16,"cut_soil_m3":31.01,"cut_rock_m3":49.51,"cut_rr_m3":49.51,"cut_br_m3":0,"net_area_m2":-92.3,"fill_m3":1531.05},{"station_id":"station_000000920000","chainage_m":920,"net_volume_m3":-884.55,"cumulative_volume_m3":-11622.71,"cut_soil_m3":37.19,"cut_rock_m3":73.52,"cut_rr_m3":73.52,"cut_br_m3":0,"net_area_m2":3.85,"fill_m3":1003.67},{"station_id":"station_000000940000","chainage_m":940,"net_volume_m3":53.7,"cumulative_volume_m3":-11569.01,"cut_soil_m3":32.03,"cut_rock_m3":43.48,"cut_rr_m3":43.48,"cut_br_m3":0,"net_area_m2":1.52,"fill_m3":25.78},{"station_id":"station_000000960000","chainage_m":960,"net_volume_m3":21.43,"cumulative_volume_m3":-11547.57,"cut_soil_m3":25.44,"cut_rock_m3":24.16,"cut_rr_m3":24.16,"cut_br_m3":0,"net_area_m2":0.62,"fill_m3":29.61},{"station_id":"station_000000980000","chainage_m":980,"net_volume_m3":-17.29,"cumulative_volume_m3":-11564.86,"cut_soil_m3":28.11,"cut_rock_m3":33.96,"cut_rr_m3":33.96,"cut_br_m3":0,"net_area_m2":-2.35,"fill_m3":82.15},{"station_id":"station_000001000000","chainage_m":1000,"net_volume_m3":-198.3,"cumulative_volume_m3":-11763.16,"cut_soil_m3":35.44,"cut_rock_m3":44.69,"cut_rr_m3":44.69,"cut_br_m3":0,"net_area_m2":-17.48,"fill_m3":282.27},{"station_id":"station_000001020000","chainage_m":1020,"net_volume_m3":-196.98,"cumulative_volume_m3":-11960.15,"cut_soil_m3":35.13,"cut_rock_m3":45.87,"cut_rr_m3":45.87,"cut_br_m3":0,"net_area_m2":-2.22,"fill_m3":282.04},{"station_id":"station_000001040000","chainage_m":1040,"net_volume_m3":-3.07,"cumulative_volume_m3":-11963.22,"cut_soil_m3":33.48,"cut_rock_m3":69.27,"cut_rr_m3":69.27,"cut_br_m3":0,"net_area_m2":1.91,"fill_m3":113.9},{"station_id":"station_000001060000","chainage_m":1060,"net_volume_m3":-544.21,"cumulative_volume_m3":-12507.43,"cut_soil_m3":31.95,"cut_rock_m3":68.41,"cut_rr_m3":68.41,"cut_br_m3":0,"net_area_m2":-56.33,"fill_m3":652.67},{"station_id":"station_000001080000","chainage_m":1080,"net_volume_m3":-543.61,"cumulative_volume_m3":-13051.03,"cut_soil_m3":32.99,"cut_rock_m3":55.87,"cut_rr_m3":55.87,"cut_br_m3":0,"net_area_m2":1.97,"fill_m3":638.38},{"station_id":"station_000001100000","chainage_m":1100,"net_volume_m3":-34.37,"cumulative_volume_m3":-13085.4,"cut_soil_m3":35.02,"cut_rock_m3":68.7,"cut_rr_m3":68.7,"cut_br_m3":0,"net_area_m2":-5.41,"fill_m3":145.92},{"station_id":"station_000001120000","chainage_m":1120,"net_volume_m3":-705.55,"cumulative_volume_m3":-13790.95,"cut_soil_m3":32.65,"cut_rock_m3":55.57,"cut_rr_m3":55.57,"cut_br_m3":0,"net_area_m2":-65.15,"fill_m3":799.67},{"station_id":"station_000001140000","chainage_m":1140,"net_volume_m3":-953.09,"cumulative_volume_m3":-14744.04,"cut_soil_m3":29.86,"cut_rock_m3":28.61,"cut_rr_m3":28.61,"cut_br_m3":0,"net_area_m2":-30.16,"fill_m3":1013.3},{"station_id":"station_000001160000","chainage_m":1160,"net_volume_m3":-264.65,"cumulative_volume_m3":-15008.69,"cut_soil_m3":39.22,"cut_rock_m3":93.83,"cut_rr_m3":93.83,"cut_br_m3":0,"net_area_m2":3.7,"fill_m3":409.25},{"station_id":"station_000001180000","chainage_m":1180,"net_volume_m3":-48.75,"cumulative_volume_m3":-15057.43,"cut_soil_m3":38.42,"cut_rock_m3":88.47,"cut_rr_m3":88.47,"cut_br_m3":0,"net_area_m2":-8.57,"fill_m3":186.39},{"station_id":"station_000001188804","chainage_m":1188.8,"net_volume_m3":-36.01,"cumulative_volume_m3":-15093.45,"cut_soil_m3":12.41,"cut_rock_m3":9.01,"cut_rr_m3":9.01,"cut_br_m3":0,"net_area_m2":0.39,"fill_m3":57.68}]},"761":{"basis":"compacted","conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"cut_natural_m3":{"soil":3387.67,"ripping_rock":29842.3,"blasting_rock":0},"cut_compacted_m3":37815.18,"fill_compacted_m3":36824.14,"final_cumulative_m3":991.04,"surplus_m3":991.04,"shortage_m3":0,"points":[{"station_id":"station_000000000000","chainage_m":0,"net_volume_m3":0,"cumulative_volume_m3":0,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-0.17,"fill_m3":0},{"station_id":"station_000000020000","chainage_m":20,"net_volume_m3":166.99,"cumulative_volume_m3":166.99,"cut_soil_m3":36.36,"cut_rock_m3":119.55,"cut_rr_m3":119.55,"cut_br_m3":0,"net_area_m2":16.87,"fill_m3":5.01},{"station_id":"station_000000040000","chainage_m":40,"net_volume_m3":567.31,"cumulative_volume_m3":734.29,"cut_soil_m3":77.42,"cut_rock_m3":427.69,"cut_rr_m3":427.69,"cut_br_m3":0,"net_area_m2":39.87,"fill_m3":0.63},{"station_id":"station_000000060000","chainage_m":60,"net_volume_m3":890.48,"cumulative_volume_m3":1624.78,"cut_soil_m3":92.83,"cut_rock_m3":692.65,"cut_rr_m3":692.65,"cut_br_m3":0,"net_area_m2":49.18,"fill_m3":0},{"station_id":"station_000000080000","chainage_m":80,"net_volume_m3":1359.94,"cumulative_volume_m3":2984.71,"cut_soil_m3":109.06,"cut_rock_m3":1083.07,"cut_rr_m3":1083.07,"cut_br_m3":0,"net_area_m2":86.81,"fill_m3":0},{"station_id":"station_000000100000","chainage_m":100,"net_volume_m3":1882.58,"cumulative_volume_m3":4867.29,"cut_soil_m3":125.68,"cut_rock_m3":1518.85,"cut_rr_m3":1518.85,"cut_br_m3":0,"net_area_m2":101.45,"fill_m3":0},{"station_id":"station_000000120000","chainage_m":120,"net_volume_m3":2358.88,"cumulative_volume_m3":7226.17,"cut_soil_m3":139.8,"cut_rock_m3":1916.79,"cut_rr_m3":1916.79,"cut_br_m3":0,"net_area_m2":134.44,"fill_m3":0},{"station_id":"station_000000140000","chainage_m":140,"net_volume_m3":2676.44,"cumulative_volume_m3":9902.62,"cut_soil_m3":147.69,"cut_rock_m3":2183.28,"cut_rr_m3":2183.28,"cut_br_m3":0,"net_area_m2":133.2,"fill_m3":0},{"station_id":"station_000000160000","chainage_m":160,"net_volume_m3":2623.86,"cumulative_volume_m3":12526.47,"cut_soil_m3":143.36,"cut_rock_m3":2141.49,"cut_rr_m3":2141.49,"cut_br_m3":0,"net_area_m2":129.18,"fill_m3":0},{"station_id":"station_000000180000","chainage_m":180,"net_volume_m3":2349.16,"cumulative_volume_m3":14875.64,"cut_soil_m3":136.06,"cut_rock_m3":1911.34,"cut_rr_m3":1911.34,"cut_br_m3":0,"net_area_m2":105.73,"fill_m3":0},{"station_id":"station_000000200000","chainage_m":200,"net_volume_m3":2056.4,"cumulative_volume_m3":16932.04,"cut_soil_m3":129.27,"cut_rock_m3":1665.29,"cut_rr_m3":1665.29,"cut_br_m3":0,"net_area_m2":99.91,"fill_m3":0},{"station_id":"station_000000220000","chainage_m":220,"net_volume_m3":1958.15,"cumulative_volume_m3":18890.19,"cut_soil_m3":127.64,"cut_rock_m3":1582.21,"cut_rr_m3":1582.21,"cut_br_m3":0,"net_area_m2":95.91,"fill_m3":0},{"station_id":"station_000000240000","chainage_m":240,"net_volume_m3":2300.65,"cumulative_volume_m3":21190.84,"cut_soil_m3":138.38,"cut_rock_m3":1867.9,"cut_rr_m3":1867.9,"cut_br_m3":0,"net_area_m2":134.16,"fill_m3":0},{"station_id":"station_000000260000","chainage_m":260,"net_volume_m3":3003.85,"cumulative_volume_m3":24194.69,"cut_soil_m3":155.49,"cut_rock_m3":2458.3,"cut_rr_m3":2458.3,"cut_br_m3":0,"net_area_m2":166.23,"fill_m3":0},{"station_id":"station_000000280000","chainage_m":280,"net_volume_m3":3125.84,"cumulative_volume_m3":27320.53,"cut_soil_m3":155.19,"cut_rock_m3":2563.23,"cut_rr_m3":2563.23,"cut_br_m3":0,"net_area_m2":146.35,"fill_m3":0},{"station_id":"station_000000300000","chainage_m":300,"net_volume_m3":1827.82,"cumulative_volume_m3":29148.35,"cut_soil_m3":117.21,"cut_rock_m3":1478.39,"cut_rr_m3":1478.39,"cut_br_m3":0,"net_area_m2":36.43,"fill_m3":0},{"station_id":"station_000000320000","chainage_m":320,"net_volume_m3":-459.7,"cumulative_volume_m3":28688.65,"cut_soil_m3":43.46,"cut_rock_m3":279.11,"cut_rr_m3":279.11,"cut_br_m3":0,"net_area_m2":-82.4,"fill_m3":823.97},{"station_id":"station_000000340000","chainage_m":340,"net_volume_m3":-5494.19,"cumulative_volume_m3":23194.46,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-467.02,"fill_m3":5494.19},{"station_id":"station_000000359000","chainage_m":359,"net_volume_m3":-8772.77,"cumulative_volume_m3":14421.69,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-456.43,"fill_m3":8772.77},{"station_id":"station_000000360000","chainage_m":360,"net_volume_m3":-465.49,"cumulative_volume_m3":13956.2,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-474.55,"fill_m3":465.49},{"station_id":"station_000000380000","chainage_m":380,"net_volume_m3":-6828.32,"cumulative_volume_m3":7127.88,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-208.28,"fill_m3":6828.32},{"station_id":"station_000000400000","chainage_m":400,"net_volume_m3":-2001.61,"cumulative_volume_m3":5126.26,"cut_soil_m3":29.27,"cut_rock_m3":47.07,"cut_rr_m3":47.07,"cut_br_m3":0,"net_area_m2":8.12,"fill_m3":2082.78},{"station_id":"station_000000404000","chainage_m":404,"net_volume_m3":28.32,"cumulative_volume_m3":5154.59,"cut_soil_m3":11.19,"cut_rock_m3":15.76,"cut_rr_m3":15.76,"cut_br_m3":0,"net_area_m2":6.04,"fill_m3":0.11},{"station_id":"station_000000420000","chainage_m":420,"net_volume_m3":892.03,"cumulative_volume_m3":6046.62,"cut_soil_m3":74.76,"cut_rock_m3":708.32,"cut_rr_m3":708.32,"cut_br_m3":0,"net_area_m2":105.46,"fill_m3":0.45},{"station_id":"station_000000440000","chainage_m":440,"net_volume_m3":1983.63,"cumulative_volume_m3":8030.25,"cut_soil_m3":131.4,"cut_rock_m3":1601.18,"cut_rr_m3":1601.18,"cut_br_m3":0,"net_area_m2":92.9,"fill_m3":0},{"station_id":"station_000000460000","chainage_m":460,"net_volume_m3":1096.24,"cumulative_volume_m3":9126.49,"cut_soil_m3":98.44,"cut_rock_m3":864.93,"cut_rr_m3":864.93,"cut_br_m3":0,"net_area_m2":16.72,"fill_m3":0},{"station_id":"station_000000480000","chainage_m":480,"net_volume_m3":197.99,"cumulative_volume_m3":9324.47,"cut_soil_m3":55.57,"cut_rock_m3":147.23,"cut_rr_m3":147.23,"cut_br_m3":0,"net_area_m2":3.08,"fill_m3":23.55},{"station_id":"station_000000500000","chainage_m":500,"net_volume_m3":46.02,"cumulative_volume_m3":9370.5,"cut_soil_m3":36.56,"cut_rock_m3":45.56,"cut_rr_m3":45.56,"cut_br_m3":0,"net_area_m2":1.53,"fill_m3":39.96},{"station_id":"station_000000520000","chainage_m":520,"net_volume_m3":18.68,"cumulative_volume_m3":9389.18,"cut_soil_m3":22.92,"cut_rock_m3":17.26,"cut_rr_m3":17.26,"cut_br_m3":0,"net_area_m2":0.34,"fill_m3":22.06},{"station_id":"station_000000540000","chainage_m":540,"net_volume_m3":-5.63,"cumulative_volume_m3":9383.54,"cut_soil_m3":18.97,"cut_rock_m3":8.98,"cut_rr_m3":8.98,"cut_br_m3":0,"net_area_m2":-0.91,"fill_m3":33.16},{"station_id":"station_000000560000","chainage_m":560,"net_volume_m3":-120.39,"cumulative_volume_m3":9263.15,"cut_soil_m3":16.53,"cut_rock_m3":9.99,"cut_rr_m3":9.99,"cut_br_m3":0,"net_area_m2":-11.13,"fill_m3":146.91},{"station_id":"station_000000580000","chainage_m":580,"net_volume_m3":-117.69,"cumulative_volume_m3":9145.46,"cut_soil_m3":23.82,"cut_rock_m3":21.13,"cut_rr_m3":21.13,"cut_br_m3":0,"net_area_m2":-0.64,"fill_m3":163.75},{"station_id":"station_000000600000","chainage_m":600,"net_volume_m3":-11.38,"cumulative_volume_m3":9134.08,"cut_soil_m3":36.1,"cut_rock_m3":42.89,"cut_rr_m3":42.89,"cut_br_m3":0,"net_area_m2":-0.5,"fill_m3":93.83},{"station_id":"station_000000620000","chainage_m":620,"net_volume_m3":101.73,"cumulative_volume_m3":9235.81,"cut_soil_m3":46.54,"cut_rock_m3":117.46,"cut_rr_m3":117.46,"cut_br_m3":0,"net_area_m2":10.67,"fill_m3":77},{"station_id":"station_000000640000","chainage_m":640,"net_volume_m3":144.66,"cumulative_volume_m3":9380.47,"cut_soil_m3":49.39,"cut_rock_m3":118.89,"cut_rr_m3":118.89,"cut_br_m3":0,"net_area_m2":3.79,"fill_m3":38.3},{"station_id":"station_000000660000","chainage_m":660,"net_volume_m3":-13.74,"cumulative_volume_m3":9366.73,"cut_soil_m3":39.14,"cut_rock_m3":64.38,"cut_rr_m3":64.38,"cut_br_m3":0,"net_area_m2":-5.17,"fill_m3":123.96},{"station_id":"station_000000680000","chainage_m":680,"net_volume_m3":-242.22,"cumulative_volume_m3":9124.51,"cut_soil_m3":37.85,"cut_rock_m3":86.65,"cut_rr_m3":86.65,"cut_br_m3":0,"net_area_m2":-19.06,"fill_m3":377.23},{"station_id":"station_000000700000","chainage_m":700,"net_volume_m3":-1984.13,"cumulative_volume_m3":7140.38,"cut_soil_m3":35.73,"cut_rock_m3":71.99,"cut_rr_m3":71.99,"cut_br_m3":0,"net_area_m2":-179.36,"fill_m3":2100.16},{"station_id":"station_000000720000","chainage_m":720,"net_volume_m3":-2032.1,"cumulative_volume_m3":5108.28,"cut_soil_m3":35.61,"cut_rock_m3":39.52,"cut_rr_m3":39.52,"cut_br_m3":0,"net_area_m2":-23.85,"fill_m3":2110.19},{"station_id":"station_000000740000","chainage_m":740,"net_volume_m3":-411.83,"cumulative_volume_m3":4696.45,"cut_soil_m3":40.82,"cut_rock_m3":48.27,"cut_rr_m3":48.27,"cut_br_m3":0,"net_area_m2":-17.33,"fill_m3":504.81},{"station_id":"station_000000760000","chainage_m":760,"net_volume_m3":-133.24,"cumulative_volume_m3":4563.22,"cut_soil_m3":42.53,"cut_rock_m3":90.73,"cut_rr_m3":90.73,"cut_br_m3":0,"net_area_m2":4.01,"fill_m3":277.22},{"station_id":"station_000000780000","chainage_m":780,"net_volume_m3":119.93,"cumulative_volume_m3":4683.15,"cut_soil_m3":47.96,"cut_rock_m3":130.88,"cut_rr_m3":130.88,"cut_br_m3":0,"net_area_m2":7.99,"fill_m3":75.71},{"station_id":"station_000000800000","chainage_m":800,"net_volume_m3":378.99,"cumulative_volume_m3":5062.14,"cut_soil_m3":71.22,"cut_rock_m3":295.38,"cut_rr_m3":295.38,"cut_br_m3":0,"net_area_m2":29.91,"fill_m3":29.23},{"station_id":"station_000000820000","chainage_m":820,"net_volume_m3":143.49,"cumulative_volume_m3":5205.63,"cut_soil_m3":64.52,"cut_rock_m3":269.63,"cut_rr_m3":269.63,"cut_br_m3":0,"net_area_m2":-15.56,"fill_m3":228.7},{"station_id":"station_000000840000","chainage_m":840,"net_volume_m3":-1116.45,"cumulative_volume_m3":4089.18,"cut_soil_m3":36.8,"cut_rock_m3":71.94,"cut_rr_m3":71.94,"cut_br_m3":0,"net_area_m2":-96.08,"fill_m3":1233.38},{"station_id":"station_000000860000","chainage_m":860,"net_volume_m3":-1020.55,"cumulative_volume_m3":3068.63,"cut_soil_m3":30.1,"cut_rock_m3":37.09,"cut_rr_m3":37.09,"cut_br_m3":0,"net_area_m2":-5.97,"fill_m3":1090.85},{"station_id":"station_000000880000","chainage_m":880,"net_volume_m3":-43.1,"cumulative_volume_m3":3025.53,"cut_soil_m3":32.88,"cut_rock_m3":44.51,"cut_rr_m3":44.51,"cut_br_m3":0,"net_area_m2":1.66,"fill_m3":124.55},{"station_id":"station_000000900000","chainage_m":900,"net_volume_m3":101.61,"cumulative_volume_m3":3127.14,"cut_soil_m3":44.33,"cut_rock_m3":97.93,"cut_rr_m3":97.93,"cut_br_m3":0,"net_area_m2":8.5,"fill_m3":52.38},{"station_id":"station_000000918000","chainage_m":918,"net_volume_m3":147.34,"cumulative_volume_m3":3274.48,"cut_soil_m3":43.78,"cut_rock_m3":125.18,"cut_rr_m3":125.18,"cut_br_m3":0,"net_area_m2":7.87,"fill_m3":37.9},{"station_id":"station_000000920000","chainage_m":920,"net_volume_m3":6.8,"cumulative_volume_m3":3281.28,"cut_soil_m3":4.42,"cut_rock_m3":9.88,"cut_rr_m3":9.88,"cut_br_m3":0,"net_area_m2":-1.07,"fill_m3":8.69},{"station_id":"station_000000940000","chainage_m":940,"net_volume_m3":-462.95,"cumulative_volume_m3":2818.33,"cut_soil_m3":20.53,"cut_rock_m3":25.36,"cut_rr_m3":25.36,"cut_br_m3":0,"net_area_m2":-45.22,"fill_m3":510.97},{"station_id":"station_000000960000","chainage_m":960,"net_volume_m3":-453.35,"cumulative_volume_m3":2364.98,"cut_soil_m3":6.8,"cut_rock_m3":2.58,"cut_rr_m3":2.58,"cut_br_m3":0,"net_area_m2":-0.11,"fill_m3":462.47},{"station_id":"station_000000980000","chainage_m":980,"net_volume_m3":181.94,"cumulative_volume_m3":2546.91,"cut_soil_m3":40.34,"cut_rock_m3":133.79,"cut_rr_m3":133.79,"cut_br_m3":0,"net_area_m2":18.31,"fill_m3":10.24},{"station_id":"station_000001000000","chainage_m":1000,"net_volume_m3":-375.69,"cumulative_volume_m3":2171.22,"cut_soil_m3":56.03,"cut_rock_m3":192.21,"cut_rr_m3":192.21,"cut_br_m3":0,"net_area_m2":-55.88,"fill_m3":650.04},{"station_id":"station_000001020000","chainage_m":1020,"net_volume_m3":-379.94,"cumulative_volume_m3":1791.28,"cut_soil_m3":57.81,"cut_rock_m3":187.18,"cut_rr_m3":187.18,"cut_br_m3":0,"net_area_m2":17.88,"fill_m3":650.04},{"station_id":"station_000001040000","chainage_m":1040,"net_volume_m3":-72.52,"cumulative_volume_m3":1718.76,"cut_soil_m3":46.45,"cut_rock_m3":139.03,"cut_rr_m3":139.03,"cut_br_m3":0,"net_area_m2":-25.13,"fill_m3":276.29},{"station_id":"station_000001060000","chainage_m":1060,"net_volume_m3":-539.63,"cumulative_volume_m3":1179.13,"cut_soil_m3":14.19,"cut_rock_m3":12.9,"cut_rr_m3":12.9,"cut_br_m3":0,"net_area_m2":-28.83,"fill_m3":567.42},{"station_id":"station_000001073274","chainage_m":1073.27,"net_volume_m3":-188.09,"cumulative_volume_m3":991.04,"cut_soil_m3":11.47,"cut_rock_m3":9.5,"cut_rr_m3":9.5,"cut_br_m3":0,"net_area_m2":0.49,"fill_m3":209.48}]},"762":{"basis":"compacted","conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"cut_natural_m3":{"soil":2660.16,"ripping_rock":16008.35,"blasting_rock":0},"cut_compacted_m3":21043.87,"fill_compacted_m3":13874.21,"final_cumulative_m3":7169.66,"surplus_m3":7169.66,"shortage_m3":0,"points":[{"station_id":"station_000000000000","chainage_m":0,"net_volume_m3":0,"cumulative_volume_m3":0,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-0.17,"fill_m3":0},{"station_id":"station_000000020000","chainage_m":20,"net_volume_m3":166.99,"cumulative_volume_m3":166.99,"cut_soil_m3":36.36,"cut_rock_m3":119.55,"cut_rr_m3":119.55,"cut_br_m3":0,"net_area_m2":16.87,"fill_m3":5.01},{"station_id":"station_000000040000","chainage_m":40,"net_volume_m3":567.31,"cumulative_volume_m3":734.29,"cut_soil_m3":77.42,"cut_rock_m3":427.69,"cut_rr_m3":427.69,"cut_br_m3":0,"net_area_m2":39.87,"fill_m3":0.63},{"station_id":"station_000000060000","chainage_m":60,"net_volume_m3":890.48,"cumulative_volume_m3":1624.78,"cut_soil_m3":92.83,"cut_rock_m3":692.65,"cut_rr_m3":692.65,"cut_br_m3":0,"net_area_m2":49.18,"fill_m3":0},{"station_id":"station_000000080000","chainage_m":80,"net_volume_m3":1359.94,"cumulative_volume_m3":2984.71,"cut_soil_m3":109.06,"cut_rock_m3":1083.07,"cut_rr_m3":1083.07,"cut_br_m3":0,"net_area_m2":86.81,"fill_m3":0},{"station_id":"station_000000100000","chainage_m":100,"net_volume_m3":1882.58,"cumulative_volume_m3":4867.29,"cut_soil_m3":125.68,"cut_rock_m3":1518.85,"cut_rr_m3":1518.85,"cut_br_m3":0,"net_area_m2":101.45,"fill_m3":0},{"station_id":"station_000000120000","chainage_m":120,"net_volume_m3":2358.88,"cumulative_volume_m3":7226.17,"cut_soil_m3":139.8,"cut_rock_m3":1916.79,"cut_rr_m3":1916.79,"cut_br_m3":0,"net_area_m2":134.44,"fill_m3":0},{"station_id":"station_000000140000","chainage_m":140,"net_volume_m3":2676.44,"cumulative_volume_m3":9902.62,"cut_soil_m3":147.69,"cut_rock_m3":2183.28,"cut_rr_m3":2183.28,"cut_br_m3":0,"net_area_m2":133.2,"fill_m3":0},{"station_id":"station_000000160000","chainage_m":160,"net_volume_m3":2623.66,"cumulative_volume_m3":12526.27,"cut_soil_m3":143.36,"cut_rock_m3":2141.32,"cut_rr_m3":2141.32,"cut_br_m3":0,"net_area_m2":129.16,"fill_m3":0},{"station_id":"station_000000180000","chainage_m":180,"net_volume_m3":2222.05,"cumulative_volume_m3":14748.32,"cut_soil_m3":132.24,"cut_rock_m3":1805.17,"cut_rr_m3":1805.17,"cut_br_m3":0,"net_area_m2":93.04,"fill_m3":0},{"station_id":"station_000000200000","chainage_m":200,"net_volume_m3":998.02,"cumulative_volume_m3":15746.34,"cut_soil_m3":83.49,"cut_rock_m3":799.13,"cut_rr_m3":799.13,"cut_br_m3":0,"net_area_m2":6.76,"fill_m3":8.12},{"station_id":"station_000000220000","chainage_m":220,"net_volume_m3":16.07,"cumulative_volume_m3":15762.41,"cut_soil_m3":36.61,"cut_rock_m3":64.46,"cut_rr_m3":64.46,"cut_br_m3":0,"net_area_m2":-5.15,"fill_m3":91.97},{"station_id":"station_000000240000","chainage_m":240,"net_volume_m3":-28.51,"cumulative_volume_m3":15733.9,"cut_soil_m3":37.16,"cut_rock_m3":69.1,"cut_rr_m3":69.1,"cut_br_m3":0,"net_area_m2":2.3,"fill_m3":142.44},{"station_id":"station_000000260000","chainage_m":260,"net_volume_m3":-120.84,"cumulative_volume_m3":15613.06,"cut_soil_m3":38.26,"cut_rock_m3":81.02,"cut_rr_m3":81.02,"cut_br_m3":0,"net_area_m2":-14.39,"fill_m3":249.67},{"station_id":"station_000000280000","chainage_m":280,"net_volume_m3":-405.51,"cumulative_volume_m3":15207.55,"cut_soil_m3":31.98,"cut_rock_m3":49.33,"cut_rr_m3":49.33,"cut_br_m3":0,"net_area_m2":-26.16,"fill_m3":491.76},{"station_id":"station_000000300000","chainage_m":300,"net_volume_m3":-241.16,"cumulative_volume_m3":14966.38,"cut_soil_m3":32.04,"cut_rock_m3":39.56,"cut_rr_m3":39.56,"cut_br_m3":0,"net_area_m2":2.05,"fill_m3":316.09},{"station_id":"station_000000320000","chainage_m":320,"net_volume_m3":58.08,"cumulative_volume_m3":15024.47,"cut_soil_m3":32.16,"cut_rock_m3":47.78,"cut_rr_m3":47.78,"cut_br_m3":0,"net_area_m2":3.76,"fill_m3":26.52},{"station_id":"station_000000340000","chainage_m":340,"net_volume_m3":16.18,"cumulative_volume_m3":15040.65,"cut_soil_m3":29.04,"cut_rock_m3":40.13,"cut_rr_m3":40.13,"cut_br_m3":0,"net_area_m2":-2.14,"fill_m3":56.7},{"station_id":"station_000000360000","chainage_m":360,"net_volume_m3":-3.49,"cumulative_volume_m3":15037.16,"cut_soil_m3":28.01,"cut_rock_m3":27.6,"cut_rr_m3":27.6,"cut_br_m3":0,"net_area_m2":1.79,"fill_m3":60.85},{"station_id":"station_000000361000","chainage_m":361,"net_volume_m3":1.08,"cumulative_volume_m3":15038.24,"cut_soil_m3":1.18,"cut_rock_m3":1.22,"cut_rr_m3":1.22,"cut_br_m3":0,"net_area_m2":0.37,"fill_m3":1.41},{"station_id":"station_000000380000","chainage_m":380,"net_volume_m3":-1726.45,"cumulative_volume_m3":13311.79,"cut_soil_m3":8.55,"cut_rock_m3":6.93,"cut_rr_m3":6.93,"cut_br_m3":0,"net_area_m2":-182.11,"fill_m3":1742.23},{"station_id":"station_000000400000","chainage_m":400,"net_volume_m3":-1816.24,"cumulative_volume_m3":11495.55,"cut_soil_m3":16.7,"cut_rock_m3":4.53,"cut_rr_m3":4.53,"cut_br_m3":0,"net_area_m2":0.48,"fill_m3":1836.55},{"station_id":"station_000000405000","chainage_m":405,"net_volume_m3":16.38,"cumulative_volume_m3":11511.93,"cut_soil_m3":10.99,"cut_rock_m3":9.7,"cut_rr_m3":9.7,"cut_br_m3":0,"net_area_m2":6.07,"fill_m3":4.8},{"station_id":"station_000000420000","chainage_m":420,"net_volume_m3":131.09,"cumulative_volume_m3":11643.02,"cut_soil_m3":39.19,"cut_rock_m3":93.71,"cut_rr_m3":93.71,"cut_br_m3":0,"net_area_m2":11.41,"fill_m3":13.36},{"station_id":"station_000000440000","chainage_m":440,"net_volume_m3":185.31,"cumulative_volume_m3":11828.32,"cut_soil_m3":46.87,"cut_rock_m3":146.73,"cut_rr_m3":146.73,"cut_br_m3":0,"net_area_m2":7.12,"fill_m3":27.81},{"station_id":"station_000000460000","chainage_m":460,"net_volume_m3":54.37,"cumulative_volume_m3":11882.69,"cut_soil_m3":35.62,"cut_rock_m3":63.7,"cut_rr_m3":63.7,"cut_br_m3":0,"net_area_m2":-1.68,"fill_m3":51.9},{"station_id":"station_000000480000","chainage_m":480,"net_volume_m3":-41.91,"cumulative_volume_m3":11840.78,"cut_soil_m3":28.91,"cut_rock_m3":19.86,"cut_rr_m3":19.86,"cut_br_m3":0,"net_area_m2":-2.51,"fill_m3":91.06},{"station_id":"station_000000500000","chainage_m":500,"net_volume_m3":-32.72,"cumulative_volume_m3":11808.07,"cut_soil_m3":29.15,"cut_rock_m3":26.44,"cut_rr_m3":26.44,"cut_br_m3":0,"net_area_m2":-0.77,"fill_m3":89.75},{"station_id":"station_000000520000","chainage_m":520,"net_volume_m3":11.93,"cumulative_volume_m3":11820,"cut_soil_m3":29.8,"cut_rock_m3":19.57,"cut_rr_m3":19.57,"cut_br_m3":0,"net_area_m2":1.96,"fill_m3":37.69},{"station_id":"station_000000540000","chainage_m":540,"net_volume_m3":19.24,"cumulative_volume_m3":11839.23,"cut_soil_m3":27.3,"cut_rock_m3":13.78,"cut_rr_m3":13.78,"cut_br_m3":0,"net_area_m2":-0.03,"fill_m3":21.39},{"station_id":"station_000000560000","chainage_m":560,"net_volume_m3":-106.86,"cumulative_volume_m3":11732.37,"cut_soil_m3":18.05,"cut_rock_m3":11.81,"cut_rr_m3":11.81,"cut_br_m3":0,"net_area_m2":-10.65,"fill_m3":136.86},{"station_id":"station_000000580000","chainage_m":580,"net_volume_m3":-119.35,"cumulative_volume_m3":11613.03,"cut_soil_m3":22.2,"cut_rock_m3":21.86,"cut_rr_m3":21.86,"cut_br_m3":0,"net_area_m2":-1.28,"fill_m3":164.8},{"station_id":"station_000000600000","chainage_m":600,"net_volume_m3":-32.9,"cumulative_volume_m3":11580.13,"cut_soil_m3":31.07,"cut_rock_m3":37.07,"cut_rr_m3":37.07,"cut_br_m3":0,"net_area_m2":-2.01,"fill_m3":104.05},{"station_id":"station_000000620000","chainage_m":620,"net_volume_m3":77.35,"cumulative_volume_m3":11657.47,"cut_soil_m3":43.21,"cut_rock_m3":101.65,"cut_rr_m3":101.65,"cut_br_m3":0,"net_area_m2":9.74,"fill_m3":79.96},{"station_id":"station_000000640000","chainage_m":640,"net_volume_m3":118.35,"cumulative_volume_m3":11775.82,"cut_soil_m3":47.13,"cut_rock_m3":106.25,"cut_rr_m3":106.25,"cut_br_m3":0,"net_area_m2":2.09,"fill_m3":47.84},{"station_id":"station_000000660000","chainage_m":660,"net_volume_m3":-64.12,"cumulative_volume_m3":11711.7,"cut_soil_m3":34.14,"cut_rock_m3":52.46,"cut_rr_m3":52.46,"cut_br_m3":0,"net_area_m2":-8.51,"fill_m3":155.95},{"station_id":"station_000000680000","chainage_m":680,"net_volume_m3":-106.18,"cumulative_volume_m3":11605.53,"cut_soil_m3":40.54,"cut_rock_m3":92.9,"cut_rr_m3":92.9,"cut_br_m3":0,"net_area_m2":-2.11,"fill_m3":250.89},{"station_id":"station_000000700000","chainage_m":700,"net_volume_m3":-64.8,"cumulative_volume_m3":11540.72,"cut_soil_m3":48.38,"cut_rock_m3":102.83,"cut_rr_m3":102.83,"cut_br_m3":0,"net_area_m2":-4.37,"fill_m3":228.15},{"station_id":"station_000000720000","chainage_m":720,"net_volume_m3":-277.94,"cumulative_volume_m3":11262.78,"cut_soil_m3":39.77,"cut_rock_m3":49.26,"cut_rr_m3":49.26,"cut_br_m3":0,"net_area_m2":-23.43,"fill_m3":371.13},{"station_id":"station_000000740000","chainage_m":740,"net_volume_m3":-506.68,"cumulative_volume_m3":10756.1,"cut_soil_m3":34.75,"cut_rock_m3":24.16,"cut_rr_m3":24.16,"cut_br_m3":0,"net_area_m2":-27.24,"fill_m3":566.11},{"station_id":"station_000000760000","chainage_m":760,"net_volume_m3":-284.39,"cumulative_volume_m3":10471.71,"cut_soil_m3":34.88,"cut_rock_m3":53.72,"cut_rr_m3":53.72,"cut_br_m3":0,"net_area_m2":-1.2,"fill_m3":378.36},{"station_id":"station_000000780000","chainage_m":780,"net_volume_m3":52.4,"cumulative_volume_m3":10524.11,"cut_soil_m3":43.68,"cut_rock_m3":109.54,"cut_rr_m3":109.54,"cut_br_m3":0,"net_area_m2":6.44,"fill_m3":114.52},{"station_id":"station_000000800000","chainage_m":800,"net_volume_m3":384.68,"cumulative_volume_m3":10908.8,"cut_soil_m3":71.33,"cut_rock_m3":310.21,"cut_rr_m3":310.21,"cut_br_m3":0,"net_area_m2":32.03,"fill_m3":40.9},{"station_id":"station_000000820000","chainage_m":820,"net_volume_m3":240.83,"cumulative_volume_m3":11149.63,"cut_soil_m3":68.39,"cut_rock_m3":300.08,"cut_rr_m3":300.08,"cut_br_m3":0,"net_area_m2":-7.95,"fill_m3":170.32},{"station_id":"station_000000840000","chainage_m":840,"net_volume_m3":-990.53,"cumulative_volume_m3":10159.1,"cut_soil_m3":41.56,"cut_rock_m3":90.13,"cut_rr_m3":90.13,"cut_br_m3":0,"net_area_m2":-91.1,"fill_m3":1132.94},{"station_id":"station_000000860000","chainage_m":860,"net_volume_m3":-937.11,"cumulative_volume_m3":9221.99,"cut_soil_m3":32.38,"cut_rock_m3":44.45,"cut_rr_m3":44.45,"cut_br_m3":0,"net_area_m2":-2.61,"fill_m3":1018.02},{"station_id":"station_000000880000","chainage_m":880,"net_volume_m3":-8.23,"cumulative_volume_m3":9213.76,"cut_soil_m3":33.69,"cut_rock_m3":46.83,"cut_rr_m3":46.83,"cut_br_m3":0,"net_area_m2":1.78,"fill_m3":93.1},{"station_id":"station_000000900000","chainage_m":900,"net_volume_m3":104.09,"cumulative_volume_m3":9317.85,"cut_soil_m3":44.41,"cut_rock_m3":99.21,"cut_rr_m3":99.21,"cut_br_m3":0,"net_area_m2":8.63,"fill_m3":51.47},{"station_id":"station_000000918000","chainage_m":918,"net_volume_m3":149.61,"cumulative_volume_m3":9467.46,"cut_soil_m3":43.81,"cut_rock_m3":126.95,"cut_rr_m3":126.95,"cut_br_m3":0,"net_area_m2":8,"fill_m3":37.71},{"station_id":"station_000000920000","chainage_m":920,"net_volume_m3":7.13,"cumulative_volume_m3":9474.58,"cut_soil_m3":4.5,"cut_rock_m3":10.03,"cut_rr_m3":10.03,"cut_br_m3":0,"net_area_m2":-0.87,"fill_m3":8.61},{"station_id":"station_000000940000","chainage_m":940,"net_volume_m3":-459.16,"cumulative_volume_m3":9015.42,"cut_soil_m3":21.34,"cut_rock_m3":25.79,"cut_rr_m3":25.79,"cut_br_m3":0,"net_area_m2":-45.04,"fill_m3":508.42},{"station_id":"station_000000960000","chainage_m":960,"net_volume_m3":-451.42,"cumulative_volume_m3":8563.99,"cut_soil_m3":6.85,"cut_rock_m3":2.62,"cut_rr_m3":2.62,"cut_br_m3":0,"net_area_m2":-0.1,"fill_m3":460.64},{"station_id":"station_000000980000","chainage_m":980,"net_volume_m3":182.55,"cumulative_volume_m3":8746.54,"cut_soil_m3":40.37,"cut_rock_m3":134.26,"cut_rr_m3":134.26,"cut_br_m3":0,"net_area_m2":18.35,"fill_m3":10.2},{"station_id":"station_000001000000","chainage_m":1000,"net_volume_m3":-380.78,"cumulative_volume_m3":8365.76,"cut_soil_m3":56,"cut_rock_m3":192.29,"cut_rr_m3":192.29,"cut_br_m3":0,"net_area_m2":-56.43,"fill_m3":655.19},{"station_id":"station_000001020000","chainage_m":1020,"net_volume_m3":-384.83,"cumulative_volume_m3":7980.94,"cut_soil_m3":57.81,"cut_rock_m3":187.41,"cut_rr_m3":187.41,"cut_br_m3":0,"net_area_m2":17.95,"fill_m3":655.19},{"station_id":"station_000001040000","chainage_m":1040,"net_volume_m3":-75.89,"cumulative_volume_m3":7905.05,"cut_soil_m3":46.67,"cut_rock_m3":139.56,"cut_rr_m3":139.56,"cut_br_m3":0,"net_area_m2":-25.54,"fill_m3":280.48},{"station_id":"station_000001060000","chainage_m":1060,"net_volume_m3":-545.55,"cumulative_volume_m3":7359.5,"cut_soil_m3":14.36,"cut_rock_m3":12.84,"cut_rr_m3":12.84,"cut_br_m3":0,"net_area_m2":-29.02,"fill_m3":573.43},{"station_id":"station_000001073309","chainage_m":1073.31,"net_volume_m3":-189.84,"cumulative_volume_m3":7169.66,"cut_soil_m3":11.47,"cut_rock_m3":9.52,"cut_rr_m3":9.52,"cut_br_m3":0,"net_area_m2":0.49,"fill_m3":211.25}]},"819":{"basis":"compacted","conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"cut_natural_m3":{"soil":1998.76,"ripping_rock":3780.29,"blasting_rock":0},"cut_compacted_m3":6202.92,"fill_compacted_m3":14651.75,"final_cumulative_m3":-8448.83,"surplus_m3":0,"shortage_m3":8448.83,"points":[{"station_id":"station_000000000000","chainage_m":0,"net_volume_m3":0,"cumulative_volume_m3":0,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-0.17,"fill_m3":0},{"station_id":"station_000000020000","chainage_m":20,"net_volume_m3":-60.3,"cumulative_volume_m3":-60.3,"cut_soil_m3":26.03,"cut_rock_m3":41.37,"cut_rr_m3":41.37,"cut_br_m3":0,"net_area_m2":-5.86,"fill_m3":131.92},{"station_id":"station_000000040000","chainage_m":40,"net_volume_m3":-0.51,"cumulative_volume_m3":-60.81,"cut_soil_m3":50.58,"cut_rock_m3":107.12,"cut_rr_m3":107.12,"cut_br_m3":0,"net_area_m2":5.81,"fill_m3":170.82},{"station_id":"station_000000060000","chainage_m":60,"net_volume_m3":14.63,"cumulative_volume_m3":-46.18,"cut_soil_m3":46.22,"cut_rock_m3":90.15,"cut_rr_m3":90.15,"cut_br_m3":0,"net_area_m2":-4.35,"fill_m3":131.99},{"station_id":"station_000000080000","chainage_m":80,"net_volume_m3":-272.62,"cumulative_volume_m3":-318.81,"cut_soil_m3":33.65,"cut_rock_m3":42.08,"cut_rr_m3":42.08,"cut_br_m3":0,"net_area_m2":-22.91,"fill_m3":351.94},{"station_id":"station_000000100000","chainage_m":100,"net_volume_m3":-411.23,"cumulative_volume_m3":-730.04,"cut_soil_m3":34.44,"cut_rock_m3":87.84,"cut_rr_m3":87.84,"cut_br_m3":0,"net_area_m2":-18.21,"fill_m3":544.56},{"station_id":"station_000000120000","chainage_m":120,"net_volume_m3":-174.28,"cumulative_volume_m3":-904.32,"cut_soil_m3":37.42,"cut_rock_m3":93.65,"cut_rr_m3":93.65,"cut_br_m3":0,"net_area_m2":0.78,"fill_m3":317.07},{"station_id":"station_000000140000","chainage_m":140,"net_volume_m3":-13.62,"cumulative_volume_m3":-917.94,"cut_soil_m3":31.22,"cut_rock_m3":32.14,"cut_rr_m3":32.14,"cut_br_m3":0,"net_area_m2":-2.14,"fill_m3":79.15},{"station_id":"station_000000160000","chainage_m":160,"net_volume_m3":33.52,"cumulative_volume_m3":-884.42,"cut_soil_m3":37.16,"cut_rock_m3":45.39,"cut_rr_m3":45.39,"cut_br_m3":0,"net_area_m2":5.5,"fill_m3":52.8},{"station_id":"station_000000180000","chainage_m":180,"net_volume_m3":106.99,"cumulative_volume_m3":-777.42,"cut_soil_m3":46.41,"cut_rock_m3":65.44,"cut_rr_m3":65.44,"cut_br_m3":0,"net_area_m2":5.2,"fill_m3":11.01},{"station_id":"station_000000200000","chainage_m":200,"net_volume_m3":70.86,"cumulative_volume_m3":-706.57,"cut_soil_m3":37.66,"cut_rock_m3":38.17,"cut_rr_m3":38.17,"cut_br_m3":0,"net_area_m2":1.88,"fill_m3":7.51},{"station_id":"station_000000220000","chainage_m":220,"net_volume_m3":69.26,"cumulative_volume_m3":-637.31,"cut_soil_m3":34.52,"cut_rock_m3":51.93,"cut_rr_m3":51.93,"cut_br_m3":0,"net_area_m2":5.04,"fill_m3":22.32},{"station_id":"station_000000240000","chainage_m":240,"net_volume_m3":13.77,"cumulative_volume_m3":-623.54,"cut_soil_m3":32.14,"cut_rock_m3":59.59,"cut_rr_m3":59.59,"cut_br_m3":0,"net_area_m2":-3.66,"fill_m3":84.58},{"station_id":"station_000000260000","chainage_m":260,"net_volume_m3":-270.91,"cumulative_volume_m3":-894.45,"cut_soil_m3":25.4,"cut_rock_m3":26.52,"cut_rr_m3":26.52,"cut_br_m3":0,"net_area_m2":-23.43,"fill_m3":324.66},{"station_id":"station_000000280000","chainage_m":280,"net_volume_m3":-357.98,"cumulative_volume_m3":-1252.43,"cut_soil_m3":26.86,"cut_rock_m3":22.43,"cut_rr_m3":22.43,"cut_br_m3":0,"net_area_m2":-12.37,"fill_m3":408.29},{"station_id":"station_000000300000","chainage_m":300,"net_volume_m3":-133.52,"cumulative_volume_m3":-1385.95,"cut_soil_m3":27.32,"cut_rock_m3":22.07,"cut_rr_m3":22.07,"cut_br_m3":0,"net_area_m2":-0.98,"fill_m3":183.82},{"station_id":"station_000000320000","chainage_m":320,"net_volume_m3":64.57,"cumulative_volume_m3":-1321.38,"cut_soil_m3":33.16,"cut_rock_m3":57.32,"cut_rr_m3":57.32,"cut_br_m3":0,"net_area_m2":7.44,"fill_m3":32.05},{"station_id":"station_000000340000","chainage_m":340,"net_volume_m3":61.54,"cumulative_volume_m3":-1259.85,"cut_soil_m3":29.1,"cut_rock_m3":51.57,"cut_rr_m3":51.57,"cut_br_m3":0,"net_area_m2":-1.28,"fill_m3":24.73},{"station_id":"station_000000360000","chainage_m":360,"net_volume_m3":-90.31,"cumulative_volume_m3":-1350.16,"cut_soil_m3":22.02,"cut_rock_m3":7.27,"cut_rr_m3":7.27,"cut_br_m3":0,"net_area_m2":-7.75,"fill_m3":118.6},{"station_id":"station_000000374000","chainage_m":374,"net_volume_m3":-59.31,"cumulative_volume_m3":-1409.47,"cut_soil_m3":14.68,"cut_rock_m3":6.23,"cut_rr_m3":6.23,"cut_br_m3":0,"net_area_m2":-0.73,"fill_m3":79.79},{"station_id":"station_000000380000","chainage_m":380,"net_volume_m3":-0.44,"cumulative_volume_m3":-1409.92,"cut_soil_m3":6.88,"cut_rock_m3":4.5,"cut_rr_m3":4.5,"cut_br_m3":0,"net_area_m2":0.58,"fill_m3":11.88},{"station_id":"station_000000385000","chainage_m":385,"net_volume_m3":-0.42,"cumulative_volume_m3":-1410.34,"cut_soil_m3":5.82,"cut_rock_m3":4.55,"cut_rr_m3":4.55,"cut_br_m3":0,"net_area_m2":-0.75,"fill_m3":10.96},{"station_id":"station_000000400000","chainage_m":400,"net_volume_m3":-23.08,"cumulative_volume_m3":-1433.41,"cut_soil_m3":14.13,"cut_rock_m3":6.84,"cut_rr_m3":6.84,"cut_br_m3":0,"net_area_m2":-2.33,"fill_m3":43.76},{"station_id":"station_000000420000","chainage_m":420,"net_volume_m3":-29.13,"cumulative_volume_m3":-1462.55,"cut_soil_m3":23.79,"cut_rock_m3":9.13,"cut_rr_m3":9.13,"cut_br_m3":0,"net_area_m2":-0.58,"fill_m3":61.18},{"station_id":"station_000000440000","chainage_m":440,"net_volume_m3":-42.33,"cumulative_volume_m3":-1504.88,"cut_soil_m3":22.81,"cut_rock_m3":10.49,"cut_rr_m3":10.49,"cut_br_m3":0,"net_area_m2":-3.65,"fill_m3":75.08},{"station_id":"station_000000460000","chainage_m":460,"net_volume_m3":-86.93,"cumulative_volume_m3":-1591.81,"cut_soil_m3":27.44,"cut_rock_m3":22.84,"cut_rr_m3":22.84,"cut_br_m3":0,"net_area_m2":-5.04,"fill_m3":138.24},{"station_id":"station_000000480000","chainage_m":480,"net_volume_m3":-13.78,"cumulative_volume_m3":-1605.59,"cut_soil_m3":39.45,"cut_rock_m3":60.04,"cut_rr_m3":60.04,"cut_br_m3":0,"net_area_m2":3.66,"fill_m3":119.24},{"station_id":"station_000000500000","chainage_m":500,"net_volume_m3":36.8,"cumulative_volume_m3":-1568.79,"cut_soil_m3":37.16,"cut_rock_m3":63.14,"cut_rr_m3":63.14,"cut_br_m3":0,"net_area_m2":0.02,"fill_m3":70.19},{"station_id":"station_000000520000","chainage_m":520,"net_volume_m3":-37.78,"cumulative_volume_m3":-1606.58,"cut_soil_m3":28.83,"cut_rock_m3":34.26,"cut_rr_m3":34.26,"cut_br_m3":0,"net_area_m2":-3.79,"fill_m3":103.65},{"station_id":"station_000000540000","chainage_m":540,"net_volume_m3":-60.16,"cumulative_volume_m3":-1666.74,"cut_soil_m3":26.42,"cut_rock_m3":19.42,"cut_rr_m3":19.42,"cut_br_m3":0,"net_area_m2":-2.22,"fill_m3":106.56},{"station_id":"station_000000560000","chainage_m":560,"net_volume_m3":-14.04,"cumulative_volume_m3":-1680.78,"cut_soil_m3":22.62,"cut_rock_m3":11.94,"cut_rr_m3":11.94,"cut_br_m3":0,"net_area_m2":0.82,"fill_m3":48.32},{"station_id":"station_000000580000","chainage_m":580,"net_volume_m3":-10.97,"cumulative_volume_m3":-1691.75,"cut_soil_m3":22.66,"cut_rock_m3":7.9,"cut_rr_m3":7.9,"cut_br_m3":0,"net_area_m2":-1.91,"fill_m3":40.57},{"station_id":"station_000000599000","chainage_m":599,"net_volume_m3":-49.1,"cumulative_volume_m3":-1740.85,"cut_soil_m3":25.86,"cut_rock_m3":13.76,"cut_rr_m3":13.76,"cut_br_m3":0,"net_area_m2":-3.25,"fill_m3":88.4},{"station_id":"station_000000600000","chainage_m":600,"net_volume_m3":-3.16,"cumulative_volume_m3":-1744.01,"cut_soil_m3":1.26,"cut_rock_m3":0.93,"cut_rr_m3":0.93,"cut_br_m3":0,"net_area_m2":-3.06,"fill_m3":5.38},{"station_id":"station_000000620000","chainage_m":620,"net_volume_m3":-0.68,"cumulative_volume_m3":-1744.69,"cut_soil_m3":31.17,"cut_rock_m3":36.22,"cut_rr_m3":36.22,"cut_br_m3":0,"net_area_m2":2.99,"fill_m3":70.93},{"station_id":"station_000000640000","chainage_m":640,"net_volume_m3":44.19,"cumulative_volume_m3":-1700.5,"cut_soil_m3":41.38,"cut_rock_m3":49.93,"cut_rr_m3":49.93,"cut_br_m3":0,"net_area_m2":1.42,"fill_m3":51.22},{"station_id":"station_000000656000","chainage_m":656,"net_volume_m3":85.57,"cumulative_volume_m3":-1614.94,"cut_soil_m3":38.63,"cut_rock_m3":87.68,"cut_rr_m3":87.68,"cut_br_m3":0,"net_area_m2":9.27,"fill_m3":51.35},{"station_id":"station_000000660000","chainage_m":660,"net_volume_m3":30.82,"cumulative_volume_m3":-1584.12,"cut_soil_m3":10.76,"cut_rock_m3":29,"cut_rr_m3":29,"cut_br_m3":0,"net_area_m2":6.14,"fill_m3":12.65},{"station_id":"station_000000680000","chainage_m":680,"net_volume_m3":97.7,"cumulative_volume_m3":-1486.42,"cut_soil_m3":51.21,"cut_rock_m3":80.66,"cut_rr_m3":80.66,"cut_br_m3":0,"net_area_m2":3.63,"fill_m3":42.36},{"station_id":"station_000000700000","chainage_m":700,"net_volume_m3":261.55,"cumulative_volume_m3":-1224.87,"cut_soil_m3":63.4,"cut_rock_m3":185.83,"cut_rr_m3":185.83,"cut_br_m3":0,"net_area_m2":22.52,"fill_m3":12.01},{"station_id":"station_000000720000","chainage_m":720,"net_volume_m3":-133.08,"cumulative_volume_m3":-1357.96,"cut_soil_m3":66.39,"cut_rock_m3":233.98,"cut_rr_m3":233.98,"cut_br_m3":0,"net_area_m2":-35.83,"fill_m3":465.42},{"station_id":"station_000000740000","chainage_m":740,"net_volume_m3":-1353.12,"cumulative_volume_m3":-2711.07,"cut_soil_m3":47.98,"cut_rock_m3":129.51,"cut_rr_m3":129.51,"cut_br_m3":0,"net_area_m2":-99.48,"fill_m3":1547.18},{"station_id":"station_000000759000","chainage_m":759,"net_volume_m3":-1129.36,"cumulative_volume_m3":-3840.43,"cut_soil_m3":38.08,"cut_rock_m3":88.25,"cut_rr_m3":88.25,"cut_br_m3":0,"net_area_m2":-19.4,"fill_m3":1266.44},{"station_id":"station_000000760000","chainage_m":760,"net_volume_m3":-13.94,"cumulative_volume_m3":-3854.37,"cut_soil_m3":2.22,"cut_rock_m3":4.34,"cut_rr_m3":4.34,"cut_br_m3":0,"net_area_m2":-8.47,"fill_m3":20.99},{"station_id":"station_000000780000","chainage_m":780,"net_volume_m3":-228.3,"cumulative_volume_m3":-4082.67,"cut_soil_m3":45.88,"cut_rock_m3":97.18,"cut_rr_m3":97.18,"cut_br_m3":0,"net_area_m2":-14.36,"fill_m3":382.8},{"station_id":"station_000000800000","chainage_m":800,"net_volume_m3":-298.42,"cumulative_volume_m3":-4381.09,"cut_soil_m3":35,"cut_rock_m3":57.55,"cut_rr_m3":57.55,"cut_br_m3":0,"net_area_m2":-15.48,"fill_m3":396.96},{"station_id":"station_000000820000","chainage_m":820,"net_volume_m3":-229.16,"cumulative_volume_m3":-4610.25,"cut_soil_m3":34.03,"cut_rock_m3":49.96,"cut_rr_m3":49.96,"cut_br_m3":0,"net_area_m2":-7.43,"fill_m3":317.99},{"station_id":"station_000000840000","chainage_m":840,"net_volume_m3":31.24,"cumulative_volume_m3":-4579.01,"cut_soil_m3":54.69,"cut_rock_m3":151.94,"cut_rr_m3":151.94,"cut_br_m3":0,"net_area_m2":10.55,"fill_m3":194.99},{"station_id":"station_000000860000","chainage_m":860,"net_volume_m3":-37.03,"cumulative_volume_m3":-4616.04,"cut_soil_m3":55.04,"cut_rock_m3":169.02,"cut_rr_m3":169.02,"cut_br_m3":0,"net_area_m2":-14.26,"fill_m3":283.48},{"station_id":"station_000000880000","chainage_m":880,"net_volume_m3":-905.57,"cumulative_volume_m3":-5521.61,"cut_soil_m3":39.08,"cut_rock_m3":91.48,"cut_rr_m3":91.48,"cut_br_m3":0,"net_area_m2":-76.3,"fill_m3":1047.31},{"station_id":"station_000000900000","chainage_m":900,"net_volume_m3":-735.51,"cumulative_volume_m3":-6257.12,"cut_soil_m3":36.13,"cut_rock_m3":63.33,"cut_rr_m3":63.33,"cut_br_m3":0,"net_area_m2":2.75,"fill_m3":841.81},{"station_id":"station_000000920000","chainage_m":920,"net_volume_m3":7.14,"cumulative_volume_m3":-6249.98,"cut_soil_m3":33.28,"cut_rock_m3":47.88,"cut_rr_m3":47.88,"cut_br_m3":0,"net_area_m2":-2.03,"fill_m3":78.59},{"station_id":"station_000000940000","chainage_m":940,"net_volume_m3":21.16,"cumulative_volume_m3":-6228.81,"cut_soil_m3":37.53,"cut_rock_m3":76.87,"cut_rr_m3":76.87,"cut_br_m3":0,"net_area_m2":4.15,"fill_m3":102.16},{"station_id":"station_000000958000","chainage_m":958,"net_volume_m3":95.12,"cumulative_volume_m3":-6133.69,"cut_soil_m3":40.64,"cut_rock_m3":108.96,"cut_rr_m3":108.96,"cut_br_m3":0,"net_area_m2":6.42,"fill_m3":68.4},{"station_id":"station_000000960000","chainage_m":960,"net_volume_m3":5.04,"cumulative_volume_m3":-6128.65,"cut_soil_m3":4.55,"cut_rock_m3":8.99,"cut_rr_m3":8.99,"cut_br_m3":0,"net_area_m2":-1.38,"fill_m3":9.53},{"station_id":"station_000000980000","chainage_m":980,"net_volume_m3":-444.5,"cumulative_volume_m3":-6573.15,"cut_soil_m3":21.83,"cut_rock_m3":23.79,"cut_rr_m3":23.79,"cut_br_m3":0,"net_area_m2":-43.07,"fill_m3":491.86},{"station_id":"station_000001000000","chainage_m":1000,"net_volume_m3":-427.95,"cumulative_volume_m3":-7001.1,"cut_soil_m3":8.35,"cut_rock_m3":4.05,"cut_rr_m3":4.05,"cut_br_m3":0,"net_area_m2":0.27,"fill_m3":440.19},{"station_id":"station_000001020000","chainage_m":1020,"net_volume_m3":190.96,"cumulative_volume_m3":-6810.14,"cut_soil_m3":42.36,"cut_rock_m3":139.34,"cut_rr_m3":139.34,"cut_br_m3":0,"net_area_m2":18.82,"fill_m3":9.5},{"station_id":"station_000001040000","chainage_m":1040,"net_volume_m3":-370.1,"cumulative_volume_m3":-7180.24,"cut_soil_m3":56.57,"cut_rock_m3":197.17,"cut_rr_m3":197.17,"cut_br_m3":0,"net_area_m2":-55.83,"fill_m3":650.71},{"station_id":"station_000001060000","chainage_m":1060,"net_volume_m3":-380.33,"cumulative_volume_m3":-7560.57,"cut_soil_m3":57.86,"cut_rock_m3":187.39,"cut_rr_m3":187.39,"cut_br_m3":0,"net_area_m2":17.8,"fill_m3":650.71},{"station_id":"station_000001080000","chainage_m":1080,"net_volume_m3":-121.08,"cumulative_volume_m3":-7681.65,"cut_soil_m3":47.05,"cut_rock_m3":138.92,"cut_rr_m3":138.92,"cut_br_m3":0,"net_area_m2":-29.91,"fill_m3":325.26},{"station_id":"station_000001100000","chainage_m":1100,"net_volume_m3":-582.13,"cumulative_volume_m3":-8263.78,"cut_soil_m3":14.96,"cut_rock_m3":13.52,"cut_rr_m3":13.52,"cut_br_m3":0,"net_area_m2":-28.31,"fill_m3":611.35},{"station_id":"station_000001113304","chainage_m":1113.3,"net_volume_m3":-185.06,"cumulative_volume_m3":-8448.83,"cut_soil_m3":11.6,"cut_rock_m3":9.54,"cut_rr_m3":9.54,"cut_br_m3":0,"net_area_m2":0.49,"fill_m3":206.61}]},"826":{"basis":"compacted","conversion":{"soil":{"loose":1.25,"compacted":0.9},"ripping_rock":{"loose":1.35,"compacted":1.1649999999999998},"blasting_rock":{"loose":1.6,"compacted":1.1649999999999998}},"cut_natural_m3":{"soil":2427.23,"ripping_rock":7048.19,"blasting_rock":0},"cut_compacted_m3":10395.65,"fill_compacted_m3":12078.35,"final_cumulative_m3":-1682.7,"surplus_m3":0,"shortage_m3":1682.7,"points":[{"station_id":"station_000000000000","chainage_m":0,"net_volume_m3":0,"cumulative_volume_m3":0,"cut_soil_m3":0,"cut_rock_m3":0,"cut_rr_m3":0,"cut_br_m3":0,"net_area_m2":-0.17,"fill_m3":0},{"station_id":"station_000000020000","chainage_m":20,"net_volume_m3":-60.35,"cumulative_volume_m3":-60.35,"cut_soil_m3":26.03,"cut_rock_m3":41.36,"cut_rr_m3":41.36,"cut_br_m3":0,"net_area_m2":-5.87,"fill_m3":131.96},{"station_id":"station_000000040000","chainage_m":40,"net_volume_m3":-50.6,"cumulative_volume_m3":-110.95,"cut_soil_m3":47.23,"cut_rock_m3":97.8,"cut_rr_m3":97.8,"cut_br_m3":0,"net_area_m2":0.81,"fill_m3":207.04},{"station_id":"station_000000060000","chainage_m":60,"net_volume_m3":-56.11,"cumulative_volume_m3":-167.06,"cut_soil_m3":39.02,"cut_rock_m3":75.86,"cut_rr_m3":75.86,"cut_br_m3":0,"net_area_m2":-6.42,"fill_m3":179.61},{"station_id":"station_000000080000","chainage_m":80,"net_volume_m3":31.24,"cumulative_volume_m3":-135.82,"cut_soil_m3":44.38,"cut_rock_m3":78.5,"cut_rr_m3":78.5,"cut_br_m3":0,"net_area_m2":9.54,"fill_m3":100.15},{"station_id":"station_000000100000","chainage_m":100,"net_volume_m3":-18.79,"cumulative_volume_m3":-154.61,"cut_soil_m3":44.58,"cut_rock_m3":101.25,"cut_rr_m3":101.25,"cut_br_m3":0,"net_area_m2":-11.42,"fill_m3":176.87},{"station_id":"station_000000120000","chainage_m":120,"net_volume_m3":-28.28,"cumulative_volume_m3":-182.89,"cut_soil_m3":38.85,"cut_rock_m3":127.24,"cut_rr_m3":127.24,"cut_br_m3":0,"net_area_m2":8.6,"fill_m3":211.47},{"station_id":"station_000000140000","chainage_m":140,"net_volume_m3":195.42,"cumulative_volume_m3":12.54,"cut_soil_m3":53.39,"cut_rock_m3":156.2,"cut_rr_m3":156.2,"cut_br_m3":0,"net_area_m2":10.95,"fill_m3":34.6},{"station_id":"station_000000160000","chainage_m":160,"net_volume_m3":215.46,"cumulative_volume_m3":228,"cut_soil_m3":63.03,"cut_rock_m3":136.26,"cut_rr_m3":136.26,"cut_br_m3":0,"net_area_m2":10.6,"fill_m3":0},{"station_id":"station_000000180000","chainage_m":180,"net_volume_m3":121.19,"cumulative_volume_m3":349.19,"cut_soil_m3":51.08,"cut_rock_m3":86.14,"cut_rr_m3":86.14,"cut_br_m3":0,"net_area_m2":1.52,"fill_m3":25.13},{"station_id":"station_000000200000","chainage_m":200,"net_volume_m3":29.91,"cumulative_volume_m3":379.1,"cut_soil_m3":39.02,"cut_rock_m3":34.26,"cut_rr_m3":34.26,"cut_br_m3":0,"net_area_m2":1.47,"fill_m3":45.12},{"station_id":"station_000000220000","chainage_m":220,"net_volume_m3":8.21,"cumulative_volume_m3":387.31,"cut_soil_m3":36.92,"cut_rock_m3":41.58,"cut_rr_m3":41.58,"cut_br_m3":0,"net_area_m2":-0.65,"fill_m3":73.47},{"station_id":"station_000000240000","chainage_m":240,"net_volume_m3":155.23,"cumulative_volume_m3":542.54,"cut_soil_m3":44.43,"cut_rock_m3":147.83,"cut_rr_m3":147.83,"cut_br_m3":0,"net_area_m2":16.17,"fill_m3":56.97},{"station_id":"station_000000260000","chainage_m":260,"net_volume_m3":50.12,"cumulative_volume_m3":592.66,"cut_soil_m3":45.69,"cut_rock_m3":138.79,"cut_rr_m3":138.79,"cut_br_m3":0,"net_area_m2":-11.16,"fill_m3":152.69},{"station_id":"station_000000280000","chainage_m":280,"net_volume_m3":-446.26,"cumulative_volume_m3":146.4,"cut_soil_m3":33.83,"cut_rock_m3":50.3,"cut_rr_m3":50.3,"cut_br_m3":0,"net_area_m2":-33.46,"fill_m3":535.31},{"station_id":"station_000000300000","chainage_m":300,"net_volume_m3":-513.37,"cumulative_volume_m3":-366.97,"cut_soil_m3":31.33,"cut_rock_m3":45.09,"cut_rr_m3":45.09,"cut_br_m3":0,"net_area_m2":-17.87,"fill_m3":594.11},{"station_id":"station_000000320000","chainage_m":320,"net_volume_m3":-623.84,"cumulative_volume_m3":-990.81,"cut_soil_m3":30.26,"cut_rock_m3":44.9,"cut_rr_m3":44.9,"cut_br_m3":0,"net_area_m2":-44.51,"fill_m3":703.38},{"station_id":"station_000000340000","chainage_m":340,"net_volume_m3":-478.82,"cumulative_volume_m3":-1469.64,"cut_soil_m3":32.42,"cut_rock_m3":52.54,"cut_rr_m3":52.54,"cut_br_m3":0,"net_area_m2":-3.37,"fill_m3":569.22},{"station_id":"station_000000360000","chainage_m":360,"net_volume_m3":-63.57,"cumulative_volume_m3":-1533.2,"cut_soil_m3":31.57,"cut_rock_m3":34.29,"cut_rr_m3":34.29,"cut_br_m3":0,"net_area_m2":-2.99,"fill_m3":131.92},{"station_id":"station_000000380000","chainage_m":380,"net_volume_m3":-32.39,"cumulative_volume_m3":-1565.59,"cut_soil_m3":28.74,"cut_rock_m3":24.33,"cut_rr_m3":24.33,"cut_br_m3":0,"net_area_m2":-0.25,"fill_m3":86.59},{"station_id":"station_000000385000","chainage_m":385,"net_volume_m3":-4.04,"cumulative_volume_m3":-1569.63,"cut_soil_m3":4.03,"cut_rock_m3":2.94,"cut_rr_m3":2.94,"cut_br_m3":0,"net_area_m2":-1.36,"fill_m3":11.1},{"station_id":"station_000000400000","chainage_m":400,"net_volume_m3":-5.16,"cumulative_volume_m3":-1574.79,"cut_soil_m3":12.56,"cut_rock_m3":11.44,"cut_rr_m3":11.44,"cut_br_m3":0,"net_area_m2":0.68,"fill_m3":29.79},{"station_id":"station_000000420000","chainage_m":420,"net_volume_m3":-44.48,"cumulative_volume_m3":-1619.27,"cut_soil_m3":28.63,"cut_rock_m3":31.33,"cut_rr_m3":31.33,"cut_br_m3":0,"net_area_m2":-5.12,"fill_m3":106.75},{"station_id":"station_000000440000","chainage_m":440,"net_volume_m3":-55.35,"cumulative_volume_m3":-1674.63,"cut_soil_m3":31.58,"cut_rock_m3":44.63,"cut_rr_m3":44.63,"cut_br_m3":0,"net_area_m2":-0.41,"fill_m3":135.77},{"station_id":"station_000000460000","chainage_m":460,"net_volume_m3":-65.95,"cumulative_volume_m3":-1740.58,"cut_soil_m3":29.98,"cut_rock_m3":36.99,"cut_rr_m3":36.99,"cut_br_m3":0,"net_area_m2":-6.18,"fill_m3":136.04},{"station_id":"station_000000480000","chainage_m":480,"net_volume_m3":-45.3,"cumulative_volume_m3":-1785.88,"cut_soil_m3":29.13,"cut_rock_m3":38.44,"cut_rr_m3":38.44,"cut_br_m3":0,"net_area_m2":1.65,"fill_m3":116.3},{"station_id":"station_000000500000","chainage_m":500,"net_volume_m3":-19.66,"cumulative_volume_m3":-1805.54,"cut_soil_m3":26.9,"cut_rock_m3":37.37,"cut_rr_m3":37.37,"cut_br_m3":0,"net_area_m2":-3.62,"fill_m3":87.39},{"station_id":"station_000000520000","chainage_m":520,"net_volume_m3":-119.09,"cumulative_volume_m3":-1924.62,"cut_soil_m3":14.67,"cut_rock_m3":7.91,"cut_rr_m3":7.91,"cut_br_m3":0,"net_area_m2":-8.29,"fill_m3":141.5},{"station_id":"station_000000540000","chainage_m":540,"net_volume_m3":-126.28,"cumulative_volume_m3":-2050.91,"cut_soil_m3":14.82,"cut_rock_m3":7.49,"cut_rr_m3":7.49,"cut_br_m3":0,"net_area_m2":-4.34,"fill_m3":148.34},{"station_id":"station_000000560000","chainage_m":560,"net_volume_m3":65.29,"cumulative_volume_m3":-1985.62,"cut_soil_m3":44.19,"cut_rock_m3":74.39,"cut_rr_m3":74.39,"cut_br_m3":0,"net_area_m2":10.87,"fill_m3":61.14},{"station_id":"station_000000580000","chainage_m":580,"net_volume_m3":242.78,"cumulative_volume_m3":-1742.83,"cut_soil_m3":66.52,"cut_rock_m3":157.01,"cut_rr_m3":157.01,"cut_br_m3":0,"net_area_m2":13.41,"fill_m3":0},{"station_id":"station_000000600000","chainage_m":600,"net_volume_m3":82.99,"cumulative_volume_m3":-1659.84,"cut_soil_m3":45.93,"cut_rock_m3":98.93,"cut_rr_m3":98.93,"cut_br_m3":0,"net_area_m2":-5.11,"fill_m3":73.6},{"station_id":"station_000000620000","chainage_m":620,"net_volume_m3":-131.92,"cumulative_volume_m3":-1791.76,"cut_soil_m3":20.67,"cut_rock_m3":10.53,"cut_rr_m3":10.53,"cut_br_m3":0,"net_area_m2":-8.08,"fill_m3":162.79},{"station_id":"station_000000640000","chainage_m":640,"net_volume_m3":-121.07,"cumulative_volume_m3":-1912.83,"cut_soil_m3":20.8,"cut_rock_m3":17.97,"cut_rr_m3":17.97,"cut_br_m3":0,"net_area_m2":-4.03,"fill_m3":160.72},{"station_id":"station_000000660000","chainage_m":660,"net_volume_m3":-39.29,"cumulative_volume_m3":-1952.12,"cut_soil_m3":32.4,"cut_rock_m3":41.61,"cut_rr_m3":41.61,"cut_br_m3":0,"net_area_m2":0.1,"fill_m3":116.92},{"station_id":"station_000000680000","chainage_m":680,"net_volume_m3":-14.3,"cumulative_volume_m3":-1966.42,"cut_soil_m3":35.3,"cut_rock_m3":45.96,"cut_rr_m3":45.96,"cut_br_m3":0,"net_area_m2":-1.53,"fill_m3":99.61},{"station_id":"station_000000700000","chainage_m":700,"net_volume_m3":-71.74,"cumulative_volume_m3":-2038.16,"cut_soil_m3":29.9,"cut_rock_m3":33.84,"cut_rr_m3":33.84,"cut_br_m3":0,"net_area_m2":-5.65,"fill_m3":138.07},{"station_id":"station_000000720000","chainage_m":720,"net_volume_m3":402.28,"cumulative_volume_m3":-1635.88,"cut_soil_m3":61.44,"cut_rock_m3":369.81,"cut_rr_m3":369.81,"cut_br_m3":0,"net_area_m2":45.88,"fill_m3":83.85},{"station_id":"station_000000740000","chainage_m":740,"net_volume_m3":650.4,"cumulative_volume_m3":-985.48,"cut_soil_m3":87.05,"cut_rock_m3":546.45,"cut_rr_m3":546.45,"cut_br_m3":0,"net_area_m2":19.16,"fill_m3":64.56},{"station_id":"station_000000760000","chainage_m":760,"net_volume_m3":-16.44,"cumulative_volume_m3":-1001.92,"cut_soil_m3":73.88,"cut_rock_m3":330.92,"cut_rr_m3":330.92,"cut_br_m3":0,"net_area_m2":-20.81,"fill_m3":468.45},{"station_id":"station_000000780000","chainage_m":780,"net_volume_m3":-280.07,"cumulative_volume_m3":-1281.99,"cut_soil_m3":57.48,"cut_rock_m3":186.36,"cut_rr_m3":186.36,"cut_br_m3":0,"net_area_m2":-7.2,"fill_m3":548.91},{"station_id":"station_000000800000","chainage_m":800,"net_volume_m3":-286.75,"cumulative_volume_m3":-1568.73,"cut_soil_m3":42.11,"cut_rock_m3":82.07,"cut_rr_m3":82.07,"cut_br_m3":0,"net_area_m2":-21.48,"fill_m3":420.25},{"station_id":"station_000000820000","chainage_m":820,"net_volume_m3":-362.28,"cumulative_volume_m3":-1931.01,"cut_soil_m3":35.3,"cut_rock_m3":59,"cut_rr_m3":59,"cut_br_m3":0,"net_area_m2":-14.75,"fill_m3":462.78},{"station_id":"station_000000840000","chainage_m":840,"net_volume_m3":166.24,"cumulative_volume_m3":-1764.77,"cut_soil_m3":60.69,"cut_rock_m3":256.79,"cut_rr_m3":256.79,"cut_br_m3":0,"net_area_m2":31.37,"fill_m3":187.54},{"station_id":"station_000000860000","chainage_m":860,"net_volume_m3":599.56,"cumulative_volume_m3":-1165.22,"cut_soil_m3":89.41,"cut_rock_m3":445.57,"cut_rr_m3":445.57,"cut_br_m3":0,"net_area_m2":28.58,"fill_m3":0},{"station_id":"station_000000880000","chainage_m":880,"net_volume_m3":385.02,"cumulative_volume_m3":-780.2,"cut_soil_m3":76.97,"cut_rock_m3":316.6,"cut_rr_m3":316.6,"cut_br_m3":0,"net_area_m2":9.92,"fill_m3":53.09},{"station_id":"station_000000900000","chainage_m":900,"net_volume_m3":-308.04,"cumulative_volume_m3":-1088.23,"cut_soil_m3":59.25,"cut_rock_m3":210.02,"cut_rr_m3":210.02,"cut_br_m3":0,"net_area_m2":-40.72,"fill_m3":606.04},{"station_id":"station_000000920000","chainage_m":920,"net_volume_m3":106.99,"cumulative_volume_m3":-981.24,"cut_soil_m3":78.01,"cut_rock_m3":506.21,"cut_rr_m3":506.21,"cut_br_m3":0,"net_area_m2":51.42,"fill_m3":552.96},{"station_id":"station_000000940000","chainage_m":940,"net_volume_m3":671.72,"cumulative_volume_m3":-309.52,"cut_soil_m3":81.63,"cut_rock_m3":516.42,"cut_rr_m3":516.42,"cut_br_m3":0,"net_area_m2":15.75,"fill_m3":3.37},{"station_id":"station_000000960000","chainage_m":960,"net_volume_m3":170.62,"cumulative_volume_m3":-138.9,"cut_soil_m3":56.94,"cut_rock_m3":150.93,"cut_rr_m3":150.93,"cut_br_m3":0,"net_area_m2":1.31,"fill_m3":56.46},{"station_id":"station_000000980000","chainage_m":980,"net_volume_m3":-104.1,"cumulative_volume_m3":-243,"cut_soil_m3":33.73,"cut_rock_m3":44.72,"cut_rr_m3":44.72,"cut_br_m3":0,"net_area_m2":-11.72,"fill_m3":186.56},{"station_id":"station_000001000000","chainage_m":1000,"net_volume_m3":-552.75,"cumulative_volume_m3":-795.75,"cut_soil_m3":7.8,"cut_rock_m3":7.92,"cut_rr_m3":7.92,"cut_br_m3":0,"net_area_m2":-43.55,"fill_m3":568.99},{"station_id":"station_000001020000","chainage_m":1020,"net_volume_m3":-422.84,"cumulative_volume_m3":-1218.59,"cut_soil_m3":12.17,"cut_rock_m3":6.59,"cut_rr_m3":6.59,"cut_br_m3":0,"net_area_m2":1.27,"fill_m3":441.47},{"station_id":"station_000001040000","chainage_m":1040,"net_volume_m3":190.35,"cumulative_volume_m3":-1028.24,"cut_soil_m3":47.18,"cut_rock_m3":132.06,"cut_rr_m3":132.06,"cut_br_m3":0,"net_area_m2":17.77,"fill_m3":5.96},{"station_id":"station_000001060000","chainage_m":1060,"net_volume_m3":137.74,"cumulative_volume_m3":-890.51,"cut_soil_m3":67.53,"cut_rock_m3":226.43,"cut_rr_m3":226.43,"cut_br_m3":0,"net_area_m2":-3.99,"fill_m3":186.83},{"station_id":"station_000001080000","chainage_m":1080,"net_volume_m3":154.83,"cumulative_volume_m3":-735.67,"cut_soil_m3":69.02,"cut_rock_m3":239.96,"cut_rr_m3":239.96,"cut_br_m3":0,"net_area_m2":19.48,"fill_m3":186.83},{"station_id":"station_000001100000","chainage_m":1100,"net_volume_m3":-127.83,"cumulative_volume_m3":-863.5,"cut_soil_m3":53.4,"cut_rock_m3":164.78,"cut_rr_m3":164.78,"cut_br_m3":0,"net_area_m2":-32.26,"fill_m3":367.86},{"station_id":"station_000001120000","chainage_m":1120,"net_volume_m3":-621.7,"cumulative_volume_m3":-1485.2,"cut_soil_m3":16.9,"cut_rock_m3":25.78,"cut_rr_m3":25.78,"cut_br_m3":0,"net_area_m2":-29.91,"fill_m3":666.95},{"station_id":"station_000001133426","chainage_m":1133.43,"net_volume_m3":-197.49,"cumulative_volume_m3":-1682.7,"cut_soil_m3":9.55,"cut_rock_m3":9.57,"cut_rr_m3":9.57,"cut_br_m3":0,"net_area_m2":0.49,"fill_m3":217.23}]}} diff --git a/resources/tester/test_309_2_calc_sites.py b/resources/tester/test_309_2_calc_sites.py index 7bd2db9c4..c78cc10bc 100644 --- a/resources/tester/test_309_2_calc_sites.py +++ b/resources/tester/test_309_2_calc_sites.py @@ -56,7 +56,7 @@ def test_묶음_여덟_줄_등록부_전부(): sites = _registry_sites() assert len(sites) == 45 nos = [no for group in calc_sites.GROUPS for no in group["nos"]] - assert len(calc_sites.GROUPS) == 12 # 511-B 단계 다섯 · F9 폼 기본값 + assert len(calc_sites.GROUPS) == 13 # 511-B 단계 다섯 · F9 폼 기본값 · 511-C haul assert len(nos) == len(set(nos)) and set(nos) == set(sites) assert calc_sites.SWITCHABLE == { "ground_sample", @@ -66,13 +66,14 @@ def test_묶음_여덟_줄_등록부_전부(): "route", "stations", "profile", # 511-B B-3 엶 + "haul", # 511-C E2 엶(기본 server) "fixed_browser", } # 511-A 셋 더 · 511-B B-2 stations · B-5 route for group in calc_sites.GROUPS: got = {sites[no] for no in group["nos"]} if group["fixed"] == "browser": assert got == {"browser_only"}, group["key"] - elif group["fixed"] == "server": + elif group["fixed"] == "server" or group.get("default") == "server": assert got <= {"server_canon", "server_only"}, group["key"] else: assert got == {"browser_now"}, group["key"] @@ -82,17 +83,19 @@ def test_묶음_여덟_줄_등록부_전부(): def test_커밋한_판_기본값_없음_깨짐(tmp_path, monkeypatch): committed = json.loads(calc_sites.PATH.read_text(encoding="utf-8")) - assert committed["sites"] == dict.fromkeys(calc_sites.SWITCHABLE, "both") - assert set(calc_sites.read_sites()["sites"].values()) == {"both"} + assert calc_sites.DEFAULTS == {**dict.fromkeys(calc_sites.SWITCHABLE, "both"), "haul": "server"} + assert set(committed["sites"].items()) <= set(calc_sites.DEFAULTS.items()) + assert calc_sites.read_sites()["sites"] == calc_sites.DEFAULTS # 판 파일 haul 없음 = server monkeypatch.setattr(calc_sites, "PATH", tmp_path / "none.json") - assert calc_sites.read_sites() == { - "version": 0, - "sites": dict.fromkeys(calc_sites.SWITCHABLE, "both"), - } + assert calc_sites.read_sites() == {"version": 0, "sites": calc_sites.DEFAULTS} broken = tmp_path / "broken.json" broken.write_text('{"sites": {"ground_sample": "server", "corridor_mesh": "x"', "utf-8") monkeypatch.setattr(calc_sites, "PATH", broken) - assert set(calc_sites.read_sites()["sites"].values()) == {"both"} + assert calc_sites.read_sites()["sites"] == calc_sites.DEFAULTS + odd = tmp_path / "odd.json" + odd.write_text('{"sites": {"haul": "x", "quantity": "server"}}', "utf-8") + monkeypatch.setattr(calc_sites, "PATH", odd) + assert calc_sites.read_sites()["sites"] == {**calc_sites.DEFAULTS, "quantity": "server"} class _Cursor: @@ -166,7 +169,7 @@ def test_잠금_모르는_줄_400_감사_한_줄(client): done = http.put(url, json={"sites": {"ground_sample": "server"}}) assert done.status_code == 200 and done.json()["changed"] == ["ground_sample"] saved = json.loads(board.read_text("utf-8")) - sites = {**dict.fromkeys(calc_sites.SWITCHABLE, "both"), "ground_sample": "server"} + sites = {**calc_sites.DEFAULTS, "ground_sample": "server"} assert saved == {"version": 2, "sites": sites} inserts = [args for sql, args in log if "system_audit_logs" in sql] assert len(inserts) == 1 diff --git a/resources/tester/test_309_2_calc_sites_browser.py b/resources/tester/test_309_2_calc_sites_browser.py index f68c8a523..cd4e067ca 100644 --- a/resources/tester/test_309_2_calc_sites_browser.py +++ b/resources/tester/test_309_2_calc_sites_browser.py @@ -148,7 +148,7 @@ def test_시스템_관리자_카드_창_G1_서버_길(browser): ) heads = [row for row in rows if row["head"]] calcs = [row for row in rows if not row["head"]] - assert len(heads) == 12 and len(calcs) == 45 # 511-B 단계 다섯 줄 · F9 폼 기본값 + assert len(heads) == 13 and len(calcs) == 45 # 511-B 단계 다섯 줄 · F9 · 511-C haul whys = {row["name"].split(" · ")[0]: row["why"] for row in calcs} for no, n in {"R1": 5, "F2": 5, "R6": 2, "C1": 4, "F6": 4}.items(): assert whys[no] == f"짝 모으는 중 · B-{n}", no @@ -156,6 +156,7 @@ def test_시스템_관리자_카드_창_G1_서버_길(browser): editable = [row["name"] for row in calcs if not row["boxes"][0][1]] opened = ["G1", "D3", "E1", "F7", "F8", "C4", "R9", "F10"] # 511-A ③ ④ 더 opened += ["P1", "P2", "P3", "P4", "P8"] # 511-B B-3 종단 엶 + opened += ["E2"] # 511-C 유토 배분 엶(기본 server = 체크 꺼짐) opened += ["R2", "R4", "R8", "P7", "C7", "D4"] # ⑧ 시험용 assert [name.split(" · ")[0] for name in editable] == opened assert all(row["name"].split(" · ")[1] for row in calcs) # 이름 = 등록부 label diff --git a/resources/tester/test_342_1_haul_plan_key.py b/resources/tester/test_342_1_haul_plan_key.py index 9a916d7e1..9a84a1d3c 100644 --- a/resources/tester/test_342_1_haul_plan_key.py +++ b/resources/tester/test_342_1_haul_plan_key.py @@ -23,15 +23,15 @@ def _patch(monkeypatch, outputs: list) -> list: async def _none(_project_id): return {} - def _run(_bundle, _script, payload): - calls.append(payload) + def _run(result, context): + calls.append(result) return outputs.pop(0) monkeypatch.setattr(router, "haul_inputs_for", _none) monkeypatch.setattr(router, "conversion_factors_for", _none) monkeypatch.setattr(router, "haul_limits_for", _none) monkeypatch.setattr(router, "_mass_haul_context", lambda *_: {"limits": []}) - monkeypatch.setattr(router, "run_bundle_json", _run) + monkeypatch.setattr(router, "_run_view", _run) router._KEPT.clear() return calls diff --git a/resources/tester/test_342_2_confirm_fingerprint.py b/resources/tester/test_342_2_confirm_fingerprint.py index 7b5580067..d653aded7 100644 --- a/resources/tester/test_342_2_confirm_fingerprint.py +++ b/resources/tester/test_342_2_confirm_fingerprint.py @@ -48,12 +48,12 @@ def node(monkeypatch, tmp_path): bundle.write_text("//", encoding="utf-8") calls: list = [] - def fake_run(path, script, payload): + def fake_run(payload): calls.append(payload) return {"areas": [], "mass_haul": {"total": len(calls)}} - monkeypatch.setattr(prebuild, "BUNDLE", bundle) - monkeypatch.setattr(prebuild, "run_bundle_json", fake_run) + monkeypatch.setattr(prebuild, "NODE_BUNDLE", bundle) + monkeypatch.setattr(prebuild, "_run_node", fake_run) monkeypatch.setattr(prebuild, "bundle_stale", lambda path: False) monkeypatch.setattr(prebuild, "_NODE_CACHE", {}) return SimpleNamespace(calls=calls, bundle=bundle) @@ -105,7 +105,7 @@ def test_TS_가_번들보다_새로우면_다시(node, monkeypatch): def test_실패는_기억_안_함(node, monkeypatch): - monkeypatch.setattr(prebuild, "run_bundle_json", lambda *a: node.calls.append(a) or None) + monkeypatch.setattr(prebuild, "_run_node", lambda *a: node.calls.append(a) or None) assert prebuild._node_output(_input()) is None assert prebuild._node_output(_input()) is None assert len(node.calls) == 2 @@ -241,13 +241,13 @@ def test_같은_입력_두_번이면_Node_0_쓰기_0(node, monkeypatch, tmp_path written_haul.append(data_patch) stored["data"]["mass_haul"] = data_patch["mass_haul"] - def fake_run(path, script, payload): + def fake_run(payload): node.calls.append(payload) # Node 에 간 입력이 곧 지문 입력 — 다른 입력 길 없음 assert set(payload) == {"detail", "context"} return {"areas": [{"chainage_m": 0.0, "cut_area_m2": 5.0}], "mass_haul": {"total": 9.0}} - monkeypatch.setattr(prebuild, "run_bundle_json", fake_run) + monkeypatch.setattr(prebuild, "_run_node", fake_run) monkeypatch.setattr(b06_router, "get_section_detail", detail) monkeypatch.setattr(prebuild, "run_with_connection", run_with) monkeypatch.setattr(prebuild, "resolve_stored_project_path", lambda path: str(tmp_path)) diff --git a/resources/tester/test_472_3_calc_registry.py b/resources/tester/test_472_3_calc_registry.py index 4c3bb9409..507349f85 100644 --- a/resources/tester/test_472_3_calc_registry.py +++ b/resources/tester/test_472_3_calc_registry.py @@ -264,7 +264,16 @@ def test_서버_쪽_코드_브라우저_밖(loaded, reach) -> None: if row["site"] in ("server_canon", "server_only") and row["ts"] ] assert server # 유토 배분(common_util_mass_haul_balance) - assert [path for path in server if path in browser] == [] + # 511-C(요구 321) — 유토 배분은 판 haul = both 때만 dynamic import(첫 묶음 밖) · 정적 import 0 + lazy = "common_util/common_util_mass_haul_balance.ts" + assert [path for path in server if path in browser] == [lazy] + static = re.compile( + r'^import\s+(?!type\s)[^;]*?from\s+"@util/common_util_mass_haul_balance"', re.M + ) + ts_files = [p for p in browser if p.endswith(".ts")] + assert [p for p in ts_files if static.search((ROOT / p).read_text(encoding="utf-8"))] == [] + haul_api = (ROOT / "B05_Profile" / "B05_Profile_Api_HaulPlan.ts").read_text(encoding="utf-8") + assert 'await import("@util/common_util_mass_haul_balance")' in haul_api assert NODE_ENTRY not in browser assert reach[REGISTRY] == sorted(REGISTRY_PARTS) # 순수 자료 — 제 몫 파일만(나눔 · 함수 안 끔) diff --git a/resources/tester/test_511_c_haul_node.py b/resources/tester/test_511_c_haul_node.py new file mode 100644 index 000000000..305d29dd0 --- /dev/null +++ b/resources/tester/test_511_c_haul_node.py @@ -0,0 +1,199 @@ +# -*- coding: utf-8 -*- +"""511-C (요구 320-4) — 유토 배분 E2 = 늘 떠 있는 Node(TS 한 벌) · 옛 server-calc 묶음 값과 0 차. + +못박는 것 + - /haul-plan 창구 새 길(`_run_view` → 시험 길 `haul.view`) = 옛 server-calc `haul_plan_for` 갈래 + = 브라우저 자리 함수(`computeHaulPlan` 바로 부름) — 프로젝트 일곱 + (저장 유토곡선 · DB 에서 한 번 뜬 `fixtures/haul_511_c.json`) + × 문맥(기본 · 장비 경계 없음 · 구조물 잔토 · 채집석) × 그림 · 수량. + - 끝 경우 — 측점 하나(배분 없음 null) · 둘 · 전부 0 · 전부 절토 · 전부 성토. + - [저장] 정본 새 길(`Server_Calc_Prebuild._node_output` → 늘 떠 있는 Node 셋) = 옛 server-calc + `{detail, context}` 답(면적 · 다단 벽 · 유토곡선 + 배분 두 벌) — golden_472 · 문맥 셋. + + ./venv/Scripts/python.exe -m pytest resources/tester/test_511_c_haul_node.py -q +""" + +from __future__ import annotations + +import copy +import json +import shutil +import subprocess +import sys +from pathlib import Path + +import pytest + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[1] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from B06_Section import B06_Section_Router_HaulPlan as haul_router # noqa: E402 +from B06_Section import B06_Section_Server_Calc_Prebuild as prebuild # noqa: E402 +from common_util.common_util_node_bundle import run_bundle_json # noqa: E402 + +pytestmark = pytest.mark.skipif( + shutil.which("node") is None or not (ROOT / "config" / "node_modules" / "vite").is_dir(), + reason="node · 프론트엔드 의존성 없음", +) + +PROJECTS = json.loads((HERE / "fixtures" / "haul_511_c.json").read_text(encoding="utf-8")) +GOLDEN = HERE / "fixtures" / "golden_472" +SPOIL_POINTS = [ + {"chainage_m": 40.0, "spoil_m3": 35.5, "ground_type": "soil"}, + {"chainage_m": 260.0, "spoil_m3": 12.25, "ground_type": "ripping_rock"}, + {"chainage_m": 600.0, "spoil_m3": 8.0, "ground_type": None}, +] + + +def _result(payload: dict) -> dict: + """저장 유토곡선 → 누가토량 결과 꼴(최소 · 최대 누계는 측점에서).""" + cums = [p["cumulative_volume_m3"] for p in payload["points"]] + return { + **payload, + "min_cumulative_m3": min(cums, default=0.0), + "max_cumulative_m3": max(cums, default=0.0), + } + + +def _contexts() -> dict[str, dict]: + base = prebuild._mass_haul_context() + return { + "기본": base, + "장비 경계 없음": {**base, "haul_equipment_limits": None}, + "구조물 잔토": { + **base, + "structure_spoil_points": SPOIL_POINTS, + "structure_spoil_m3": 55.75, + }, + "채집석": { + **base, + "collected_stone_deduction_m3": 120.0, + "collected_stone_by_ground_m3": {"soil": 40.0, "blasting_rock": 30.0}, + "collected_stone_ground_unknown_m3": 5.0, + }, + } + + +def _options(context: dict, drawing: bool) -> dict: + keys = ( + "collected_stone_deduction_m3", + "collected_stone_by_ground_m3", + "collected_stone_ground_unknown_m3", + "structure_spoil_m3", + "structure_spoil_points", + ) + return { + **{k: context.get(k) for k in keys}, + "conversion": context.get("earthwork_conversion"), + "drawing": drawing, + } + + +def _browser(tmp_path: Path, calls: list[dict]) -> list: + job = tmp_path / "job.json" + job.write_text(json.dumps(calls, ensure_ascii=False), encoding="utf-8") + proc = subprocess.run( # noqa: S603 — 고정 실행 파일 + ["node", str(HERE / "helper_511_a_mirror.cjs"), str(job)], + cwd=ROOT, + capture_output=True, + text=True, + encoding="utf-8", + timeout=300, + ) + assert proc.returncode == 0, proc.stderr + return json.loads(proc.stdout) + + +def _old_view(result: dict, context: dict): + out = run_bundle_json( + prebuild.BUNDLE, prebuild._NPM_SCRIPT, {"haul_plan_for": result, "context": context} + ) + assert isinstance(out, dict), out + return out["haul_plan"] + + +def _edge_results() -> dict[str, dict]: + conv = PROJECTS["483"]["conversion"] + + def make(nets: list[float]) -> dict: + points, cum = [], 0.0 + for index, net in enumerate(nets): + cum += net + points.append( + { + "station_id": f"s{index}", + "chainage_m": index * 20.0, + "net_volume_m3": net, + "cumulative_volume_m3": cum, + "cut_soil_m3": max(net, 0.0), + "cut_rock_m3": 0.0, + "cut_rr_m3": 0.0, + "cut_br_m3": 0.0, + "net_area_m2": net / 20.0, + "fill_m3": max(-net, 0.0), + } + ) + return _result({"points": points, "conversion": conv}) + + return { + "측점 하나": make([0.0]), + "측점 둘": make([0.0, 50.0]), + "전부 0": make([0.0] * 6), + "전부 절토": make([0.0, 30.0, 45.0, 10.0, 80.0]), + "전부 성토": make([0.0, -30.0, -45.0, -10.0, -80.0]), + "오르내림": make([0.0, 120.0, -200.0, 90.0, -15.0, 60.0, -140.0, 70.0]), + } + + +def test_창구_새_길_옛_묶음_브라우저_같음(tmp_path): + cases = [ + (f"{rid}·{name}", _result(payload), context) + for rid, payload in PROJECTS.items() + for name, context in _contexts().items() + ] + [(f"끝·{name}", result, _contexts()["기본"]) for name, result in _edge_results().items()] + calls = [ + { + "mod": "common_util/common_util_mass_haul_balance.ts", + "fn": "computeHaulPlan", + "args": [result, context.get("haul_equipment_limits"), _options(context, drawing)], + } + for _, result, context in cases + for drawing in (True, False) + ] + browser = _browser(tmp_path, calls) + checked = 0 + for index, (label, result, context) in enumerate(cases): + new = haul_router._run_view(result, context) + assert new is not None, label + assert new["haul_plan"] == _old_view(result, context), label + assert new["haul_plan"] == browser[2 * index], label + quantity = haul_router.run_calc_served( + ["haul.view"], + { + "args": { + "haul.view": [ + result, + context.get("haul_equipment_limits"), + _options(context, False), + ] + } + }, + )["results"]["haul.view"] + assert quantity == browser[2 * index + 1], label + checked += 1 + assert checked == len(PROJECTS) * 4 + 6 + assert browser[2 * len(PROJECTS) * 4] is None # 측점 하나 = 배분 없음 + assert any(item and item["transfers"] for item in browser) # 빈 배분만 견준 것 아님 + + +@pytest.mark.parametrize("name", ["기본", "구조물 잔토", "채집석"]) +def test_저장_정본_새_길_옛_묶음_같음(name, monkeypatch): + monkeypatch.setattr(prebuild, "_NODE_CACHE", {}) + detail = json.loads((GOLDEN / "detail.json").read_text(encoding="utf-8")) + node_input = {"detail": detail, "context": _contexts()[name]} + old = run_bundle_json(prebuild.BUNDLE, prebuild._NPM_SCRIPT, copy.deepcopy(node_input)) + new = prebuild._node_output(copy.deepcopy(node_input)) + assert new == old + assert new["mass_haul"]["haul_plan"] and new["mass_haul"]["haul_plan_drawing"] diff --git a/ui_template/ui_template_locale_m1.ts b/ui_template/ui_template_locale_m1.ts index 4fc17dd60..bbc381f47 100644 --- a/ui_template/ui_template_locale_m1.ts +++ b/ui_template/ui_template_locale_m1.ts @@ -97,6 +97,7 @@ export const ui_locales_m1 = { B01_CalcSites_profile: ["종단 단계", "Profile stage"], B01_CalcSites_cross: ["횡단 단계", "Cross stage"], B01_CalcSites_form_default: ["폼 기본값 도구", "Form default tool"], + B01_CalcSites_haul: ["유토 배분", "Haul plan"], B01_CalcSites_fixed_server: ["고정 서버", "Fixed server"], B01_CalcSites_fixed_browser: ["고정 브라우저", "Fixed browser"], B01_CalcSites_Why_ground_sample: [ @@ -137,6 +138,26 @@ export const ui_locales_m1 = { "서버 단독(GIS · 래스터 · 값어치)", "Server only (GIS · raster · value)", ], + B01_CalcSites_Why_haul: [ + "켜면 브라우저가 배분 모듈을 그때 받아 셈 · 끄면(기본) 잠잠 뒤 서버 /haul-plan", + "On = browser loads the haul module then calcs · off (default) = server /haul-plan after a pause", + ], + B01_CalcSites_Why_server_G3: [ + "셈이 아니라 DB 씀(넓힘 저장) · 미리 보일 값 없음", + "Writes the DB (widening save), not a calc · nothing to preview", + ], + B01_CalcSites_Why_server_D1: [ + "래스터 흐름 · 유역 다각형(GIS 라이브러리) · DEM 수십 MB", + "Raster flow · basin polygons (GIS libraries) · DEM tens of MB", + ], + B01_CalcSites_Why_server_D2: [ + "배수유역(D1) 흐름 누적 래스터 위에서만 셈", + "Runs only on the D1 flow-accumulation raster", + ], + B01_CalcSites_Why_server_N1: [ + "큰 지형 파일 · 세류 래스터 탐색(scipy) · 6~87 s", + "Large terrain file · stream raster search (scipy) · 6–87 s", + ], B01_CalcSites_Why_browser: [ "시험용 — 끄면 서버로 돌림(느림) · P7 · D4 = 서버 값 · 끌기 도구(R2 · R4 · R8 · C7) = 대조만", "Test only — off = server (slow) · P7 · D4 = server value · drag tools (R2 · R4 · R8 · C7) = compare only",