도면 출력 추가: 수리 종단면도, 관망도(색인도·부분도·수압표), 범례·색상 선택, DXF(AutoCAD 2018)
- 데모 v2 이후 개발 PC의 변경을 옮김 (이 저장소의 이름 변경·README 정리는 유지) - 서버 기본 DEM, DEM 등고선, 속성 테이블 범위 선택, WNTR 교차 검증 시험 포함 - 새 라이브러리: ezdxf(Python), maplibre-contour(npm) - 변경 내역: docs/SPipe_패치내역_도면출력_2026-11-06.txt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
14f07181db
commit
e3f386addf
33 files changed
+3454
-482
No files matched your search
+48
-2
@@ -16,6 +16,7 @@ from .columnar import to_columnar
|
||||
from .gisio import read_vector
|
||||
from . import dem as dem_store
|
||||
from . import gisops
|
||||
from .drawing_dxf import drawing_to_dxf
|
||||
from urllib.parse import quote
|
||||
from .jobs import get_job
|
||||
from .epanet import EN, EpanetAPI, EpanetException, FLOW_UNITS, PRESS_UNITS, QUAL_TYPES
|
||||
@@ -186,6 +187,12 @@ async def dem_upload(file: UploadFile = File(...)):
|
||||
pass
|
||||
|
||||
|
||||
@app.get("/api/dem-base")
|
||||
async def dem_base():
|
||||
"""Server-provided DEMs (registered with `python -m backend.dem register ...`)."""
|
||||
return await _dem_run(dem_store.list_base)
|
||||
|
||||
|
||||
@app.get("/api/dem/{dem_id}")
|
||||
async def dem_get(dem_id: str):
|
||||
return await _dem_run(dem_store.dem_info, dem_id)
|
||||
@@ -219,9 +226,9 @@ async def dem_hydro(dem_id: str, request: Request):
|
||||
"""Body: {"op": "streams", "minAreaKm2": 0.1} or {"op": "watershed", "points": [[x, y]], "crs": "...", "snap": m}."""
|
||||
body = await request.json()
|
||||
if body.get("op") == "streams":
|
||||
return await _dem_run(dem_store.streams, dem_id, float(body.get("minAreaKm2") or 0.1), timeout=600)
|
||||
return await _dem_run(dem_store.streams, dem_id, float(body.get("minAreaKm2") or 0.1), body.get("bbox"), body.get("crs"), timeout=600)
|
||||
if body.get("op") == "watershed":
|
||||
return await _dem_run(dem_store.watershed, dem_id, body.get("points") or [], body.get("crs"), float(body.get("snap") or 50), timeout=600)
|
||||
return await _dem_run(dem_store.watershed, dem_id, body.get("points") or [], body.get("crs"), float(body.get("snap") or 50), body.get("bbox"), timeout=600)
|
||||
raise HTTPException(status_code=400, detail="op must be streams or watershed")
|
||||
|
||||
|
||||
@@ -257,6 +264,45 @@ async def gis_reproject(request: Request):
|
||||
return await _dem_run(gisops.reproject_fc, body.get("featureCollection"), body.get("from"), body.get("to"))
|
||||
|
||||
|
||||
_UESC = re.compile(r"\\U\+([0-9A-Fa-f]{4})")
|
||||
_CRLF = "\r\n"
|
||||
|
||||
|
||||
def dxf_to_cp949(text: str) -> bytes:
|
||||
"""R12 DXF from the browser (non-ASCII written as backslash-U+XXXX) -> Korean
|
||||
code page (ANSI_949), which Korean CAD reads; characters outside CP949 stay escaped."""
|
||||
def unesc(m: re.Match) -> str:
|
||||
ch = chr(int(m.group(1), 16))
|
||||
try:
|
||||
ch.encode("cp949")
|
||||
return ch
|
||||
except UnicodeEncodeError:
|
||||
return m.group(0)
|
||||
|
||||
out = _UESC.sub(unesc, text)
|
||||
ver = _CRLF.join(["$ACADVER", "1", "AC1009", ""])
|
||||
out = out.replace(ver, ver + _CRLF.join(["9", "$DWGCODEPAGE", "3", "ANSI_949", ""]), 1)
|
||||
return out.encode("cp949", errors="replace")
|
||||
|
||||
|
||||
@app.post("/api/drawing/dxf")
|
||||
async def drawing_dxf(request: Request):
|
||||
"""Body: {"drawing": {...items}, "name": ...} -> DXF R2018 (ezdxf, UTF-8 text);
|
||||
legacy {"dxf": "<R12 DXF text>"} -> CP949 R12."""
|
||||
body = await request.json()
|
||||
name = str(body.get("name") or "drawing") + ".dxf"
|
||||
if isinstance(body.get("drawing"), dict):
|
||||
data = await _dem_run(drawing_to_dxf, body["drawing"], timeout=120)
|
||||
return Response(content=data, media_type="application/dxf",
|
||||
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{quote(name)}"})
|
||||
text = str(body.get("dxf") or "")
|
||||
if not text.startswith("0" + _CRLF + "SECTION") or len(text) > 50_000_000:
|
||||
raise HTTPException(status_code=400, detail="DXF가 올바르지 않습니다.")
|
||||
name = str(body.get("name") or "drawing") + ".dxf"
|
||||
return Response(content=dxf_to_cp949(text), media_type="application/dxf",
|
||||
headers={"Content-Disposition": f"attachment; filename*=UTF-8''{quote(name)}"})
|
||||
|
||||
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
Reference in new issue
Block a user