Skip to main content
Glama
README.md
# Cadastral MCP

브이월드(국토교통부) Open API 기반 **토지이용계획 조회 + 지적도 DXF 내보내기** MCP 서버.

## 기능

| 도구 | 설명 |
|---|---|
| `get_land_use_plan` | 지번 주소로 토지대장(지목·면적·소유구분) + 용도지역/지구/구역 + 개별공시지가 통합 조회 |
| `get_land_use_plan_by_pnu` | PNU(19자리)로 위와 동일 조회 |
| `export_cadastral_dxf` | 지번 주소로 필지 경계(+주변 필지)를 DXF 파일로 내보내기 |
| `export_cadastral_dxf_by_pnu` | PNU(19자리)로 위와 동일 내보내기 |
| `export_land_report_dxf` | 지번 주소로 요청정보(표)+토지이용계획+지적도(XCLIP 크롭)를 한 페이지 DXF로 내보내기. `topoDxfPath`를 주면 지적도 패널에 수치지형도(등고선·건물·도로)를 겹쳐 그림 |
| `export_land_report_dxf_by_pnu` | PNU(19자리)로 위와 동일 내보내기 |
| `export_site_3d_dxf` | 지번 주소 + 수치지형도 DXF로 3D 대지 모델(지형 TIN + 건물 매스 + 도로·하천) 생성 |
| `export_site_3d_dxf_by_pnu` | PNU(19자리)로 위와 동일 3D 모델 생성 |
| `search_national_law` | 국가법령(법률/시행령/시행규칙) 이름으로 검색 (판례·법률용어 등은 다루지 않음) |
| `get_national_law_text` | 국가법령 MST로 조문 조회 — 조번호(jo) 미지정 시 목차, 지정 시 해당 조문 전체 텍스트 |

