파일마다 포맷 폭이 달라(≈80 대 100) 한 줄만 고쳐도 포맷터가 무관한 줄을 대량 재포맷했음. 사용자 지시로 전체를 한 번에 맞춤. 코드 동작 변경 없음 — 포맷만. - 프론트엔드 `.ts/.css/.html` → 저장소 prettier (`.prettierrc`, printWidth 100) - `B07_DesignDetail/openwebcad/**` → 자체 biome (tab 들여쓰기·single quote·lineWidth 100). `biome format` 만 사용 — `biome lint --write` 는 포맷 아닌 코드 수정까지 하므로 제외 - 파이썬 → `ruff format` (엔진 코드는 이미 정합, resources·scratch 스크립트 24개만 변경) 두 포맷터가 서로 되돌리지 않도록 `.prettierignore` 신규 — openwebcad 와 빌드·산출물 폴더를 prettier 대상에서 뺌. `.prettierrc` 에 `endOfLine: "auto"` 추가 — 기본값 `lf` 가 `core.autocrlf=true` 로 받은 CRLF 파일을 매번 전부 다시 써서 `--list-different` 가 실제 포맷 차이를 가리고 있었음. 검증: `tsc --noEmit` 통과(루트·openwebcad 둘 다), pytest 349 passed / 17 skipped / 0 failed, CAD vitest 87건 중 81 passed / 6 failed(laptop-sub 기준선과 동일, 회귀 없음). 포맷터 재실행 시 prettier·biome 모두 변경 0건. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
114 lines
4.5 KiB
Python
114 lines
4.5 KiB
Python
"""
|
|
국가 기본도 등고선 대용량 SHP 파일 세트를 고속 공간 인덱싱이 적용된 단일 GeoPackage(.gpkg)로 병합 변환하는 유틸리티.
|
|
|
|
[사전 필수 패키지 설치]
|
|
실행 전 가상환경(venv) 터미널에서 다음 라이브러리를 설치해 주세요:
|
|
pip install geopandas pyogrio
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
import time
|
|
|
|
try:
|
|
import geopandas as gpd
|
|
|
|
# fiona 대신 현재 설치된 고속 pyogrio 엔진을 검증 및 사용합니다.
|
|
import pyogrio
|
|
except ImportError:
|
|
print("[오류] 필수 라이브러리가 누락되었습니다.")
|
|
print("터미널에 다음 명령어를 입력하여 설치해 주세요:")
|
|
print(">>> pip install geopandas pyogrio")
|
|
sys.exit(1)
|
|
|
|
|
|
def convert_shp_to_gpkg():
|
|
current_dir = Path(__file__).resolve().parent
|
|
shp_files = sorted(list(current_dir.glob("TN_CTRLN*.shp")))
|
|
|
|
if not shp_files:
|
|
print(
|
|
f"[경고] {current_dir} 경로에서 'TN_CTRLN'으로 시작하는 .shp 파일을 찾을 수 없습니다."
|
|
)
|
|
return
|
|
|
|
gpkg_output_path = current_dir / "national_contours.gpkg"
|
|
print(f"-> 총 {len(shp_files)}개의 등고선 SHP 파일이 감지되었습니다.")
|
|
print(f"-> 변환 시작 (저장 경로: {gpkg_output_path})")
|
|
|
|
start_time = time.time()
|
|
|
|
# 첫 번째 파일 처리 (새 GeoPackage 파일 생성)
|
|
first_shp = shp_files[0]
|
|
print(f"\n[1/{len(shp_files)}] {first_shp.name} 읽는 중...")
|
|
try:
|
|
# GeoPandas를 이용해 shapefile 로드 (엔진으로 pyogrio 명시하여 고속 로드)
|
|
gdf = gpd.read_file(first_shp, encoding="cp949", engine="pyogrio")
|
|
|
|
# 속성 필드명 표준화 (소문자로 통일하고 cont_val / elev 필드가 있으면 elevation으로 통일)
|
|
gdf.columns = [col.lower() for col in gdf.columns]
|
|
for elev_col in ["cont_val", "elev_val", "elevation"]:
|
|
if elev_col in gdf.columns:
|
|
gdf["elevation"] = gdf[elev_col].astype(float)
|
|
break
|
|
|
|
# 필요한 필드만 최소한으로 남겨 용량 최소화
|
|
keep_cols = ["geometry", "elevation"] if "elevation" in gdf.columns else ["geometry"]
|
|
gdf = gdf[keep_cols]
|
|
|
|
# GeoPackage 파일로 쓰기 (고속 pyogrio 엔진 및 spatial_index 생성 활성화)
|
|
print(" -> GeoPackage 초기 생성 및 쓰기 중...")
|
|
gdf.to_file(
|
|
gpkg_output_path, layer="contours", driver="GPKG", spatial_index=True, engine="pyogrio"
|
|
)
|
|
print(f" -> 완료 (레코드 수: {len(gdf)}개)")
|
|
|
|
except Exception as e:
|
|
print(f" -> [에러] 첫 번째 파일 처리 중 오류 발생: {e}")
|
|
return
|
|
|
|
# 나머지 파일들을 루프를 돌며 기존 GeoPackage 레이어에 추가(Append)
|
|
for idx, shp_path in enumerate(shp_files[1:], start=2):
|
|
print(f"\n[{idx}/{len(shp_files)}] {shp_path.name} 읽는 중...")
|
|
try:
|
|
gdf_append = gpd.read_file(shp_path, encoding="cp949", engine="pyogrio")
|
|
|
|
# 컬럼 표준화
|
|
gdf_append.columns = [col.lower() for col in gdf_append.columns]
|
|
for elev_col in ["cont_val", "elev_val", "elevation"]:
|
|
if elev_col in gdf_append.columns:
|
|
gdf_append["elevation"] = gdf_append[elev_col].astype(float)
|
|
break
|
|
|
|
keep_cols = (
|
|
["geometry", "elevation"] if "elevation" in gdf_append.columns else ["geometry"]
|
|
)
|
|
gdf_append = gdf_append[keep_cols]
|
|
|
|
# 기존 gpkg 파일에 이어쓰기 (append mode, pyogrio 엔진 사용)
|
|
print(" -> GeoPackage에 데이터 이어붙이는 중...")
|
|
gdf_append.to_file(
|
|
gpkg_output_path,
|
|
layer="contours",
|
|
driver="GPKG",
|
|
mode="a",
|
|
spatial_index=True,
|
|
engine="pyogrio",
|
|
)
|
|
print(f" -> 완료 (레코드 수: {len(gdf_append)}개)")
|
|
|
|
except Exception as e:
|
|
print(f" -> [에러] {shp_path.name} 파일 처리 중 오류 발생: {e}. 계속 진행합니다.")
|
|
continue
|
|
|
|
end_time = time.time()
|
|
elapsed = end_time - start_time
|
|
print("\n==================================================")
|
|
print(f"★ 모든 등고선 변환이 완료되었습니다! (소요시간: {elapsed:.2f}초)")
|
|
print(f"★ 파일 위치: {gpkg_output_path}")
|
|
print("==================================================")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
convert_shp_to_gpkg()
|