모든 기능이 참고용입니다 — 법적 효력이 있는 「토지이용계획확인서」·지적측량성과도를 대체하지 않습니다. 정확한 확인은 [토지이음(eum.go.kr)](https://www.eum.go.kr) 또는 관할 행정청에 문의하세요.

## 설치

```bash
npm install
npm run build
```

`export_land_report_dxf`(한 페이지 리포트, XCLIP 크롭)를 쓰려면 Python 3 + ezdxf도 필요합니다:

```bash
pip install ezdxf
```

Python이 여러 개 설치돼 있어 자동탐지(`python3` → `python` → `py`)가 ezdxf 없는 쪽을 먼저 찾으면, `.env`에 `CADASTRAL_PYTHON`으로 정확한 경로를 지정하세요. `export_cadastral_dxf`(지도만 내보내는 도구)는 Python 없이도 그대로 동작합니다.

## VWorld API 키 발급

1. [vworld.kr](https://www.vworld.kr) 가입 후 로그인
2. 오픈API 인증키 신청 페이지에서:
   - **서비스URL**: 이 서버를 실행할 도메인/IP (예: `http://localhost`, 서버 배포 시 실제 도메인)
   - **활용API**: 아래 항목 체크 필수
     - `검색 API` — 주소 → PNU 변환
     - `2D데이터 API` — 지적 필지 경계 좌표, 개별공시지가(폴리곤 속성)
     - `국가중점 API` — 토지대장, 용도지역/지구 (`ned/data/*` 엔드포인트가 이 카테고리)
     - `WMS/WFS API` — 필수는 아니지만 함께 체크해두면 무방
3. 승인은 즉시~수 시간 소요될 수 있습니다.

**중요**: `검색 API`는 도메인 값 없이도 동작하지만, `2D데이터`·`국가중점` API는 요청에 `domain` 파라미터가 없거나 키 신청 시 등록한 서비스URL과 다르면 `INCORRECT_KEY` 에러가 발생합니다. 아래 `VWORLD_DOMAIN`을 반드시 서비스URL과 동일하게 맞추세요.

## 환경변수

`.env.example`을 `.env`로 복사한 뒤 채웁니다.

| 변수 | 필수 | 설명 |
|---|---|---|
| `VWORLD_KEY` | 필수 | VWorld 오픈API 인증키 |
| `VWORLD_DOMAIN` | 필수에 가까움 | 키 신청 시 등록한 서비스URL과 정확히 일치해야 함. 없으면 2D데이터·국가중점 API가 `INCORRECT_KEY`로 실패 |
| `DXF_OUTPUT_DIR` | 선택 | DXF 파일 저장 경로 (기본값: `./output`) |
| `CADASTRAL_PYTHON` | 선택 | `export_land_report_dxf`의 XCLIP 크롭에 쓸 Python 실행 파일 경로. 미지정 시 `python3`/`python`/`py` 자동탐지 |
| `LAW_OC` | 선택 | 법제처 Open API 인증키. `export_land_report_dxf`가 필지 소재 지자체 조례에서 건폐율/용적률/조경 기준을 자동 조회하는 데 사용. 없으면 국가법령 상한으로 대체 |
| `VWORLD_REQUEST_TIMEOUT_MS` | 선택 | VWorld 요청 제한시간. 기본 15,000ms |
| `VWORLD_MAX_BODY_BYTES` | 선택 | VWorld 단일 응답 본문 상한. 기본 10MiB |
| `VWORLD_COLLECTION_DEADLINE_MS` | 선택 | 타일·페이지 전체 수집 deadline. 기본 60,000ms |
| `CADASTRAL_XCLIP_TIMEOUT_MS` | 선택 | Python XCLIP 자식 프로세스 제한시간. 기본 60,000ms |
| `LAW_REQUEST_TIMEOUT_MS` / `LAW_DOWNLOAD_TIMEOUT_MS` | 선택 | 법제처 JSON/파일 요청 전체 deadline. 기본 10초/30초 |
| `LAW_MAX_BODY_BYTES` / `LAW_MAX_FILE_BYTES` | 선택 | 법제처 JSON/파일 본문 상한. 기본 10MiB/50MiB |
| `CADASTRAL_AUDIT_ENABLED` | 선택 | `0`이면 전용 감사 로그 비활성. 기본 활성 |
| `CADASTRAL_AUDIT_LOG_DIR` | 선택 | 개인정보 최소화 JSONL 감사 로그 폴더. 기본 `./logs` |
| `CADASTRAL_AUDIT_RETENTION_DAYS` | 선택 | 월별 감사 로그 보존일. 기본 90일 |

감사 로그에는 시각·서버버전·도구명·성공/실패·실행시간·응답크기·타일/페이지/완전수집 통계만 기록합니다. 주소·PNU·수치지형도 경로·생성 파일경로·건축용도 원문·API 키는 기록하지 않습니다.

## MCP 클라이언트 연결 (예: Claude Desktop)

`claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "cadastral-mcp": {
      "command": "node",
      "args": ["절대경로/cadastral-mcp/build/index.js"],
      "env": {
        "VWORLD_KEY": "발급받은키",
        "VWORLD_DOMAIN": "키 신청 시 등록한 서비스URL"
      }
    }
  }
}
```

## 도구 상세

### `get_land_use_plan` / `get_land_use_plan_by_pnu`

- 입력: `address`(또는 `pnu`), `stdrYear`(선택, 개별공시지가 기준연도, 기본값 올해)
- 출력 예시:

```json
{
  "PNU": "1234567890123456789",
  "토지대장": { "지목": "대", "면적": "300", "소유구분": "개인" },
  "용도지역지구": ["제2종일반주거지역", "가축사육제한구역"],
  "개별공시지가": [{ "기준연도": "2026", "제곱미터당공시지가": "500000" }]
}
```

### `export_cadastral_dxf` / `export_cadastral_dxf_by_pnu`

- 입력: `address`(또는 `pnu`), `crs`(선택 — 미지정 시 필지 경도로 벨트 자동 감지), `includeNeighbors`(선택, 기본 `true`), `bufferMeters`(선택, 기본 `50`, 허용 0~5,000m)
- 대상 필지는 대상 필지 경계상자를 `bufferMeters`만큼 확장한 범위 안의 모든 필지(공공도로 포함)를 함께 조회해 그립니다.
- VWorld의 요청면적 10㎢ 제한을 넘으면 3km 이하 bbox 타일로 분할하고, 각 타일의 `record.total`·페이지를 끝까지 수집한 뒤 PNU로 중복 제거합니다. 오류·부분수집은 빈 주변필지로 바꾸지 않고 실패로 반환합니다.
- 응답의 `주변필지_조회`에 타일수·페이지수·API 반환 피처수·고유필지수·완전수집 여부가 기록됩니다.
- **사용자가 범위(`bufferMeters`)를 지정하지 않았다면, 호출 전에 몇 미터로 할지 먼저 물어보고 진행하세요.** (도구 설명에도 이 지침이 포함되어 있어 MCP 클라이언트가 자동으로 인지합니다.)
- DXF 레이어 구성:
  - `TARGET_PARCEL` / `TARGET_LABEL` — 대상 필지 경계(굵은 빨강 선) + 지번 라벨
  - `NEIGHBOR_PARCELS` / `NEIGHBOR_LABEL` — 주변 필지 경계(얇은 파랑 선) + 지번 라벨
  - `SOURCE_NOTE` — 출처/면책 문구(초록)
- 좌표계는 VWorld가 서버단에서 투영 변환해 반환하므로(EPSG:5186/5174 등) 별도 좌표변환 라이브러리 없이 그대로 DXF 좌표로 사용합니다.

### `export_land_report_dxf` / `export_land_report_dxf_by_pnu`

- 입력: `address`(또는 `pnu`), `crs`/`includeNeighbors`/`bufferMeters`(기본값은 `export_cadastral_dxf`와 동일), `stdrYear`(선택)
- 한 페이지 레이아웃(돋움 폰트, 흰 다이아몬드 아이콘 + 초록 제목 + 노랑 이중밑줄 타이틀), 좌/우상단 모두 표 형식:
  - **좌측**: 요청 정보 표 — 대지위치·대지면적·지목·지역/지구는 VWorld 데이터. **최대건폐율·최대용적률·조경계획·주차계획은 국가법령 상한과 필지 소재 지자체 조례(도시계획조례·건축조례·주차장조례) 값을 각각 별도 행으로 나란히 표시**(아래 "지자체 조례 자동 조회" 참고). 전면도로·접도길이는 데이터 소스가 없어 `-`
  - **우상단**: 토지이용계획 — 법정동·지번·지목·면적·소유구분·지역/지구·개별공시지가 7행 표
  - **우하단**: 지적도 — `CADASTRAL_MAP` 블록으로 그린 뒤, 패널 테두리를 경계로 **실제 AutoCAD XCLIP(SPATIAL_FILTER)**을 적용해 박스 밖으로 나가는 필지(도로 등)를 진짜로 잘라냅니다
- XCLIP은 Python(ezdxf) 서브프로세스로 적용됩니다. Python/ezdxf가 없으면 클립 없이(블록은 그대로) 파일이 만들어지고, 응답의 `지적도_XCLIP크롭` 필드에 실패 사유가 표시됩니다.
- 표 셀 값은 줄이지 않고 전부 보여줍니다 — 짧은 값은 한 줄, 긴 값(조경계획·주차계획의 조례 원문 등)은 자동 줄바꿈되면서 그 행만 세로로 커집니다(다른 행에서 높이를 나눠 빌려오는 방식이라 표 전체 높이는 그대로).

#### 지자체 조례 자동 조회 (건폐율/용적률/조경/주차)

- 법정동명에서 시·군 이름을 추출해 법제처 Open API로 "{시} 도시계획 조례"/"{시} 건축 조례"/"{시} 주차장 조례"를 검색하고, 전체 조문에서 "건폐율"+21개 용도지역명 같은 키워드로 해당 조문을 직접 찾습니다(조문번호가 목차 순서와 안 맞는 경우가 많아 번호를 추측하지 않음).
- **주차 기준은 조문 본문이 아니라 별표(HWP/PDF 첨부파일)로 위임된 경우가 많아**, 조문에서 "별표N" 인용을 발견하면 별표 목록을 검색해 실제 파일을 다운로드하고 [`kordoc`](https://github.com/chrisryugj/kordoc)(korean-law-mcp가 별표 파싱에 쓰는 것과 동일한 라이브러리)으로 HWP/HWPX/PDF를 마크다운으로 변환한 뒤 용도별 설치기준 행을 뽑습니다.
- **건축용도별 주차 기준** (`buildingUse` 파라미터): `export_land_report_dxf`에 `buildingUse`(예: `사무소`, `제1종근린생활시설`, `창고`)를 주면, 주차장법 시행령 별표1 용도 분류(위락 100㎡·업무/판매/문화 150㎡·근생/숙박 200㎡·공장 350㎡·창고 400㎡·그 밖 300㎡)로 해석해 **법령·조례 주차 기준을 그 용도 행으로** 계산합니다. 예: `사무소` → 법령 "업무시설 150㎡당 1대", 전주시 조례 "업무시설 120㎡당 1대". `buildingUse` 미지정 시 기존처럼 "그 밖의 건축물(300㎡당 1대)"로 폴백합니다. 조례 별표에서 해당 용도 행을 못 찾으면(지자체마다 표기가 달라) "기타" 행으로 폴백하고 그 사실을 표시합니다.
- 조례를 못 찾거나, 조문·별표 파싱에 실패하면 **국가법령 상한**이 항상 별도 행으로 함께 표시됩니다. 표에는 (법령)/(조례) 두 행이 나란히 나오고, 조례 조회가 실패하면 조례 행에 실패 사유가 표시됩니다.
- 응답의 `지자체조례_조회` 필드로 각 항목의 조회 성공 여부를 확인할 수 있습니다.
- `korean-law-mcp`를 이미 쓰고 있어도 별도 프로세스라 키를 공유할 수 없어 `LAW_OC`를 cadastral-mcp에도 따로 설정해야 합니다.

#### 국가법령 상한 실시간 조회 (건폐율/용적률) — `lib/national-law.ts`

- 조례처럼 **매 요청마다 법제처 API를 직접 호출**해 국토계획법 시행령 제84조(건폐율)·제85조(용적률) 원문을 읽어옵니다 — 시행령이 개정돼도 코드 수정 없이 자동으로 최신 값이 반영됩니다.
- 조문 제목("건폐율" 키워드 등)은 다른 조문과 혼동될 수 있어(예: 제46조 제목에도 "건폐율"이 들어있음) 조번호(제84조/제85조)로 정확히 찾습니다. 조번호는 법 개정으로도 거의 바뀌지 않는 안정적 인용입니다.
- 용적률(제85조)은 조례와 달리 "X% 이상 Y% 이하" **범위**로 규정돼 있어(구체적 수치는 지자체 조례가 그 범위 안에서 정함), 조례용 단일값 추출과는 별도로 범위를 그대로 추출합니다.
- 조경(건축법 시행령 제27조)은 조사해보면 조경 면적 %표가 아니라 "조경 조치 면제 대상" 목록입니다 — 국가법령 차원에는 전국 공통 조경 비율표 자체가 없고 전부 지자체 건축조례로 위임되어 있으므로, 녹지지역 면제 여부만 실시간 확인하고 그 외에는 정적 안내문("면적·용도별 기준 상이 — 조례 확인 필요")으로 대체합니다. 이건 낡은 값이 아니라 애초에 국가법령에 값이 없다는 사실을 안내하는 것입니다.
- 주차(주차장법 시행령 별표1)는 별표라 조문처럼 항/호/목으로 구조화되어 있지 않고 HWP에서 추출된 고정폭 텍스트라 안정적 파싱이 어려워, 원문을 직접 대조해 검증한 고정값(`lib/zoning-standards.ts`)을 계속 사용합니다.
- 실시간 조회가 실패하면(네트워크 오류 등) 항상 `zoning-standards.ts`의 검증된 고정값으로 조용히 폴백합니다. 응답의 `국가법령_조회` 필드로 실시간/고정값 여부를 확인할 수 있습니다.

#### `search_national_law` / `get_national_law_text` — 국가법령 자유 조회

- `export_land_report_dxf` 파이프라인이 내부적으로 쓰는 것과 같은 법제처 API 클라이언트(`lib/national-law-api-client.ts`)를 일반 질의용으로 노출한 도구입니다.
- `search_national_law(query)`로 법령명을 검색해 MST(법령일련번호)를 얻고, `get_national_law_text(mst, jo?)`로 조문을 조회합니다. `jo` 미지정 시 조문 목차(번호+제목)만 반환해 응답 크기를 줄이고, `jo`를 지정하면(예: `"84"`, `"제84조"`, `"12의2"`) 해당 조문 전체 텍스트를 반환합니다.
- 판례·심결례·법률용어·조약 등은 다루지 않습니다 — `target=law`(법령) 한 종류만 구현했습니다. 그 범위를 넘어서는 법률 조사가 필요하면 `korean-law-mcp`를 함께 쓰세요(런타임 의존성은 없습니다 — 아래 "korean-law-mcp와의 관계" 참고).

### 수치지형도 병합 (2D 겹침 / 3D 모델) — `lib/topo-dxf-reader.ts`, `lib/terrain-3d.ts`

건축 대지분석용으로, 국토지리정보원 **수치지형도(등고선·건물·도로·하천·표고점)**를 지적도에 겹치거나 3D 대지 모델로 만듭니다.

**먼저 수치지형도 DXF를 받아야 합니다** — [국토정보플랫폼(map.ngii.go.kr)](https://map.ngii.go.kr)에서 대상 지역 **수치지형도 V1.0 DXF**를 무상 다운로드하세요(회원 로그인 필요, 지역별 신청). 받은 파일 경로를 `topoDxfPath`로 넘깁니다. (SHP인 V2.0도 읽을 수 있으나 도구는 V1.0 DXF를 기본으로 합니다.)

- **표준 레이어 자동 인식**: 수치지형도는 전국 표준 8자리 코드를 씁니다. 교통(A) 계열을 세분해 분류합니다 — `A001`=도로경계, `A002`=도로중심선, `A003`=인도(보도), `A004~A014`=도로시설(횡단보도·안전지대·교량·터널 등), `A015~A018`=철도. 그 외 `F001`=등고선, `F002`=표고점, `B001`=건물, `B002`=담장, `E001~E003`=하천. 파일마다 레이어명이 달라도 코드로 분류합니다.
- **좌표계(벨트) 자동 감지**: 한국은 지역별 원점 4개(서부 5185·중부 5186·동부 5187·동해 5188)를 쓰는데, `crs`를 지정하지 않으면 **필지 중심 경도로 벨트를 자동 결정**합니다. 나아가 수치지형도가 주어지면 **그 벨트로 필지가 실제 겹치는지 교차검증하고, 안 맞으면 후보 벨트로 자동 보정**합니다(사용자가 벨트 다른 도엽을 받아도 교정). 응답의 `좌표계_감지` 필드로 어떻게 결정됐는지 확인할 수 있습니다.

**2D 겹침** (`export_land_report_dxf`의 `topoDxfPath`): 통합리포트 우하단 지적도 블록 안에 지형을 함께 그려, 지적도와 같이 XCLIP으로 패널 경계에 잘립니다.

**3D 모델** (`export_site_3d_dxf`): 3D DXF(3DFACE) 대지 모델을 만듭니다.
- **지형**: 등고선 정점 + 표고점을 Delaunay 삼각분할(TIN)하고, `gridCell`(기본 5m) 간격으로 재샘플링해 매끄러운 격자 곡면으로 출력합니다. **STURA3D 등이 쓰는 90m DEM보다 정확합니다**(실측 등고선 기반 — 90m DEM은 실측 대비 RMSE 약 4m).
- **건물**: **GIS건물통합정보(`LT_C_BLDGINFO`)**의 폴리곤 + **실측 높이(`height`)**로 extrude합니다. 이 레이어는 연속지적 건물 형상에 건축물대장 속성(높이·층수·용도)을 통합한 것으로, **별도 키 없이 `VWORLD_KEY`로 함께 조회**됩니다. 대장에 높이가 미기재된 건물만 **층수(`grnd_flr`) × `floorHeight`(기본 3.3m) 추정**으로 폴백합니다. 응답의 `건물높이_출처`로 실측/추정 건수를 확인할 수 있습니다.
- **도로**(`lib/road-surface.ts`): 연속지적도에는 도로도 필지로 등록돼 있고(대개 지목 "도"의 큰 MultiPolygon 하나가 그 일대 도로망 전체) 경계가 실측 지적선이라 오차가 없습니다. 그래서 도로경계선을 근사하지 않고 **"도로 필지"를 골라 union**해 도로면으로 씁니다. 도로 판정 = **지목이 "도"** 이거나 **도로중심선(A002)이 지나가는 필지**(지목이 도가 아닌 현황도로·대지 위 통행로도 잡힘). MultiPolygon의 구멍(=블록)을 보존해 union한 뒤 표시 버퍼로 crop합니다("크게 계산 후 split" — 표시 경계에서 잘려 열리는 문제 방지). 인도·도로시설·철도는 실측 선형 그대로 지형에 드레이프합니다.
  - *(한계)* 지목이 도가 아니고 도로중심선도 없는 막다른 골목은 잡히지 않습니다(데이터에 도로 정보 자체가 없는 경우).
- **하천**: 실폭하천(닫힌 면)은 실제 강 폭 그대로, 하천중심선은 선으로 지형에 드레이프합니다.
- 성능: 지형 TIN에 공간 인덱스(격자 버킷)를 붙여 표고 샘플링을 가속합니다.

## korean-law-mcp와의 관계

cadastral-mcp는 [`korean-law-mcp`](https://github.com/chrisryugj/korean-law-mcp)를 **참조**했을 뿐 **런타임 의존성이 전혀 없습니다** — `package.json`에도 없고, 설치·실행 시 korean-law-mcp가 켜져 있을 필요도 없습니다. 법제처 Open API(대한민국 정부가 공개한 표준 API — `target`/`OC`/`MST` 같은 파라미터 규약은 정부가 정한 것이지 korean-law-mcp의 창작물이 아닙니다)를 cadastral-mcp가 **직접** 호출합니다.

다만 법제처 API는 UA/Referer 헤더가 없으면 거부하고 클라우드 IP에서는 JS 리다이렉트로 위장한 안티봇 페이지를 주는 까다로운 특성이 있는데, 이 우회 로직(`lib/law-antibot.ts`)만은 korean-law-mcp가 먼저 해결해둔 것을 MIT 라이선스 하에 가져다 썼습니다 — `LICENSE` 파일에 출처를 명시해 두었습니다. 이 저장소가 다루는 범위(자치법규 조례 + 국가법령 조문, 판례·법률용어 등은 제외)는 korean-law-mcp보다 훨씬 좁고, cadastral-mcp의 지적도/토지이용계획 파이프라인에 필요한 만큼만 구현되어 있습니다.

## 데이터 출처

이 서버가 쓰는 모든 데이터와 출처입니다. shp 파일을 직접 읽지 않고 전부 공개 API/표준 파일로 받습니다.

### VWorld (`VWORLD_KEY` 하나로 전부 조회 — `api.vworld.kr`)
- **지적 경계·지번·공시지가**: 2D데이터 API `LP_PA_CBND_BUBUN`(연속지적도). 원본은 국토교통부/한국국토정보공사(LX)의 연속지적도(수치지적).
- **도로면(3D)**: 위 연속지적도에서 **도로 필지**(지목 "도" 또는 도로중심선이 지나는 필지)를 골라 union — 경계가 실측 지적선이라 오차 없음.
- **건물 형상·실측 높이·층수·용도**: 2D데이터 API `LT_C_BLDGINFO`(**GIS건물통합정보** — 연속지적 건물형상 + 건축물대장 속성 통합). 3D 건물 매스의 외곽 형상과 실측 높이(`height`)를 여기서 받습니다(별도 키 불필요). 높이 미기재 건물만 층수×층고 추정.
- **토지대장(지목·면적·소유구분)**: 국가중점(NED) API `ned/data/ladfrlList`
- **용도지역지구**: 국가중점(NED) API `ned/data/getLandUseAttr`
- **개별공시지가**: 국가중점(NED) API `ned/data/getIndvdLandPriceAttr`

### 법제처 Open API (`LAW_OC` — `open.law.go.kr`)
- **국가법령 건폐율/용적률**: 국토계획법 시행령 제84·85조 조문을 매 요청 실시간 조회(`lib/national-law.ts`).
- **지자체 조례**: 필지 소재 시·군의 도시계획·건축·주차장 조례에서 건폐율/용적률/조경/주차 기준을 검색·파싱. 주차는 조례 **별표(HWP/HWPX/PDF)**를 다운로드해 [`kordoc`](https://github.com/chrisryugj/kordoc)로 표를 추출(`lib/local-ordinance.ts`).

### 국토지리정보원 수치지형도 (사용자 수동 다운로드 — `map.ngii.go.kr`)
- **지형/건물/도로/하천 벡터**: V1.0 DXF. 등고선(F001)·표고점(F002)으로 지형 TIN, 도로중심선(A002)·도로경계(A001)·인도(A003), 건물(B001), 하천(E) 등 표준 8자리 코드로 분류. `topoDxfPath`로 전달. (로그인·전용 전송 프로그램 필요라 자동 다운로드는 미지원)

### 좌표계
- 지번 중심 경도로 **EPSG 벨트(서부5185·중부5186·동부5187·동해5188) 자동 감지**, 수치지형도가 있으면 겹침으로 교차검증·보정(`lib/crs-resolver.ts`).

## 알려진 한계

- 홀(구멍)이 있는 도넛형 필지는 로직상 지원하지만 실사용 케이스로 검증되지 않았습니다.
- DXF는 참고용이며 법적 효력이 있는 지적측량성과도가 아닙니다.
- VWorld 데이터 갱신 주기(보통 월 단위)를 따르므로 실시간 최신 상태가 아닐 수 있습니다.
- `export_land_report_dxf`의 XCLIP은 `ezdxf.audit()` 구조 검증(오류 0건, `has_extension_dict`/`has_clipping_path` 정상)에 더해, 실제 AutoCAD에서 열어 정상 작동(박스 밖 필지 크롭)까지 확인했습니다.
- 시스템에 Python이 여러 개 설치돼 있고 그중 ezdxf가 없는 것이 먼저 잡히면 `CADASTRAL_PYTHON`으로 경로를 지정해야 합니다.
- 지자체 조례 자동 조회는 "{시} 도시계획 조례"/"{시} 건축 조례"/"{시} 주차장 조례" 이름 패턴과 21개 용도지역 표 형식(예: "생산녹지지역:100분의 20")에 맞춰 파싱합니다. 지자체마다 조례명·표기 방식이 달라 일부 지역은 매칭에 실패할 수 있으며, 그 경우 국가법령 상한 행만 표시됩니다.
- 별표 HWP 파싱(kordoc)은 문서 포맷이 다양해 100% 성공을 보장하지 않습니다. 실패 시 "[별표N] 참조 — 세부 기준표 자동추출 실패"로 조번호·별표번호까지만 안내합니다(수치를 지어내지 않음).
- 국가법령 건폐율·용적률 실시간 조회(`lib/national-law.ts`)는 국토계획법 시행령 제84·85조라는 **조번호**가 안정적이라는 전제로 동작합니다. 법 개정으로 이 조문 자체가 삭제·이동되는 극단적인 경우 실시간 조회는 실패하지만(→ 고정값으로 조용히 폴백), 고정값 자체가 시행령 개정을 자동 반영하진 못합니다.
- 국가법령 주차 기준(주차장법 시행령 별표1)은 실시간 조회를 구현하지 않았습니다 — 별표는 조문과 달리 항/호/목 구조가 없는 HWP 추출 고정폭 텍스트라 표 경계를 안정적으로 구분하기 어렵습니다. 원문을 직접 대조해 검증한 고정값(`PARKING_STANDARD_CELL`)을 계속 사용합니다.
- 수치지형도(3D/2D 병합)는 **사용자가 map.ngii.go.kr에서 직접 다운로드한 파일**이 있어야 동작합니다 — 로그인·전용 전송 프로그램(INNORIX)을 거치는 다운로드는 자동화하지 않습니다.
- 3D 건물 높이는 GIS건물통합정보의 실측 `height`를 우선하고, 높이 미기재 건물만 층수×추정 층고를 사용합니다. 지붕은 평지붕 근사(부채꼴 삼각분할)입니다. 3D는 대지·주변 매스 검토용이며 측량 성과가 아닙니다.
- 3D 지형은 수치지형도 등고선/표고점을 Delaunay 삼각분할한 것이라, 같은 표고 등고선 사이에 드물게 납작한 삼각형이 생길 수 있습니다(표고점이 이를 완화). 격자 재샘플링으로 표면을 정돈합니다.

## 개발

```bash
npm run watch      # TypeScript watch 빌드
npm run typecheck  # 타입 체크만
npm test           # vitest
```

테스트는 네트워크 호출 없이 순수 로직만 검증합니다 — 지자체 조례 파싱(`local-ordinance.test.ts`), 국가법령 상한 고정값(`zoning-standards.test.ts`), 국가법령 실시간 조회 파싱(`national-law.test.ts`), 수치지형도 레이어 분류·클립(`topo-dxf-reader.test.ts`), 지형 TIN·표고 보간·격자 재샘플링(`terrain-3d.test.ts`), 도로 필지 판정·구멍 보존·crop(`road-surface.test.ts`), 좌표계 벨트 감지(`belt.test.ts`). API 연동·DXF/3D 산출(VWorld/법제처/XCLIP/수치지형도)은 실제 데이터로 수동 검증했습니다(ezdxf audit 오류 0건, AutoCAD 확인).

TDQS

A4.2/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a distinct operation (export DXF, export report, get plan) with clear address/PNU variants. No overlap in purpose.

Naming Consistency5/5

All names follow a consistent verb_object_suffix pattern using snake_case. Variants are logically named with '_by_pnu' suffix.

Tool Count5/5

Six tools cover the core cadastral operations (export and query) without being excessive or undersized.

Completeness5/5

The set provides complete read coverage for the domain: parcel boundaries, integrated reports, and land use plans, all accessible by address or PNU.

Maintenance

ActivityMaintained
ResponsivenessNo issues